diff --git a/cue/ast/ast.go b/cue/ast/ast.go index e564bc46e..f2bce9c27 100644 --- a/cue/ast/ast.go +++ b/cue/ast/ast.go @@ -403,7 +403,6 @@ type Ident struct { // This LHS path element may be an identifier. Possible forms: // foo: a normal identifier // "foo": JSON compatible - // : a template shorthand Name string Scope Node // scope in which node was found or nil if referring directly diff --git a/cue/ast/astutil/resolve.go b/cue/ast/astutil/resolve.go index 2db157033..28b112d57 100644 --- a/cue/ast/astutil/resolve.go +++ b/cue/ast/astutil/resolve.go @@ -64,9 +64,9 @@ type ErrFunc func(pos token.Pos, msg string, args ...interface{}) // X in [X=x]: y Field Expr (x) // X in X=[x]: y Field Field // -// for k, v in Field ForClause +// for k, v in ForClause Ident +// let x = y LetClause Ident // -// Template Field Template // Fields inside lambda // Label Field Expr // Value Field Field @@ -125,21 +125,11 @@ func newScope(f *ast.File, outer *scope, node ast.Node, decls []ast.Decl) *scope if a, ok := x.Label.(*ast.Alias); ok { // TODO(legacy): use name := a.Ident.Name once quoted // identifiers are no longer supported. - if name, _, _ := ast.LabelName(a.Ident); name != "" { - s.insert(name, x, a) - } label, _ = a.Expr.(ast.Label) - } - - switch y := label.(type) { - // TODO: support *ast.ParenExpr? - case *ast.ListLit: - // In this case, it really should be scoped like a template. - if len(y.Elts) != 1 { - break - } - if a, ok := y.Elts[0].(*ast.Alias); ok { - s.insert(a.Ident.Name, x, a) + if name, _, _ := ast.LabelName(a.Ident); name != "" { + if _, ok := label.(*ast.ListLit); !ok { + s.insert(name, x, a) + } } } @@ -277,6 +267,8 @@ func (s *scope) Before(n ast.Node) (w visitor) { case *ast.Comprehension: s = scopeClauses(s, x.Clauses) + walk(s, x.Value) + return nil case *ast.Field: var n ast.Node = x.Label diff --git a/cue/ast/astutil/resolve_test.go b/cue/ast/astutil/resolve_test.go new file mode 100644 index 000000000..b2809822f --- /dev/null +++ b/cue/ast/astutil/resolve_test.go @@ -0,0 +1,73 @@ +// Copyright 2021 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 astutil_test + +import ( + "fmt" + "path/filepath" + "testing" + "text/tabwriter" + + "cuelang.org/go/cue/ast" + "cuelang.org/go/internal/astinternal" + "cuelang.org/go/internal/cuetest" + "cuelang.org/go/internal/cuetxtar" +) + +func TestResolve(t *testing.T) { + test := cuetxtar.TxTarTest{ + Root: "./testdata/resolve", + Name: "resolve", + Update: cuetest.UpdateGoldenFiles, + } + + test.Run(t, func(t *cuetxtar.Test) { + a := t.ValidInstances() + + for _, f := range a[0].Files { + if filepath.Ext(f.Filename) != ".cue" { + continue + } + + identMap := map[ast.Node]int{} + ast.Walk(f, func(n ast.Node) bool { + switch n.(type) { + case *ast.File, *ast.StructLit, *ast.Field, *ast.ImportSpec, + *ast.Ident, *ast.ForClause, *ast.LetClause, *ast.Alias: + identMap[n] = len(identMap) + 1 + } + return true + }, nil) + + // Resolve was already called. + + base := filepath.Base(f.Filename) + b := t.Writer(base[:len(base)-len(".cue")]) + w := tabwriter.NewWriter(b, 0, 4, 1, ' ', 0) + ast.Walk(f, func(n ast.Node) bool { + if x, ok := n.(*ast.Ident); ok { + fmt.Fprintf(w, "%d[%s]:\tScope: %d[%T]\tNode: %d[%s]\n", + identMap[x], astinternal.DebugStr(x), + identMap[x.Scope], x.Scope, + identMap[x.Node], astinternal.DebugStr(x.Node)) + } + return true + }, nil) + w.Flush() + + fmt.Fprintln(b) + } + }) +} diff --git a/cue/ast/astutil/testdata/resolve/comprehensions.txtar b/cue/ast/astutil/testdata/resolve/comprehensions.txtar new file mode 100644 index 000000000..b4b89a54a --- /dev/null +++ b/cue/ast/astutil/testdata/resolve/comprehensions.txtar @@ -0,0 +1,36 @@ +-- in.cue -- +src: [{foo: 3}] + +for k, v in src +let y = v.foo +if y > 3 { + x: [k, v, y] +} + +-- out/resolve/in -- +3[src]: Scope: 0[] Node: 0[] +6[foo]: Scope: 0[] Node: 0[] +8[k]: Scope: 0[] Node: 0[] +9[v]: Scope: 0[] Node: 0[] +10[src]: Scope: 1[*ast.File] Node: 0[[{foo: 3}]] +12[y]: Scope: 0[] Node: 0[] +13[v]: Scope: 7[*ast.ForClause] Node: 9[v] +14[foo]: Scope: 0[] Node: 0[] +15[y]: Scope: 11[*ast.LetClause] Node: 12[y] +18[x]: Scope: 0[] Node: 0[] +19[k]: Scope: 7[*ast.ForClause] Node: 8[k] +20[v]: Scope: 7[*ast.ForClause] Node: 9[v] +21[y]: Scope: 11[*ast.LetClause] Node: 12[y] + +-- issue946.cue -- +x: {for a in a {}} +y: {for aa in a {}} + +-- out/resolve/issue946 -- +3[x]: Scope: 0[] Node: 0[] +6[a]: Scope: 0[] Node: 0[] +7[a]: Scope: 0[] Node: 0[] +10[y]: Scope: 0[] Node: 0[] +13[aa]: Scope: 0[] Node: 0[] +14[a]: Scope: 0[] Node: 0[] + diff --git a/cue/ast/astutil/testdata/resolve/fieldalias.txtar b/cue/ast/astutil/testdata/resolve/fieldalias.txtar new file mode 100644 index 000000000..cbd7c1c06 --- /dev/null +++ b/cue/ast/astutil/testdata/resolve/fieldalias.txtar @@ -0,0 +1,31 @@ +-- in.cue -- +X=a: int +b: a +c: X + +-- out/resolve/in -- +4[X]: Scope: 0[] Node: 0[] +5[a]: Scope: 0[] Node: 0[] +6[int]: Scope: 0[] Node: 0[] +8[b]: Scope: 0[] Node: 0[] +9[a]: Scope: 1[*ast.File] Node: 6[int] +11[c]: Scope: 0[] Node: 0[] +12[X]: Scope: 1[*ast.File] Node: 2[X=a: int] + +-- dynamic.cue -- +X=("foo"): int +Y="\(X)": string +a: X +b: Y + +-- out/resolve/dynamic -- +4[X]: Scope: 0[] Node: 0[] +5[int]: Scope: 0[] Node: 0[] +8[Y]: Scope: 0[] Node: 0[] +9[X]: Scope: 1[*ast.File] Node: 2[X=("foo"): int] +10[string]: Scope: 0[] Node: 0[] +12[a]: Scope: 0[] Node: 0[] +13[X]: Scope: 1[*ast.File] Node: 2[X=("foo"): int] +15[b]: Scope: 0[] Node: 0[] +16[Y]: Scope: 1[*ast.File] Node: 6[Y="\(X)": string] + diff --git a/cue/ast/astutil/testdata/resolve/labels.txtar b/cue/ast/astutil/testdata/resolve/labels.txtar new file mode 100644 index 000000000..b0fe12c7c --- /dev/null +++ b/cue/ast/astutil/testdata/resolve/labels.txtar @@ -0,0 +1,24 @@ +-- in.cue -- +[X=string]: name: X +a: X + +Z=[string]: { x: string, y: Z.x } +c: Z + +-- out/resolve/in -- +4[X]: Scope: 0[] Node: 0[] +5[string]: Scope: 0[] Node: 0[] +8[name]: Scope: 0[] Node: 0[] +9[X]: Scope: 2[*ast.Field] Node: 5[string] +11[a]: Scope: 0[] Node: 0[] +12[X]: Scope: 0[] Node: 0[] +15[Z]: Scope: 0[] Node: 0[] +16[string]: Scope: 0[] Node: 0[] +19[x]: Scope: 0[] Node: 0[] +20[string]: Scope: 0[] Node: 0[] +22[y]: Scope: 0[] Node: 0[] +23[Z]: Scope: 13[*ast.Field] Node: 13[Z=[string]: {x: string, y: Z.x}] +24[x]: Scope: 0[] Node: 0[] +26[c]: Scope: 0[] Node: 0[] +27[Z]: Scope: 0[] Node: 0[] + diff --git a/cue/ast/astutil/testdata/resolve/let.txtar b/cue/ast/astutil/testdata/resolve/let.txtar new file mode 100644 index 000000000..b3a5f6645 --- /dev/null +++ b/cue/ast/astutil/testdata/resolve/let.txtar @@ -0,0 +1,13 @@ +-- let.cue -- +b: X +let X = int +a: X + +-- out/resolve/let -- +3[b]: Scope: 0[] Node: 0[] +4[X]: Scope: 1[*ast.File] Node: 5[let X=int] +6[X]: Scope: 0[] Node: 0[] +7[int]: Scope: 0[] Node: 0[] +9[a]: Scope: 0[] Node: 0[] +10[X]: Scope: 1[*ast.File] Node: 5[let X=int] + diff --git a/cue/ast/astutil/testdata/resolve/value.txtar b/cue/ast/astutil/testdata/resolve/value.txtar new file mode 100644 index 000000000..3ba1621c0 --- /dev/null +++ b/cue/ast/astutil/testdata/resolve/value.txtar @@ -0,0 +1,12 @@ +-- in.cue -- +b: X={ + c: X.a +} + +-- out/resolve/in -- +3[b]: Scope: 0[] Node: 0[] +5[X]: Scope: 0[] Node: 0[] +8[c]: Scope: 0[] Node: 0[] +9[X]: Scope: 2[*ast.Field] Node: 4[X={c: X.a}] +10[a]: Scope: 0[] Node: 0[] + diff --git a/cue/testdata/compile/erralias.txtar b/cue/testdata/compile/erralias.txtar index c30c1a9af..07c3c0ada 100644 --- a/cue/testdata/compile/erralias.txtar +++ b/cue/testdata/compile/erralias.txtar @@ -15,7 +15,7 @@ Z1=[Z2=string]: Z1+Z2 -- out/compile -- unreferenced alias or let clause X: ./in.cue:1:1 -a: illegal reference Y: +a: reference "Y" not found: ./in.cue:4:4 definitions not supported for interpolations: ./in.cue:6:1 @@ -24,7 +24,7 @@ for[].a: reference "E" not found: --- in.cue { ["foo"]: 3 - a: _|_(illegal reference Y) + a: _|_(reference "Y" not found) "\(〈0;b〉)": 3 b: "foo" c: {} diff --git a/cue/testdata/compile/files.txtar b/cue/testdata/compile/files.txtar new file mode 100644 index 000000000..44414a322 --- /dev/null +++ b/cue/testdata/compile/files.txtar @@ -0,0 +1,38 @@ +// Issue #946 +-- in.cue -- +package repro + +a: [] + +-- out.cue -- +package repro + +x: {for a in a {}} +y: {{{for a in a {}}}} +-- out/compile -- +--- in.cue +{ + a: [] +} +--- out.cue +{ + x: { + for _, a in 〈1;a〉 {} + } + y: { + { + { + for _, a in 〈3;a〉 {} + } + } + } +} +-- out/eval -- +(struct){ + a: (#list){ + } + x: (struct){ + } + y: (struct){ + } +}