cue/ast/astutil: catch cycle for label expressions
If such labels need to would be applied to
the field referenced to.
In some cases this should actually be legal,
according to the spec, but we will make this
a compilation error for now until we fix the
evaluator.
Closes #251
Change-Id: I167a87cb1866fc2991abe29a05c53356cf23805b
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4720
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/ast/astutil/resolve.go b/cue/ast/astutil/resolve.go
index 6f923fb..af8cbd3 100644
--- a/cue/ast/astutil/resolve.go
+++ b/cue/ast/astutil/resolve.go
@@ -45,10 +45,11 @@
// scope.
//
type scope struct {
- file *ast.File
- outer *scope
- node ast.Node
- index map[string]ast.Node
+ file *ast.File
+ outer *scope
+ node ast.Node
+ index map[string]ast.Node
+ inField bool
errFn func(p token.Pos, msg string, args ...interface{})
}
@@ -208,7 +209,7 @@
if len(label.Elts) != 1 {
break
}
- s := newScope(s.file, s, x, nil)
+ s = newScope(s.file, s, x, nil)
if alias != nil {
if name, _, _ := ast.LabelName(alias.Ident); name != "" {
s.insert(name, x)
@@ -231,22 +232,33 @@
s.insert(name, a.Expr)
}
}
+
+ ast.Walk(expr, nil, func(n ast.Node) {
+ if x, ok := n.(*ast.Ident); ok {
+ for s := s; s != nil && !s.inField; s = s.outer {
+ if _, ok := s.index[x.Name]; ok {
+ s.errFn(n.Pos(),
+ "reference %q in label expression refers to field against which it would be matched", x.Name)
+ }
+ }
+ }
+ })
walk(s, expr)
- walk(s, x.Value)
- return nil
case *ast.TemplateLabel:
- s := newScope(s.file, s, x, nil)
+ s = newScope(s.file, s, x, nil)
name, err := ast.ParseIdent(label.Ident)
if err == nil {
s.insert(name, x.Label) // Field used for entire lambda.
}
- walk(s, x.Value)
- return nil
}
+
if x.Value != nil {
+ s.inField = true
walk(s, x.Value)
+ s.inField = false
}
+
return nil
case *ast.Alias:
diff --git a/cue/ast_test.go b/cue/ast_test.go
index 87f93dd..240105f 100644
--- a/cue/ast_test.go
+++ b/cue/ast_test.go
@@ -230,14 +230,28 @@
// optional fields with key filters
in: `
JobID: =~"foo"
- [JobID]: { name: string }
+ a: [JobID]: { name: string }
[<"s"]: { other: string }
`,
out: `<0>{` +
- `[<0>.JobID]: <1>(_: string)-><2>{name: string}, ` +
- `[<"s"]: <3>(_: string)-><4>{other: string}, ` +
- `JobID: =~"foo"}`,
+ `[<"s"]: <1>(_: string)-><2>{other: string}, ` +
+ `JobID: =~"foo", a: <3>{` +
+ `[<0>.JobID]: <4>(_: string)-><5>{name: string}, ` +
+ `}}`,
+ }, {
+ // Issue #251
+ // TODO: the is one of the cases where it is relatively easy to catch
+ // a structural cycle. We should be able, however, to break the cycle
+ // with a post-validation constraint. Clean this up with the evaluator
+ // update.
+ in: `
+ {
+ [x]: 3
+ }
+ x: "x"
+ `,
+ out: "reference \"x\" in label expression refers to field against which it would be matched:\n test:3:5\n<0>{}",
}, {
// illegal alias usage
in: `