cuelang.org/go@v0.13.0/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 "fmt" 21 "strings" 22 23 "cuelang.org/go/cue/ast" 24 "cuelang.org/go/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 visitor := &scope{errFn: errFn, identFn: resolveIdent} 79 ast.Walk(f, visitor.Before, nil) 80 } 81 82 // Resolve resolves all identifiers in an expression. 83 // It will not overwrite already resolved values. 84 func ResolveExpr(e ast.Expr, errFn ErrFunc) { 85 f := &ast.File{} 86 visitor := &scope{file: f, errFn: errFn, identFn: resolveIdent} 87 ast.Walk(e, visitor.Before, nil) 88 } 89 90 // A Scope maintains the set of named language entities declared 91 // in the scope and a link to the immediately surrounding (outer) 92 // scope. 93 type scope struct { 94 file *ast.File 95 outer *scope 96 node ast.Node 97 index map[string]entry 98 inField bool 99 100 identFn func(s *scope, n *ast.Ident) bool 101 nameFn func(name string) 102 errFn func(p token.Pos, msg string, args ...interface{}) 103 } 104 105 type entry struct { 106 node ast.Node 107 link ast.Node // Alias, LetClause, or Field 108 } 109 110 func newScope(f *ast.File, outer *scope, node ast.Node, decls []ast.Decl) *scope { 111 const n = 4 // initial scope capacity 112 s := &scope{ 113 file: f, 114 outer: outer, 115 node: node, 116 index: make(map[string]entry, n), 117 identFn: outer.identFn, 118 nameFn: outer.nameFn, 119 errFn: outer.errFn, 120 } 121 for _, d := range decls { 122 switch x := d.(type) { 123 case *ast.Field: 124 label := x.Label 125 126 if a, ok := x.Label.(*ast.Alias); ok { 127 name := a.Ident.Name 128 if _, ok := a.Expr.(*ast.ListLit); !ok { 129 s.insert(name, x, a) 130 } 131 } 132 133 // default: 134 name, isIdent, _ := ast.LabelName(label) 135 if isIdent { 136 v := x.Value 137 // Avoid interpreting value aliases at this point. 138 if a, ok := v.(*ast.Alias); ok { 139 v = a.Expr 140 } 141 s.insert(name, v, x) 142 } 143 case *ast.LetClause: 144 name, isIdent, _ := ast.LabelName(x.Ident) 145 if isIdent { 146 s.insert(name, x, x) 147 } 148 case *ast.Alias: 149 name, isIdent, _ := ast.LabelName(x.Ident) 150 if isIdent { 151 s.insert(name, x, x) 152 } 153 case *ast.ImportDecl: 154 for _, spec := range x.Specs { 155 info, _ := ParseImportSpec(spec) 156 s.insert(info.Ident, spec, spec) 157 } 158 } 159 } 160 return s 161 } 162 163 func (s *scope) isLet(n ast.Node) bool { 164 if _, ok := s.node.(*ast.Field); ok { 165 return true 166 } 167 switch n.(type) { 168 case *ast.LetClause, *ast.Alias, *ast.Field: 169 return true 170 } 171 return false 172 } 173 174 func (s *scope) mustBeUnique(n ast.Node) bool { 175 if _, ok := s.node.(*ast.Field); ok { 176 return true 177 } 178 switch n.(type) { 179 // TODO: add *ast.ImportSpec when some implementations are moved over to 180 // Sanitize. 181 case *ast.ImportSpec, *ast.LetClause, *ast.Alias, *ast.Field: 182 return true 183 } 184 return false 185 } 186 187 func (s *scope) insert(name string, n, link ast.Node) { 188 if name == "" { 189 return 190 } 191 if s.nameFn != nil { 192 s.nameFn(name) 193 } 194 // TODO: record both positions. 195 if outer, _, existing := s.lookup(name); existing.node != nil { 196 if s.isLet(n) != outer.isLet(existing.node) { 197 s.errFn(n.Pos(), "cannot have both alias and field with name %q in same scope", name) 198 return 199 } else if s.mustBeUnique(n) || outer.mustBeUnique(existing.node) { 200 if outer == s { 201 if _, ok := existing.node.(*ast.ImportSpec); ok { 202 return 203 // TODO: 204 // s.errFn(n.Pos(), "conflicting declaration %s\n"+ 205 // "\tprevious declaration at %s", 206 // name, existing.node.Pos()) 207 } else { 208 s.errFn(n.Pos(), "alias %q redeclared in same scope", name) 209 } 210 return 211 } 212 // TODO: Should we disallow shadowing of aliases? 213 // This was the case, but it complicates the transition to 214 // square brackets. The spec says allow it. 215 // s.errFn(n.Pos(), "alias %q already declared in enclosing scope", name) 216 } 217 } 218 s.index[name] = entry{node: n, link: link} 219 } 220 221 func (s *scope) resolveScope(name string, node ast.Node) (scope ast.Node, e entry, ok bool) { 222 last := s 223 for s != nil { 224 if n, ok := s.index[name]; ok && node == n.node { 225 if last.node == n.node { 226 return nil, n, true 227 } 228 return s.node, n, true 229 } 230 s, last = s.outer, s 231 } 232 return nil, entry{}, false 233 } 234 235 func (s *scope) lookup(name string) (p *scope, obj ast.Node, node entry) { 236 // TODO(#152): consider returning nil for obj if it is a reference to root. 237 // last := s 238 if name == "_" { 239 return nil, nil, entry{} 240 } 241 for s != nil { 242 if n, ok := s.index[name]; ok { 243 if _, ok := n.node.(*ast.ImportSpec); ok { 244 return s, nil, n 245 } 246 return s, s.node, n 247 } 248 // s, last = s.outer, s 249 s = s.outer 250 } 251 return nil, nil, entry{} 252 } 253 254 func (s *scope) Before(n ast.Node) bool { 255 switch x := n.(type) { 256 case *ast.File: 257 s := newScope(x, s, x, x.Decls) 258 // Support imports. 259 for _, d := range x.Decls { 260 ast.Walk(d, s.Before, nil) 261 } 262 return false 263 264 case *ast.StructLit: 265 s = newScope(s.file, s, x, x.Elts) 266 for _, elt := range x.Elts { 267 ast.Walk(elt, s.Before, nil) 268 } 269 return false 270 271 case *ast.Comprehension: 272 s = scopeClauses(s, x.Clauses) 273 ast.Walk(x.Value, s.Before, nil) 274 return false 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 ast.Walk(label, s.Before, nil) 286 287 case *ast.Interpolation: 288 ast.Walk(label, s.Before, nil) 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 burden on clients of this library 311 // to detect illegal usage, though. 312 s.insert(a.Ident.Name, a.Expr, a) 313 } 314 315 ast.Walk(expr, nil, func(n ast.Node) { 316 if x, ok := n.(*ast.Ident); ok { 317 for s := s; s != nil && !s.inField; s = s.outer { 318 if _, ok := s.index[x.Name]; ok { 319 s.errFn(n.Pos(), 320 "reference %q in label expression refers to field against which it would be matched", x.Name) 321 } 322 } 323 } 324 }) 325 ast.Walk(expr, s.Before, nil) 326 } 327 328 if n := x.Value; n != nil { 329 if alias, ok := x.Value.(*ast.Alias); ok { 330 // TODO: this should move into Before once decl attributes 331 // have been fully deprecated and embed attributes are introduced. 332 s = newScope(s.file, s, x, nil) 333 s.insert(alias.Ident.Name, alias, x) 334 n = alias.Expr 335 } 336 s.inField = true 337 ast.Walk(n, s.Before, nil) 338 s.inField = false 339 } 340 341 return false 342 343 case *ast.LetClause: 344 // Disallow referring to the current LHS name. 345 name := x.Ident.Name 346 saved := s.index[name] 347 delete(s.index, name) // The same name may still appear in another scope 348 349 if x.Expr != nil { 350 ast.Walk(x.Expr, s.Before, nil) 351 } 352 s.index[name] = saved 353 return false 354 355 case *ast.Alias: 356 // Disallow referring to the current LHS name. 357 name := x.Ident.Name 358 saved := s.index[name] 359 delete(s.index, name) // The same name may still appear in another scope 360 361 if x.Expr != nil { 362 ast.Walk(x.Expr, s.Before, nil) 363 } 364 s.index[name] = saved 365 return false 366 367 case *ast.ImportSpec: 368 return false 369 370 case *ast.Attribute: 371 // TODO: tokenize attributes, resolve identifiers and store the ones 372 // that resolve in a list. 373 374 case *ast.SelectorExpr: 375 ast.Walk(x.X, s.Before, nil) 376 return false 377 378 case *ast.Ident: 379 if s.identFn(s, x) { 380 return false 381 } 382 } 383 return true 384 } 385 386 func resolveIdent(s *scope, x *ast.Ident) bool { 387 name, ok, _ := ast.LabelName(x) 388 if !ok { 389 // TODO: generate error 390 return false 391 } 392 if _, obj, node := s.lookup(name); node.node != nil { 393 switch x.Node { 394 case nil: 395 x.Node = node.node 396 x.Scope = obj 397 398 case node.node: 399 x.Scope = obj 400 401 default: // x.Node != node 402 scope, _, ok := s.resolveScope(name, x.Node) 403 if !ok { 404 s.file.Unresolved = append(s.file.Unresolved, x) 405 } 406 x.Scope = scope 407 } 408 } else { 409 s.file.Unresolved = append(s.file.Unresolved, x) 410 } 411 return true 412 } 413 414 func scopeClauses(s *scope, clauses []ast.Clause) *scope { 415 for _, c := range clauses { 416 switch x := c.(type) { 417 case *ast.ForClause: 418 ast.Walk(x.Source, s.Before, nil) 419 s = newScope(s.file, s, x, nil) 420 if x.Key != nil { 421 s.insert(x.Key.Name, x.Key, x) 422 } 423 s.insert(x.Value.Name, x.Value, x) 424 425 case *ast.LetClause: 426 ast.Walk(x.Expr, s.Before, nil) 427 s = newScope(s.file, s, x, nil) 428 s.insert(x.Ident.Name, x.Ident, x) 429 430 default: 431 ast.Walk(c, s.Before, nil) 432 } 433 } 434 return s 435 } 436 437 // Debugging support 438 func (s *scope) String() string { 439 var b strings.Builder 440 fmt.Fprintf(&b, "scope %p {", s) 441 if s != nil && len(s.index) > 0 { 442 fmt.Fprintln(&b) 443 for name := range s.index { 444 fmt.Fprintf(&b, "\t%v\n", name) 445 } 446 } 447 fmt.Fprintf(&b, "}\n") 448 return b.String() 449 }