this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue/ast/astutil: detect aliasv2 postfix label-name redeclarations

In the postfix dual form `"foo"~(K, V):`, the K identifier captures
the label name, and the resolver inserts it into the surrounding
struct scope with the label expression (e.g. a *ast.BasicLit) as the
entry's node. The uniqueness check inspected only that node type,
which falls outside the let-like set (*ast.LetClause, *ast.Alias,
*ast.Field, …), so two sibling fields could declare the same K alias
and the second silently overwrote the first instead of producing an
error like the equivalent old-style `N=("foo"):` form does.

Thread the entry's link through isLet and mustBeUnique, and treat any
entry linked to an *ast.PostfixAlias as let-like and required to be
unique. The other postfix-alias insertion sites (single-form ~X and
dual-form V) already pass *ast.Field as the node, so they were already
handled; pattern constraints `[…]~(K, V):` insert into a field-local
scope where uniqueness is automatic.

Fixes #4342.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: Id8604dfdb3e1c82b526cdf1ce88b3650bbf9ab60
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1236313
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>

+13 -10
+10 -4
cue/ast/astutil/resolve.go
··· 230 230 return s 231 231 } 232 232 233 - func (s *scope) isLet(n ast.Node) bool { 233 + func (s *scope) isLet(n, link ast.Node) bool { 234 234 if _, ok := s.node.(*ast.Field); ok { 235 + return true 236 + } 237 + if _, ok := link.(*ast.PostfixAlias); ok { 235 238 return true 236 239 } 237 240 switch n.(type) { ··· 241 244 return false 242 245 } 243 246 244 - func (s *scope) mustBeUnique(n ast.Node) bool { 247 + func (s *scope) mustBeUnique(n, link ast.Node) bool { 245 248 if _, ok := s.node.(*ast.Field); ok { 246 249 return true 247 250 } 251 + if _, ok := link.(*ast.PostfixAlias); ok { 252 + return true 253 + } 248 254 switch n.(type) { 249 255 // TODO: add *ast.ImportSpec when some implementations are moved over to 250 256 // Sanitize. ··· 263 269 } 264 270 // TODO: record both positions. 265 271 if outer, _, existing := s.lookup(name); existing.node != nil { 266 - if s.isLet(n) != outer.isLet(existing.node) { 272 + if s.isLet(n, link) != outer.isLet(existing.node, existing.link) { 267 273 s.errFn(n.Pos(), "cannot have both alias and field with name %q in same scope", name) 268 274 return 269 - } else if s.mustBeUnique(n) || outer.mustBeUnique(existing.node) { 275 + } else if s.mustBeUnique(n, link) || outer.mustBeUnique(existing.node, existing.link) { 270 276 if outer == s { 271 277 if _, ok := existing.node.(*ast.ImportSpec); ok { 272 278 return
+3 -6
cue/ast/astutil/testdata/resolve/redeclared.txtar
··· 21 21 @experiment(aliasv2) 22 22 23 23 // Postfix dual-form, K (label name capture) shared across siblings. 24 - // 25 - // TODO(https://cuelang.org/issue/4342): this should also report `alias 26 - // "N" redeclared in same scope`, matching the equivalent old-style 27 - // `N=("foo"):` form, but the uniqueness check in astutil/resolve.go 28 - // does not currently see the K binding as let-like, so the second 29 - // declaration silently overrides the first. 24 + // Regression test for https://cuelang.org/issue/4342. 30 25 "foo"~(N, _): 1 31 26 "bar"~(N, _): 2 32 27 -- let_clauses.cue -- ··· 58 53 ./old_label.cue:3:1 59 54 alias "V" redeclared in same scope: 60 55 ./postfix_field.cue:5:1 56 + alias "N" redeclared in same scope: 57 + ./postfix_label.cue:6:8 61 58 alias "X" redeclared in same scope: 62 59 ./postfix_single.cue:5:1 63 60 -- out/resolve/postfix_label --