github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/cue/ast/astutil/resolve.go (about) 1 // Copyright 2018 The CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // This file implements scopes and the objects they contain. 16 17 package astutil 18 19 import ( 20 "bytes" 21 "fmt" 22 23 "github.com/joomcode/cue/cue/ast" 24 "github.com/joomcode/cue/cue/token" 25 ) 26 27 // An ErrFunc processes errors. 28 type ErrFunc func(pos token.Pos, msg string, args ...interface{}) 29 30 // TODO: future development 31 // 32 // Resolution currently assigns values along the table below. This is based on 33 // Go's resolver and is not quite convenient for CUE's purposes. For one, CUE 34 // allows manually setting resolution and than call astutil.Sanitize to 35 // normalize the ast.File. Manually assigning resolutions according to the 36 // below table is rather tedious though. 37 // 38 // Instead of using the Scope and Node fields in identifiers, we suggest the 39 // following assignments: 40 // 41 // Reference Node // an Decl or Clause 42 // Ident *Ident // The identifier in References (optional) 43 // 44 // References always refers to the direct element in the scope in which the 45 // identifier occurs, not the final value, so: *Field, *LetClause, *ForClause, 46 // etc. In case Ident is defined, it must be the same pointer as the 47 // referencing identifier. In case it is not defined, the Name of the 48 // referencing identifier can be used to locate the proper identifier in the 49 // referenced node. 50 // 51 // The Scope field in the original design then loses its function. 52 // 53 // Type of reference Scope Node 54 // Let Clause File/Struct LetClause 55 // Alias declaration File/Struct Alias (deprecated) 56 // Illegal Reference File/Struct 57 // Value 58 // X in a: X=y Field Alias 59 // Fields 60 // X in X: y File/Struct Expr (y) 61 // X in X=x: y File/Struct Field 62 // X in X=(x): y File/Struct Field 63 // X in X="\(x)": y File/Struct Field 64 // X in [X=x]: y Field Expr (x) 65 // X in X=[x]: y Field Field 66 // 67 // for k, v in ForClause Ident 68 // let x = y LetClause Ident 69 // 70 // Fields inside lambda 71 // Label Field Expr 72 // Value Field Field 73 // Pkg nil ImportSpec 74 75 // Resolve resolves all identifiers in a file. Unresolved identifiers are 76 // recorded in Unresolved. It will not overwrite already resolved values. 77 func Resolve(f *ast.File, errFn ErrFunc) { 78 walk(&scope{errFn: errFn, identFn: resolveIdent}, f) 79 } 80 81 // Resolve resolves all identifiers in an expression. 82 // It will not overwrite already resolved values. 83 func ResolveExpr(e ast.Expr, errFn ErrFunc) { 84 f := &ast.File{} 85 walk(&scope{file: f, errFn: errFn, identFn: resolveIdent}, e) 86 } 87 88 // A Scope maintains the set of named language entities declared 89 // in the scope and a link to the immediately surrounding (outer) 90 // scope. 91 // 92 type scope struct { 93 file *ast.File 94 outer *scope 95 node ast.Node 96 index map[string]entry 97 inField bool 98 99 identFn func(s *scope, n *ast.Ident) bool 100 nameFn func(name string) 101 errFn func(p token.Pos, msg string, args ...interface{}) 102 } 103 104 type entry struct { 105 node ast.Node 106 link ast.Node // Alias, LetClause, or Field 107 } 108 109 func newScope(f *ast.File, outer *scope, node ast.Node, decls []ast.Decl) *scope { 110 const n = 4 // initial scope capacity 111 s := &scope{ 112 file: f, 113 outer: outer, 114 node: node, 115 index: make(map[string]entry, n), 116 identFn: outer.identFn, 117 nameFn: outer.nameFn, 118 errFn: outer.errFn, 119 } 120 for _, d := range decls { 121 switch x := d.(type) { 122 case *ast.Field: 123 label := x.Label 124 125 if a, ok := x.Label.(*ast.Alias); ok { 126 // TODO(legacy): use name := a.Ident.Name once quoted 127 // identifiers are no longer supported. 128 label, _ = a.Expr.(ast.Label) 129 if name, _, _ := ast.LabelName(a.Ident); name != "" { 130 if _, ok := label.(*ast.ListLit); !ok { 131 s.insert(name, x, a) 132 } 133 } 134 } 135 136 // default: 137 name, isIdent, _ := ast.LabelName(label) 138 if isIdent { 139 v := x.Value 140 // Avoid interpreting value aliases at this point. 141 if a, ok := v.(*ast.Alias); ok { 142 v = a.Expr 143 } 144 s.insert(name, v, x) 145 } 146 case *ast.LetClause: 147 name, isIdent, _ := ast.LabelName(x.Ident) 148 if isIdent { 149 s.insert(name, x, x) 150 } 151 case *ast.Alias: 152 name, isIdent, _ := ast.LabelName(x.Ident) 153 if isIdent { 154 s.insert(name, x, x) 155 } 156 case *ast.ImportDecl: 157 for _, spec := range x.Specs { 158 info, _ := ParseImportSpec(spec) 159 s.insert(info.Ident, spec, spec) 160 } 161 } 162 } 163 return s 164 } 165 166 func (s *scope) isLet(n ast.Node) bool { 167 if _, ok := s.node.(*ast.Field); ok { 168 return true 169 } 170 switch n.(type) { 171 case *ast.LetClause, *ast.Alias, *ast.Field: 172 return true 173 } 174 return false 175 } 176 177 func (s *scope) mustBeUnique(n ast.Node) bool { 178 if _, ok := s.node.(*ast.Field); ok { 179 return true 180 } 181 switch n.(type) { 182 // TODO: add *ast.ImportSpec when some implementations are moved over to 183 // Sanitize. 184 case *ast.ImportSpec, *ast.LetClause, *ast.Alias, *ast.Field: 185 return true 186 } 187 return false 188 } 189 190 func (s *scope) insert(name string, n, link ast.Node) { 191 if name == "" { 192 return 193 } 194 if s.nameFn != nil { 195 s.nameFn(name) 196 } 197 // TODO: record both positions. 198 if outer, _, existing := s.lookup(name); existing.node != nil { 199 if s.isLet(n) != outer.isLet(existing.node) { 200 s.errFn(n.Pos(), "cannot have both alias and field with name %q in same scope", name) 201 return 202 } else if s.mustBeUnique(n) || outer.mustBeUnique(existing.node) { 203 if outer == s { 204 if _, ok := existing.node.(*ast.ImportSpec); ok { 205 return 206 // TODO: 207 s.errFn(n.Pos(), "conflicting declaration %s\n"+ 208 "\tprevious declaration at %s", 209 name, existing.node.Pos()) 210 } else { 211 s.errFn(n.Pos(), "alias %q redeclared in same scope", name) 212 } 213 return 214 } 215 // TODO: Should we disallow shadowing of aliases? 216 // This was the case, but it complicates the transition to 217 // square brackets. The spec says allow it. 218 // s.errFn(n.Pos(), "alias %q already declared in enclosing scope", name) 219 } 220 } 221 s.index[name] = entry{node: n, link: link} 222 } 223 224 func (s *scope) resolveScope(name string, node ast.Node) (scope ast.Node, e entry, ok bool) { 225 last := s 226 for s != nil { 227 if n, ok := s.index[name]; ok && node == n.node { 228 if last.node == n.node { 229 return nil, n, true 230 } 231 return s.node, n, true 232 } 233 s, last = s.outer, s 234 } 235 return nil, entry{}, false 236 } 237 238 func (s *scope) lookup(name string) (p *scope, obj ast.Node, node entry) { 239 // TODO(#152): consider returning nil for obj if it is a reference to root. 240 // last := s 241 if name == "_" { 242 return nil, nil, entry{} 243 } 244 for s != nil { 245 if n, ok := s.index[name]; ok { 246 if _, ok := n.node.(*ast.ImportSpec); ok { 247 return s, nil, n 248 } 249 return s, s.node, n 250 } 251 // s, last = s.outer, s 252 s = s.outer 253 } 254 return nil, nil, entry{} 255 } 256 257 func (s *scope) After(n ast.Node) {} 258 func (s *scope) Before(n ast.Node) (w visitor) { 259 switch x := n.(type) { 260 case *ast.File: 261 s := newScope(x, s, x, x.Decls) 262 // Support imports. 263 for _, d := range x.Decls { 264 walk(s, d) 265 } 266 return nil 267 268 case *ast.StructLit: 269 return newScope(s.file, s, x, x.Elts) 270 271 case *ast.Comprehension: 272 s = scopeClauses(s, x.Clauses) 273 walk(s, x.Value) 274 return nil 275 276 case *ast.Field: 277 var n ast.Node = x.Label 278 alias, ok := x.Label.(*ast.Alias) 279 if ok { 280 n = alias.Expr 281 } 282 283 switch label := n.(type) { 284 case *ast.ParenExpr: 285 walk(s, label) 286 287 case *ast.Interpolation: 288 walk(s, label) 289 290 case *ast.ListLit: 291 if len(label.Elts) != 1 { 292 break 293 } 294 s = newScope(s.file, s, x, nil) 295 if alias != nil { 296 if name, _, _ := ast.LabelName(alias.Ident); name != "" { 297 s.insert(name, x, alias) 298 } 299 } 300 301 expr := label.Elts[0] 302 303 if a, ok := expr.(*ast.Alias); ok { 304 expr = a.Expr 305 306 // Add to current scope, instead of the value's, and allow 307 // references to bind to these illegally. 308 // We need this kind of administration anyway to detect 309 // illegal name clashes, and it allows giving better error 310 // messages. This puts the burdon on clients of this library 311 // to detect illegal usage, though. 312 name, err := ast.ParseIdent(a.Ident) 313 if err == nil { 314 s.insert(name, a.Expr, a) 315 } 316 } 317 318 ast.Walk(expr, nil, func(n ast.Node) { 319 if x, ok := n.(*ast.Ident); ok { 320 for s := s; s != nil && !s.inField; s = s.outer { 321 if _, ok := s.index[x.Name]; ok { 322 s.errFn(n.Pos(), 323 "reference %q in label expression refers to field against which it would be matched", x.Name) 324 } 325 } 326 } 327 }) 328 walk(s, expr) 329 } 330 331 if n := x.Value; n != nil { 332 if alias, ok := x.Value.(*ast.Alias); ok { 333 // TODO: this should move into Before once decl attributes 334 // have been fully deprecated and embed attributes are introduced. 335 s = newScope(s.file, s, x, nil) 336 s.insert(alias.Ident.Name, alias, x) 337 n = alias.Expr 338 } 339 s.inField = true 340 walk(s, n) 341 s.inField = false 342 } 343 344 return nil 345 346 case *ast.LetClause: 347 // Disallow referring to the current LHS name. 348 name := x.Ident.Name 349 saved := s.index[name] 350 delete(s.index, name) // The same name may still appear in another scope 351 352 if x.Expr != nil { 353 walk(s, x.Expr) 354 } 355 s.index[name] = saved 356 return nil 357 358 case *ast.Alias: 359 // Disallow referring to the current LHS name. 360 name := x.Ident.Name 361 saved := s.index[name] 362 delete(s.index, name) // The same name may still appear in another scope 363 364 if x.Expr != nil { 365 walk(s, x.Expr) 366 } 367 s.index[name] = saved 368 return nil 369 370 case *ast.ImportSpec: 371 return nil 372 373 case *ast.Attribute: 374 // TODO: tokenize attributes, resolve identifiers and store the ones 375 // that resolve in a list. 376 377 case *ast.SelectorExpr: 378 walk(s, x.X) 379 return nil 380 381 case *ast.Ident: 382 if s.identFn(s, x) { 383 return nil 384 } 385 } 386 return s 387 } 388 389 func resolveIdent(s *scope, x *ast.Ident) bool { 390 name, ok, _ := ast.LabelName(x) 391 if !ok { 392 // TODO: generate error 393 return false 394 } 395 if _, obj, node := s.lookup(name); node.node != nil { 396 switch { 397 case x.Node == nil: 398 x.Node = node.node 399 x.Scope = obj 400 401 case x.Node == node.node: 402 x.Scope = obj 403 404 default: // x.Node != node 405 scope, _, ok := s.resolveScope(name, x.Node) 406 if !ok { 407 s.file.Unresolved = append(s.file.Unresolved, x) 408 } 409 x.Scope = scope 410 } 411 } else { 412 s.file.Unresolved = append(s.file.Unresolved, x) 413 } 414 return true 415 } 416 417 func scopeClauses(s *scope, clauses []ast.Clause) *scope { 418 for _, c := range clauses { 419 switch x := c.(type) { 420 case *ast.ForClause: 421 walk(s, x.Source) 422 s = newScope(s.file, s, x, nil) 423 if x.Key != nil { 424 name, err := ast.ParseIdent(x.Key) 425 if err == nil { 426 s.insert(name, x.Key, x) 427 } 428 } 429 name, err := ast.ParseIdent(x.Value) 430 if err == nil { 431 s.insert(name, x.Value, x) 432 } 433 434 case *ast.LetClause: 435 walk(s, x.Expr) 436 s = newScope(s.file, s, x, nil) 437 name, err := ast.ParseIdent(x.Ident) 438 if err == nil { 439 s.insert(name, x.Ident, x) 440 } 441 442 default: 443 walk(s, c) 444 } 445 } 446 return s 447 } 448 449 // Debugging support 450 func (s *scope) String() string { 451 var buf bytes.Buffer 452 fmt.Fprintf(&buf, "scope %p {", s) 453 if s != nil && len(s.index) > 0 { 454 fmt.Fprintln(&buf) 455 for name := range s.index { 456 fmt.Fprintf(&buf, "\t%v\n", name) 457 } 458 } 459 fmt.Fprintf(&buf, "}\n") 460 return buf.String() 461 }