cmd/cue/cmd: import: use astutil

this simplifies the code, but, more importantly, now
passes correct code to CompileExpr. This, in turn,
will allow enabling of stricter error checking.

Change-Id: I5e9d3ca0c71805bf4760e02fd83d3903ac27ee7a
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3761
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index 8f6bab2..814c37d 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -27,6 +27,7 @@
 
 	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/ast/astutil"
 	"cuelang.org/go/cue/encoding"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/format"
@@ -402,16 +403,23 @@
 
 	f := &ast.File{}
 
-	h := hoister{
-		fields:   map[string]bool{},
-		altNames: map[string]*ast.Ident{},
+	if flagRecursive.Bool(cmd) {
+		h := hoister{fields: map[string]bool{}}
+
+		imports := &ast.ImportDecl{}
+
+		h.hoist(&ast.File{Decls: []ast.Decl{
+			imports,
+			&ast.EmbedDecl{Expr: &ast.ListLit{Elts: objs}},
+		}})
+
+		if len(imports.Specs) > 0 {
+			f.Decls = append(f.Decls, imports)
+		}
 	}
 
 	index := newIndex()
 	for _, expr := range objs {
-		if flagRecursive.Bool(cmd) {
-			h.hoist(expr)
-		}
 
 		// Compute a path different from root.
 		var pathElems []ast.Label
@@ -477,25 +485,6 @@
 		}
 	}
 
-	if len(h.altNames) > 0 {
-		imports := &ast.ImportDecl{}
-
-		for _, enc := range encoding.All() {
-			if ident, ok := h.altNames[enc.Name()]; ok {
-				short := enc.Name()
-				name := h.uniqueName(short, "", "")
-				ident.Name = name
-				if name == short {
-					ident = nil
-				}
-
-				imports.Specs = append(imports.Specs,
-					ast.NewImport(ident, "encoding/"+short))
-			}
-		}
-		f.Decls = append([]ast.Decl{imports}, f.Decls...)
-	}
-
 	if pkg != "" {
 		p := &ast.Package{Name: ast.NewIdent(pkg)}
 		f.Decls = append([]ast.Decl{p}, f.Decls...)
@@ -638,12 +627,11 @@
 }
 
 type hoister struct {
-	fields   map[string]bool
-	altNames map[string]*ast.Ident
+	fields map[string]bool
 }
 
