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