cue: disallow references to string labels (per spec)

Run cue fmt with an older version to update code.

Change-Id: Iecdecc38f3e7954978f37f547c2ae83069a3fbba
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3964
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/fix.go b/cmd/cue/cmd/fix.go
index e4391fc..87bbf4e 100644
--- a/cmd/cue/cmd/fix.go
+++ b/cmd/cue/cmd/fix.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"strconv"
 	"strings"
 
 	"cuelang.org/go/cue/ast"
@@ -107,7 +106,7 @@
 		return true
 	}, nil).(*ast.File)
 
-	// Rewrite strings fields that are referenced.
+	// Rewrite quoted identifier fields that are referenced.
 	f = astutil.Apply(f, func(c astutil.Cursor) bool {
 		n := c.Node()
 		switch x := n.(type) {
@@ -116,33 +115,30 @@
 			if !ok {
 				break
 			}
-			switch b := x.Label.(type) {
-			case *ast.BasicLit:
-				if b.Kind != token.STRING {
-					return true
-				}
-				str, err := strconv.Unquote(b.Value)
-				if err != nil || str != m {
-					return true
-				}
 
-			case *ast.Ident:
+			if b, ok := x.Label.(*ast.Ident); ok {
 				str, err := ast.ParseIdent(b)
-				if err != nil || str != m || str == b.Name {
+				var expr ast.Expr = b
+
+				switch {
+				case token.Lookup(str) != token.IDENT:
+					// quote keywords
+					expr = ast.NewString(b.Name)
+
+				case err != nil || str != m || str == b.Name:
 					return true
-				}
-				if ast.IsValidIdent(str) {
+
+				case ast.IsValidIdent(str):
 					x.Label = astutil.CopyMeta(ast.NewIdent(str), x.Label).(ast.Label)
 					return true
 				}
-			}
 
-			ident := newIdent()
-			replacement[x.Value] = ident
-			expr := x.Label.(ast.Expr)
-			expr = &ast.Alias{Ident: ast.NewIdent(ident), Expr: expr}
-			ast.SetRelPos(x.Label, token.NoRelPos)
-			x.Label = astutil.CopyMeta(expr, x.Label).(ast.Label)
+				ident := newIdent()
+				replacement[x.Value] = ident
+				expr = &ast.Alias{Ident: ast.NewIdent(ident), Expr: expr}
+				ast.SetRelPos(x.Label, token.NoRelPos)
+				x.Label = astutil.CopyMeta(expr, x.Label).(ast.Label)
+			}
 		}
 		return true
 	}, nil).(*ast.File)
@@ -164,7 +160,7 @@
 			// as here, or it is a complicated identifier and the original
 			// destination must have been quoted, in which case it is handled
 			// above.
-			if ast.IsValidIdent(str) {
+			if ast.IsValidIdent(str) && token.Lookup(str) == token.IDENT {
 				c.Replace(astutil.CopyMeta(ast.NewIdent(str), n))
 			}
 		}
diff --git a/cmd/cue/cmd/fix_test.go b/cmd/cue/cmd/fix_test.go
index 49019e3..596e594 100644
--- a/cmd/cue/cmd/fix_test.go
+++ b/cmd/cue/cmd/fix_test.go
@@ -27,55 +27,31 @@
 		in   string
 		out  string
 	}{{
-		name: "referenced string fields",
+		name: "referenced quoted fields",
 		in: `package foo
 
-"foo": 3
-"foo-bar": 2
-"baz": ` + "`foo-bar`" + `
-
 a: {
-	// qux
-	"qux": 3 // qux line
-	// qux-quux
-	"qux-quux": qux
-	"qaz": ` + "`qux-quux`" + `
-	qax:  qux
-
 	fiz: 4
 	faz: ` + "`fiz`" + `
-	// fuz
-	fuz: ` + "`qux-quux`" + ` // fuz
 
 	// biz
 	` + "`biz`" + `: 5 // biz
 	buz: ` + "`biz`" + `
-	baz: ` + "`qux`" + `
+	in: 3
+	ref: ` + "`in`" + ` & x
 }
 `,
 		out: `package foo
 
-"foo":        3
-X1="foo-bar": 2
-"baz":        X1
-
 a: {
-	// qux
-	X2="qux": 3 // qux line
-	// qux-quux
-	X3="qux-quux": X2
-	"qaz":         X3
-	qax:           X2
-
 	fiz: 4
 	faz: fiz
-	// fuz
-	fuz: X3 // fuz
 
 	// biz
-	biz: 5 // biz
-	buz: biz
-	baz: X2
+	biz:     5 // biz
+	buz:     biz
+	X1="in": 3
+	ref:     X1 & x
 }
 `,
 	}, {
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index 1460bc8..169d1ac 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -638,7 +638,7 @@
 		name := ""
 		switch x := n.(type) {
 		case *ast.Field:
-			name, _ = internal.LabelName(x.Label)
+			name, _, _ = ast.LabelName(x.Label)
 		case *ast.Alias:
 			name = x.Ident.Name
 		}
@@ -658,8 +658,8 @@
 	}, func(c astutil.Cursor) bool {
 		switch f := c.Node().(type) {
 		case *ast.Field:
-			name, ident := internal.LabelName(f.Label)
-			if name == "" || !ident {
+			name, _, _ := ast.LabelName(f.Label)
+			if name == "" {
 				return false
 			}
 
diff --git a/cue/ast.go b/cue/ast.go
index ec77515..178d3be 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -424,7 +424,7 @@
 			if internal.DropOptional && opt {
 				break
 			}
-			v.sel, _ = internal.LabelName(x)
+			v.sel, _, _ = ast.LabelName(x)
 			if v.sel == "_" {
 				if _, ok := x.(*ast.BasicLit); ok {
 					v.sel = "*"
diff --git a/cue/ast/astutil/resolve.go b/cue/ast/astutil/resolve.go
index ed9db2b..4282a3e 100644
--- a/cue/ast/astutil/resolve.go
+++ b/cue/ast/astutil/resolve.go
@@ -22,7 +22,6 @@
 
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/token"
-	"cuelang.org/go/internal"
 )
 
 // An ErrFunc processes errors.
@@ -69,7 +68,7 @@
 			label := x.Label
 
 			if a, ok := x.Label.(*ast.Alias); ok {
-				if name, _ := internal.LabelName(a.Ident); name != "" {
+				if name, _, _ := ast.LabelName(a.Ident); name != "" {
 					s.insert(name, x)
 				}
 				label, _ = a.Expr.(ast.Label)
@@ -88,8 +87,7 @@
 			}
 
 			// default:
-			// TODO: switch to ast's implementation
-			name, isIdent := internal.LabelName(label)
+			name, isIdent, _ := ast.LabelName(label)
 			if isIdent {
 				s.insert(name, x.Value)
 			}
@@ -210,7 +208,7 @@
 			}
 			s := newScope(s.file, s, x, nil)
 			if alias != nil {
-				if name, _ := internal.LabelName(alias.Ident); name != "" {
+				if name, _, _ := ast.LabelName(alias.Ident); name != "" {
 					s.insert(name, x)
 				}
 			}
diff --git a/cue/ast/ident.go b/cue/ast/ident.go
index 35d8838..8760c7b 100644
--- a/cue/ast/ident.go
+++ b/cue/ast/ident.go
@@ -128,13 +128,13 @@
 //     X=foo   "foo"  true   nil
 //
 func LabelName(l Label) (name string, isIdent bool, err error) {
-	// XXX: alias unwrap once only.
-	switch n := l.(type) {
-	case *Alias:
-		if label, ok := n.Expr.(Label); ok {
-			return LabelName(label)
+	a, ok := l.(*Alias)
+	if ok {
+		if l, ok = a.Expr.(Label); !ok {
+			goto expressionLabel
 		}
-
+	}
+	switch n := l.(type) {
 	case *ListLit:
 		// An expression, but not one can evaluated.
 		return "", false, errors.Newf(l.Pos(),
@@ -163,6 +163,8 @@
 			// TODO: allow numbers to be fields?
 		}
 	}
+
+expressionLabel:
 	// This includes interpolation and template labels.
 	return "", false, errors.Wrapf(ErrIsExpression, l.Pos(),
 		"label is an expression")
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 1c3ee9f..2a1ece3 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -598,7 +598,7 @@
 	}, {
 		desc: "JSON",
 		in: `
-			"a": 3
+			a="a": 3
 			b: a
 			o: { "a\nb": 2 } // TODO: use $ for root?
 			c: o["a\nb"]
diff --git a/internal/internal.go b/internal/internal.go
index 051cdac..f4e0ab7 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -20,8 +20,6 @@
 // TODO: refactor packages as to make this package unnecessary.
 
 import (
-	"strconv"
-
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/token"
 	"github.com/cockroachdb/apd/v2"
@@ -102,35 +100,3 @@
 	}
 	return nil, "", f.Pos()
 }
-
-// LabelName reports the name of a label, if known, and whether it is valid.
-func LabelName(l ast.Label) (name string, ok bool) {
-	switch n := l.(type) {
-	case *ast.Ident:
-		str, err := ast.ParseIdent(n)
-		if err != nil {
-			return "", false
-		}
-		return str, true
-
-	case *ast.BasicLit:
-		switch n.Kind {
-		case token.STRING:
-			// Use strconv to only allow double-quoted, single-line strings.
-			if str, err := strconv.Unquote(n.Value); err == nil {
-				return str, true
-			}
-
-		case token.NULL, token.TRUE, token.FALSE:
-			return n.Value, true
-
-			// TODO: allow numbers to be fields?
-		}
-
-	case *ast.TemplateLabel:
-		return n.Ident.Name, false
-
-	}
-	// This includes interpolation.
-	return "", false
-}
diff --git a/internal/internal_test.go b/internal/internal_test.go
deleted file mode 100644
index a3e8e73..0000000
--- a/internal/internal_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2019 CUE Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package internal_test
-
-import (
-	"testing"
-
-	"cuelang.org/go/cue/ast"
-	"cuelang.org/go/cue/format"
-	"cuelang.org/go/internal"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestLabelName(t *testing.T) {
-	testCases := []struct {
-		in  ast.Label
-		out string
-		ok  bool
-	}{{
-		in:  ast.NewString("foo-bar"),
-		out: "foo-bar",
-		ok:  true,
-	}, {
-		in:  ast.NewString("foo bar"),
-		out: "foo bar",
-		ok:  true,
-	}, {
-		in:  &ast.Ident{Name: "`foo-bar`"},
-		out: "foo-bar",
-		ok:  true,
-	}, {
-		in:  &ast.Ident{Name: "`foo-bar\x00`"},
-		out: "",
-		ok:  false,
-	}}
-	for _, tc := range testCases {
-		b, _ := format.Node(tc.in)
-		t.Run(string(b), func(t *testing.T) {
-			str, ok := internal.LabelName(tc.in)
-			assert.Equal(t, tc.out, str)
-			assert.Equal(t, tc.ok, ok)
-		})
-	}
-}