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)
- })
- }
-}