-func (h *hoister) hoist(expr ast.Expr) {
-	ast.Walk(expr, nil, func(n ast.Node) {
+func (h *hoister) hoist(f *ast.File) {
+	ast.Walk(f, nil, func(n ast.Node) {
 		name := ""
 		switch x := n.(type) {
 		case *ast.Field:
@@ -656,65 +644,55 @@
 		}
 	})
 
-	ast.Walk(expr, func(n ast.Node) bool {
+	_ = astutil.Apply(f, func(c astutil.Cursor) bool {
+		n := c.Node()
 		switch n.(type) {
 		case *ast.Comprehension:
 			return false
 		}
 		return true
 
-	}, func(n ast.Node) {
-		obj, ok := n.(*ast.StructLit)
-		if !ok {
-			return
-		}
-		for i := 0; i < len(obj.Elts); i++ {
-			f, ok := obj.Elts[i].(*ast.Field)
-			if !ok {
-				continue
-			}
-
+	}, func(c astutil.Cursor) bool {
+		switch f := c.Node().(type) {
+		case *ast.Field:
 			name, ident := internal.LabelName(f.Label)
 			if name == "" || !ident {
-				continue
+				return false
 			}
 
 			lit, ok := f.Value.(*ast.BasicLit)
 			if !ok || lit.Kind != token.STRING {
-				continue
+				return false
 			}
 
 			str, err := literal.Unquote(lit.Value)
 			if err != nil {
-				continue
+				return false
 			}
 
 			expr, enc := tryParse(str)
 			if expr == nil {
-				continue
+				return false
 			}
 
-			if h.altNames[enc.typ] == nil {
-				h.altNames[enc.typ] = &ast.Ident{Name: "_cue"} // set name later
+			pkg := c.Import("encoding/" + enc.typ)
+			if pkg == nil {
+				return false
 			}
 
 			// found a replacable string
 			dataField := h.uniqueName(name, "_", "cue_")
 
 			f.Value = ast.NewCall(
-				ast.NewSel(h.altNames[enc.typ], "Marshal"),
+				ast.NewSel(pkg, "Marshal"),
 				ast.NewIdent(dataField))
 
-			obj.Elts = append(obj.Elts, nil)
-			copy(obj.Elts[i+1:], obj.Elts[i:])
-
-			obj.Elts[i+1] = &ast.Alias{
+			c.InsertAfter(astutil.ApplyRecursively(&ast.Alias{
 				Ident: ast.NewIdent(dataField),
 				Expr:  expr,
-			}
-
-			h.hoist(expr)
+			}))
 		}
+		return true
 	})
 }
 
diff --git a/cmd/cue/cmd/testdata/script/import_hoiststr.txt b/cmd/cue/cmd/testdata/script/import_hoiststr.txt
index 266e99e..e821f07 100644
--- a/cmd/cue/cmd/testdata/script/import_hoiststr.txt
+++ b/cmd/cue/cmd/testdata/script/import_hoiststr.txt
@@ -1,7 +1,7 @@
 cue import -o - -f --list -l '"\(strings.ToLower(kind))" "\(name)"' --recursive ./import
 cmp stdout expect-stdout
 -- expect-stdout --
-import xjson "encoding/json"
+import json656e63 "encoding/json"
 
 service: {
 	booster: [{
@@ -14,7 +14,7 @@
 		supplement
 		foo
 		"""
-		json: xjson.Marshal(_cue_json)
+		json: json656e63.Marshal(_cue_json)
 		_cue_json = [1, 2]
 	}]
 }
diff --git a/doc/tutorial/kubernetes/quick/services/mon/alertmanager/configmap.cue b/doc/tutorial/kubernetes/quick/services/mon/alertmanager/configmap.cue
index a8b8d62..e641eed 100644
--- a/doc/tutorial/kubernetes/quick/services/mon/alertmanager/configmap.cue
+++ b/doc/tutorial/kubernetes/quick/services/mon/alertmanager/configmap.cue
@@ -1,12 +1,12 @@
 package kube
 
-import "encoding/yaml"
+import yaml656e63 "encoding/yaml"
 
 configMap alertmanager: {
 	apiVersion: "v1"
 	kind:       "ConfigMap"
 	data: {
-		"alerts.yaml": yaml.Marshal(_cue_alerts_yaml)
+		"alerts.yaml": yaml656e63.Marshal(_cue_alerts_yaml)
 		_cue_alerts_yaml = {
 			receivers: [{
 				name: "pager"
diff --git a/doc/tutorial/kubernetes/quick/services/mon/prometheus/configmap.cue b/doc/tutorial/kubernetes/quick/services/mon/prometheus/configmap.cue
index 4ef2755..c73acf7 100644
--- a/doc/tutorial/kubernetes/quick/services/mon/prometheus/configmap.cue
+++ b/doc/tutorial/kubernetes/quick/services/mon/prometheus/configmap.cue
@@ -1,12 +1,12 @@
 package kube
 
-import "encoding/yaml"
+import yaml656e63 "encoding/yaml"
 
 configMap prometheus: {
 	apiVersion: "v1"
 	kind:       "ConfigMap"
 	data: {
-		"alert.rules": yaml.Marshal(_cue_alert_rules)
+		"alert.rules": yaml656e63.Marshal(_cue_alert_rules)
 		_cue_alert_rules = {
 			groups: [{
 				name: "rules.yaml"
@@ -48,7 +48,7 @@
 			}]
 		}
 
-		"prometheus.yml": yaml.Marshal(_cue_prometheus_yml)
+		"prometheus.yml": yaml656e63.Marshal(_cue_prometheus_yml)
 		_cue_prometheus_yml = {
 			global scrape_interval: "15s"
 			rule_files: [