go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/resolve/resolve.go (about) 1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package resolve defines a name-resolution pass for Starlark abstract 6 // syntax trees. 7 // 8 // The resolver sets the Locals and FreeVars arrays of each DefStmt and 9 // the LocalIndex field of each syntax.Ident that refers to a local or 10 // free variable. It also sets the Locals array of a File for locals 11 // bound by top-level comprehensions and load statements. 12 // Identifiers for global variables do not get an index. 13 package resolve // import "go.starlark.net/resolve" 14 15 // All references to names are statically resolved. Names may be 16 // predeclared, global, or local to a function or file. 17 // File-local variables include those bound by top-level comprehensions 18 // and by load statements. ("Top-level" means "outside of any function".) 19 // The resolver maps each global name to a small integer and each local 20 // name to a small integer; these integers enable a fast and compact 21 // representation of globals and locals in the evaluator. 22 // 23 // As an optimization, the resolver classifies each predeclared name as 24 // either universal (e.g. None, len) or per-module (e.g. glob in Bazel's 25 // build language), enabling the evaluator to share the representation 26 // of the universal environment across all modules. 27 // 28 // The lexical environment is a tree of blocks with the file block at 29 // its root. The file's child blocks may be of two kinds: functions 30 // and comprehensions, and these may have further children of either 31 // kind. 32 // 33 // Python-style resolution requires multiple passes because a name is 34 // determined to be local to a function only if the function contains a 35 // "binding" use of it; similarly, a name is determined to be global (as 36 // opposed to predeclared) if the module contains a top-level binding use. 37 // Unlike ordinary top-level assignments, the bindings created by load 38 // statements are local to the file block. 39 // A non-binding use may lexically precede the binding to which it is resolved. 40 // In the first pass, we inspect each function, recording in 41 // 'uses' each identifier and the environment block in which it occurs. 42 // If a use of a name is binding, such as a function parameter or 43 // assignment, we add the name to the block's bindings mapping and add a 44 // local variable to the enclosing function. 45 // 46 // As we finish resolving each function, we inspect all the uses within 47 // that function and discard ones that were found to be function-local. The 48 // remaining ones must be either free (local to some lexically enclosing 49 // function), or top-level (global, predeclared, or file-local), but we cannot tell 50 // which until we have finished inspecting the outermost enclosing 51 // function. At that point, we can distinguish local from top-level names 52 // (and this is when Python would compute free variables). 53 // 54 // However, Starlark additionally requires that all references to global 55 // names are satisfied by some declaration in the current module; 56 // Starlark permits a function to forward-reference a global or file-local 57 // that has not 58 // been declared yet so long as it is declared before the end of the 59 // module. So, instead of re-resolving the unresolved references after 60 // each top-level function, we defer this until the end of the module 61 // and ensure that all such references are satisfied by some definition. 62 // 63 // At the end of the module, we visit each of the nested function blocks 64 // in bottom-up order, doing a recursive lexical lookup for each 65 // unresolved name. If the name is found to be local to some enclosing 66 // function, we must create a DefStmt.FreeVar (capture) parameter for 67 // each intervening function. We enter these synthetic bindings into 68 // the bindings map so that we create at most one freevar per name. If 69 // the name was not local, we check that it was defined at module level. 70 // 71 // We resolve all uses of locals in the module (due to load statements 72 // and comprehensions) in a similar way and compute the file's set of 73 // local variables. 74 // 75 // Starlark enforces that all global names are assigned at most once on 76 // all control flow paths by forbidding if/else statements and loops at 77 // top level. A global may be used before it is defined, leading to a 78 // dynamic error. However, the AllowGlobalReassign flag (really: allow 79 // top-level reassign) makes the resolver allow multiple to a variable 80 // at top-level. It also allows if-, for-, and while-loops at top-level, 81 // which in turn may make the evaluator dynamically assign multiple 82 // values to a variable at top-level. (These two roles should be separated.) 83 84 import ( 85 "fmt" 86 "log" 87 "sort" 88 "strings" 89 90 "go.starlark.net/internal/spell" 91 "go.starlark.net/syntax" 92 ) 93 94 const debug = false 95 const doesnt = "this Starlark dialect does not " 96 97 // global options 98 // These features are either not standard Starlark (yet), or deprecated 99 // features of the BUILD language, so we put them behind flags. 100 // 101 // Deprecated: use an explicit [syntax.FileOptions] argument instead, 102 // as it avoids all the usual problems of global variables. 103 var ( 104 AllowSet = false // allow the 'set' built-in 105 AllowGlobalReassign = false // allow reassignment to top-level names; also, allow if/for/while at top-level 106 AllowRecursion = false // allow while statements and recursive functions 107 LoadBindsGlobally = false // load creates global not file-local bindings (deprecated) 108 109 // obsolete flags for features that are now standard. No effect. 110 AllowNestedDef = true 111 AllowLambda = true 112 AllowFloat = true 113 AllowBitwise = true 114 ) 115 116 // File resolves the specified file and records information about the 117 // module in file.Module. 118 // 119 // The isPredeclared and isUniversal predicates report whether a name is 120 // a pre-declared identifier (visible in the current module) or a 121 // universal identifier (visible in every module). 122 // Clients should typically pass predeclared.Has for the first and 123 // starlark.Universe.Has for the second, where predeclared is the 124 // module's StringDict of predeclared names and starlark.Universe is the 125 // standard set of built-ins. 126 // The isUniverse predicate is supplied a parameter to avoid a cyclic 127 // dependency upon starlark.Universe, not because users should ever need 128 // to redefine it. 129 func File(file *syntax.File, isPredeclared, isUniversal func(name string) bool) error { 130 return REPLChunk(file, nil, isPredeclared, isUniversal) 131 } 132 133 // REPLChunk is a generalization of the File function that supports a 134 // non-empty initial global block, as occurs in a REPL. 135 func REPLChunk(file *syntax.File, isGlobal, isPredeclared, isUniversal func(name string) bool) error { 136 r := newResolver(file.Options, isGlobal, isPredeclared, isUniversal) 137 r.stmts(file.Stmts) 138 139 r.env.resolveLocalUses() 140 141 // At the end of the module, resolve all non-local variable references, 142 // computing closures. 143 // Function bodies may contain forward references to later global declarations. 144 r.resolveNonLocalUses(r.env) 145 146 file.Module = &Module{ 147 Locals: r.moduleLocals, 148 Globals: r.moduleGlobals, 149 } 150 151 if len(r.errors) > 0 { 152 return r.errors 153 } 154 return nil 155 } 156 157 // Expr calls [ExprOptions] using [syntax.LegacyFileOptions]. 158 // Deprecated: relies on legacy global variables. 159 func Expr(expr syntax.Expr, isPredeclared, isUniversal func(name string) bool) ([]*Binding, error) { 160 return ExprOptions(syntax.LegacyFileOptions(), expr, isPredeclared, isUniversal) 161 } 162 163 // ExprOptions resolves the specified expression. 164 // It returns the local variables bound within the expression. 165 // 166 // The isPredeclared and isUniversal predicates behave as for the File function 167 func ExprOptions(opts *syntax.FileOptions, expr syntax.Expr, isPredeclared, isUniversal func(name string) bool) ([]*Binding, error) { 168 r := newResolver(opts, nil, isPredeclared, isUniversal) 169 r.expr(expr) 170 r.env.resolveLocalUses() 171 r.resolveNonLocalUses(r.env) // globals & universals 172 if len(r.errors) > 0 { 173 return nil, r.errors 174 } 175 return r.moduleLocals, nil 176 } 177 178 // An ErrorList is a non-empty list of resolver error messages. 179 type ErrorList []Error // len > 0 180 181 func (e ErrorList) Error() string { return e[0].Error() } 182 183 // An Error describes the nature and position of a resolver error. 184 type Error struct { 185 Pos syntax.Position 186 Msg string 187 } 188 189 func (e Error) Error() string { return e.Pos.String() + ": " + e.Msg } 190 191 func newResolver(options *syntax.FileOptions, isGlobal, isPredeclared, isUniversal func(name string) bool) *resolver { 192 file := new(block) 193 return &resolver{ 194 options: options, 195 file: file, 196 env: file, 197 isGlobal: isGlobal, 198 isPredeclared: isPredeclared, 199 isUniversal: isUniversal, 200 globals: make(map[string]*Binding), 201 predeclared: make(map[string]*Binding), 202 } 203 } 204 205 type resolver struct { 206 options *syntax.FileOptions 207 208 // env is the current local environment: 209 // a linked list of blocks, innermost first. 210 // The tail of the list is the file block. 211 env *block 212 file *block // file block (contains load bindings) 213 214 // moduleLocals contains the local variables of the module 215 // (due to load statements and comprehensions outside any function). 216 // moduleGlobals contains the global variables of the module. 217 moduleLocals []*Binding 218 moduleGlobals []*Binding 219 220 // globals maps each global name in the module to its binding. 221 // predeclared does the same for predeclared and universal names. 222 globals map[string]*Binding 223 predeclared map[string]*Binding 224 225 // These predicates report whether a name is 226 // pre-declared, either in this module or universally, 227 // or already declared in the module globals (as in a REPL). 228 // isGlobal may be nil. 229 isGlobal, isPredeclared, isUniversal func(name string) bool 230 231 loops int // number of enclosing for/while loops 232 ifstmts int // number of enclosing if statements loops 233 234 errors ErrorList 235 } 236 237 // container returns the innermost enclosing "container" block: 238 // a function (function != nil) or file (function == nil). 239 // Container blocks accumulate local variable bindings. 240 func (r *resolver) container() *block { 241 for b := r.env; ; b = b.parent { 242 if b.function != nil || b == r.file { 243 return b 244 } 245 } 246 } 247 248 func (r *resolver) push(b *block) { 249 r.env.children = append(r.env.children, b) 250 b.parent = r.env 251 r.env = b 252 } 253 254 func (r *resolver) pop() { r.env = r.env.parent } 255 256 type block struct { 257 parent *block // nil for file block 258 259 // In the file (root) block, both these fields are nil. 260 function *Function // only for function blocks 261 comp *syntax.Comprehension // only for comprehension blocks 262 263 // bindings maps a name to its binding. 264 // A local binding has an index into its innermost enclosing container's locals array. 265 // A free binding has an index into its innermost enclosing function's freevars array. 266 bindings map[string]*Binding 267 268 // children records the child blocks of the current one. 269 children []*block 270 271 // uses records all identifiers seen in this container (function or file), 272 // and a reference to the environment in which they appear. 273 // As we leave each container block, we resolve them, 274 // so that only free and global ones remain. 275 // At the end of each top-level function we compute closures. 276 uses []use 277 } 278 279 func (b *block) bind(name string, bind *Binding) { 280 if b.bindings == nil { 281 b.bindings = make(map[string]*Binding) 282 } 283 b.bindings[name] = bind 284 } 285 286 func (b *block) String() string { 287 if b.function != nil { 288 return "function block at " + fmt.Sprint(b.function.Pos) 289 } 290 if b.comp != nil { 291 return "comprehension block at " + fmt.Sprint(b.comp.Span()) 292 } 293 return "file block" 294 } 295 296 func (r *resolver) errorf(posn syntax.Position, format string, args ...interface{}) { 297 r.errors = append(r.errors, Error{posn, fmt.Sprintf(format, args...)}) 298 } 299 300 // A use records an identifier and the environment in which it appears. 301 type use struct { 302 id *syntax.Ident 303 env *block 304 } 305 306 // bind creates a binding for id: a global (not file-local) 307 // binding at top-level, a local binding otherwise. 308 // At top-level, it reports an error if a global or file-local 309 // binding already exists, unless AllowGlobalReassign. 310 // It sets id.Binding to the binding (whether old or new), 311 // and returns whether a binding already existed. 312 func (r *resolver) bind(id *syntax.Ident) bool { 313 // Binding outside any local (comprehension/function) block? 314 if r.env == r.file { 315 bind, ok := r.file.bindings[id.Name] 316 if !ok { 317 bind, ok = r.globals[id.Name] 318 if !ok { 319 // first global binding of this name 320 bind = &Binding{ 321 First: id, 322 Scope: Global, 323 Index: len(r.moduleGlobals), 324 } 325 r.globals[id.Name] = bind 326 r.moduleGlobals = append(r.moduleGlobals, bind) 327 } 328 } 329 if ok && !r.options.GlobalReassign { 330 r.errorf(id.NamePos, "cannot reassign %s %s declared at %s", 331 bind.Scope, id.Name, bind.First.NamePos) 332 } 333 id.Binding = bind 334 return ok 335 } 336 337 return r.bindLocal(id) 338 } 339 340 func (r *resolver) bindLocal(id *syntax.Ident) bool { 341 // Mark this name as local to current block. 342 // Assign it a new local (positive) index in the current container. 343 _, ok := r.env.bindings[id.Name] 344 if !ok { 345 var locals *[]*Binding 346 if fn := r.container().function; fn != nil { 347 locals = &fn.Locals 348 } else { 349 locals = &r.moduleLocals 350 } 351 bind := &Binding{ 352 First: id, 353 Scope: Local, 354 Index: len(*locals), 355 } 356 r.env.bind(id.Name, bind) 357 *locals = append(*locals, bind) 358 } 359 360 r.use(id) 361 return ok 362 } 363 364 func (r *resolver) use(id *syntax.Ident) { 365 use := use{id, r.env} 366 367 // The spec says that if there is a global binding of a name 368 // then all references to that name in that block refer to the 369 // global, even if the use precedes the def---just as for locals. 370 // For example, in this code, 371 // 372 // print(len); len=1; print(len) 373 // 374 // both occurrences of len refer to the len=1 binding, which 375 // completely shadows the predeclared len function. 376 // 377 // The rationale for these semantics, which differ from Python, 378 // is that the static meaning of len (a reference to a global) 379 // does not change depending on where it appears in the file. 380 // Of course, its dynamic meaning does change, from an error 381 // into a valid reference, so it's not clear these semantics 382 // have any practical advantage. 383 // 384 // In any case, the Bazel implementation lags behind the spec 385 // and follows Python behavior, so the first use of len refers 386 // to the predeclared function. This typically used in a BUILD 387 // file that redefines a predeclared name half way through, 388 // for example: 389 // 390 // proto_library(...) # built-in rule 391 // load("myproto.bzl", "proto_library") 392 // proto_library(...) # user-defined rule 393 // 394 // We will piggyback support for the legacy semantics on the 395 // AllowGlobalReassign flag, which is loosely related and also 396 // required for Bazel. 397 if r.options.GlobalReassign && r.env == r.file { 398 r.useToplevel(use) 399 return 400 } 401 402 b := r.container() 403 b.uses = append(b.uses, use) 404 } 405 406 // useToplevel resolves use.id as a reference to a name visible at top-level. 407 // The use.env field captures the original environment for error reporting. 408 func (r *resolver) useToplevel(use use) (bind *Binding) { 409 id := use.id 410 411 if prev, ok := r.file.bindings[id.Name]; ok { 412 // use of load-defined name in file block 413 bind = prev 414 } else if prev, ok := r.globals[id.Name]; ok { 415 // use of global declared by module 416 bind = prev 417 } else if r.isGlobal != nil && r.isGlobal(id.Name) { 418 // use of global defined in a previous REPL chunk 419 bind = &Binding{ 420 First: id, // wrong: this is not even a binding use 421 Scope: Global, 422 Index: len(r.moduleGlobals), 423 } 424 r.globals[id.Name] = bind 425 r.moduleGlobals = append(r.moduleGlobals, bind) 426 } else if prev, ok := r.predeclared[id.Name]; ok { 427 // repeated use of predeclared or universal 428 bind = prev 429 } else if r.isPredeclared(id.Name) { 430 // use of pre-declared name 431 bind = &Binding{Scope: Predeclared} 432 r.predeclared[id.Name] = bind // save it 433 } else if r.isUniversal(id.Name) { 434 // use of universal name 435 if !r.options.Set && id.Name == "set" { 436 r.errorf(id.NamePos, doesnt+"support sets") 437 } 438 bind = &Binding{Scope: Universal} 439 r.predeclared[id.Name] = bind // save it 440 } else { 441 bind = &Binding{Scope: Undefined} 442 var hint string 443 if n := r.spellcheck(use); n != "" { 444 hint = fmt.Sprintf(" (did you mean %s?)", n) 445 } 446 r.errorf(id.NamePos, "undefined: %s%s", id.Name, hint) 447 } 448 id.Binding = bind 449 return bind 450 } 451 452 // spellcheck returns the most likely misspelling of 453 // the name use.id in the environment use.env. 454 func (r *resolver) spellcheck(use use) string { 455 var names []string 456 457 // locals 458 for b := use.env; b != nil; b = b.parent { 459 for name := range b.bindings { 460 names = append(names, name) 461 } 462 } 463 464 // globals 465 // 466 // We have no way to enumerate the sets whose membership 467 // tests are isPredeclared, isUniverse, and isGlobal, 468 // which includes prior names in the REPL session. 469 for _, bind := range r.moduleGlobals { 470 names = append(names, bind.First.Name) 471 } 472 473 sort.Strings(names) 474 return spell.Nearest(use.id.Name, names) 475 } 476 477 // resolveLocalUses is called when leaving a container (function/module) 478 // block. It resolves all uses of locals/cells within that block. 479 func (b *block) resolveLocalUses() { 480 unresolved := b.uses[:0] 481 for _, use := range b.uses { 482 if bind := lookupLocal(use); bind != nil && (bind.Scope == Local || bind.Scope == Cell) { 483 use.id.Binding = bind 484 } else { 485 unresolved = append(unresolved, use) 486 } 487 } 488 b.uses = unresolved 489 } 490 491 func (r *resolver) stmts(stmts []syntax.Stmt) { 492 for _, stmt := range stmts { 493 r.stmt(stmt) 494 } 495 } 496 497 func (r *resolver) stmt(stmt syntax.Stmt) { 498 switch stmt := stmt.(type) { 499 case *syntax.ExprStmt: 500 r.expr(stmt.X) 501 502 case *syntax.BranchStmt: 503 if r.loops == 0 && (stmt.Token == syntax.BREAK || stmt.Token == syntax.CONTINUE) { 504 r.errorf(stmt.TokenPos, "%s not in a loop", stmt.Token) 505 } 506 507 case *syntax.IfStmt: 508 if !r.options.TopLevelControl && r.container().function == nil { 509 r.errorf(stmt.If, "if statement not within a function") 510 } 511 r.expr(stmt.Cond) 512 r.ifstmts++ 513 r.stmts(stmt.True) 514 r.stmts(stmt.False) 515 r.ifstmts-- 516 517 case *syntax.AssignStmt: 518 r.expr(stmt.RHS) 519 isAugmented := stmt.Op != syntax.EQ 520 r.assign(stmt.LHS, isAugmented) 521 522 case *syntax.DefStmt: 523 r.bind(stmt.Name) 524 fn := &Function{ 525 Name: stmt.Name.Name, 526 Pos: stmt.Def, 527 Params: stmt.Params, 528 Body: stmt.Body, 529 } 530 stmt.Function = fn 531 r.function(fn, stmt.Def) 532 533 case *syntax.ForStmt: 534 if !r.options.TopLevelControl && r.container().function == nil { 535 r.errorf(stmt.For, "for loop not within a function") 536 } 537 r.expr(stmt.X) 538 const isAugmented = false 539 r.assign(stmt.Vars, isAugmented) 540 r.loops++ 541 r.stmts(stmt.Body) 542 r.loops-- 543 544 case *syntax.WhileStmt: 545 if !r.options.While { 546 r.errorf(stmt.While, doesnt+"support while loops") 547 } 548 if !r.options.TopLevelControl && r.container().function == nil { 549 r.errorf(stmt.While, "while loop not within a function") 550 } 551 r.expr(stmt.Cond) 552 r.loops++ 553 r.stmts(stmt.Body) 554 r.loops-- 555 556 case *syntax.ReturnStmt: 557 if r.container().function == nil { 558 r.errorf(stmt.Return, "return statement not within a function") 559 } 560 if stmt.Result != nil { 561 r.expr(stmt.Result) 562 } 563 564 case *syntax.LoadStmt: 565 // A load statement may not be nested in any other statement. 566 if r.container().function != nil { 567 r.errorf(stmt.Load, "load statement within a function") 568 } else if r.loops > 0 { 569 r.errorf(stmt.Load, "load statement within a loop") 570 } else if r.ifstmts > 0 { 571 r.errorf(stmt.Load, "load statement within a conditional") 572 } 573 574 for i, from := range stmt.From { 575 if from.Name == "" { 576 r.errorf(from.NamePos, "load: empty identifier") 577 continue 578 } 579 if from.Name[0] == '_' { 580 r.errorf(from.NamePos, "load: names with leading underscores are not exported: %s", from.Name) 581 } 582 583 id := stmt.To[i] 584 if r.options.LoadBindsGlobally { 585 r.bind(id) 586 } else if r.bindLocal(id) && !r.options.GlobalReassign { 587 // "Global" in AllowGlobalReassign is a misnomer for "toplevel". 588 // Sadly we can't report the previous declaration 589 // as id.Binding may not be set yet. 590 r.errorf(id.NamePos, "cannot reassign top-level %s", id.Name) 591 } 592 } 593 594 default: 595 log.Panicf("unexpected stmt %T", stmt) 596 } 597 } 598 599 func (r *resolver) assign(lhs syntax.Expr, isAugmented bool) { 600 switch lhs := lhs.(type) { 601 case *syntax.Ident: 602 // x = ... 603 r.bind(lhs) 604 605 case *syntax.IndexExpr: 606 // x[i] = ... 607 r.expr(lhs.X) 608 r.expr(lhs.Y) 609 610 case *syntax.DotExpr: 611 // x.f = ... 612 r.expr(lhs.X) 613 614 case *syntax.TupleExpr: 615 // (x, y) = ... 616 if isAugmented { 617 r.errorf(syntax.Start(lhs), "can't use tuple expression in augmented assignment") 618 } 619 for _, elem := range lhs.List { 620 r.assign(elem, isAugmented) 621 } 622 623 case *syntax.ListExpr: 624 // [x, y, z] = ... 625 if isAugmented { 626 r.errorf(syntax.Start(lhs), "can't use list expression in augmented assignment") 627 } 628 for _, elem := range lhs.List { 629 r.assign(elem, isAugmented) 630 } 631 632 case *syntax.ParenExpr: 633 r.assign(lhs.X, isAugmented) 634 635 default: 636 name := strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", lhs), "*syntax.")) 637 r.errorf(syntax.Start(lhs), "can't assign to %s", name) 638 } 639 } 640 641 func (r *resolver) expr(e syntax.Expr) { 642 switch e := e.(type) { 643 case *syntax.Ident: 644 r.use(e) 645 646 case *syntax.Literal: 647 648 case *syntax.ListExpr: 649 for _, x := range e.List { 650 r.expr(x) 651 } 652 653 case *syntax.CondExpr: 654 r.expr(e.Cond) 655 r.expr(e.True) 656 r.expr(e.False) 657 658 case *syntax.IndexExpr: 659 r.expr(e.X) 660 r.expr(e.Y) 661 662 case *syntax.DictEntry: 663 r.expr(e.Key) 664 r.expr(e.Value) 665 666 case *syntax.SliceExpr: 667 r.expr(e.X) 668 if e.Lo != nil { 669 r.expr(e.Lo) 670 } 671 if e.Hi != nil { 672 r.expr(e.Hi) 673 } 674 if e.Step != nil { 675 r.expr(e.Step) 676 } 677 678 case *syntax.Comprehension: 679 // The 'in' operand of the first clause (always a ForClause) 680 // is resolved in the outer block; consider: [x for x in x]. 681 clause := e.Clauses[0].(*syntax.ForClause) 682 r.expr(clause.X) 683 684 // A list/dict comprehension defines a new lexical block. 685 // Locals defined within the block will be allotted 686 // distinct slots in the locals array of the innermost 687 // enclosing container (function/module) block. 688 r.push(&block{comp: e}) 689 690 const isAugmented = false 691 r.assign(clause.Vars, isAugmented) 692 693 for _, clause := range e.Clauses[1:] { 694 switch clause := clause.(type) { 695 case *syntax.IfClause: 696 r.expr(clause.Cond) 697 case *syntax.ForClause: 698 r.assign(clause.Vars, isAugmented) 699 r.expr(clause.X) 700 } 701 } 702 r.expr(e.Body) // body may be *DictEntry 703 r.pop() 704 705 case *syntax.TupleExpr: 706 for _, x := range e.List { 707 r.expr(x) 708 } 709 710 case *syntax.DictExpr: 711 for _, entry := range e.List { 712 entry := entry.(*syntax.DictEntry) 713 r.expr(entry.Key) 714 r.expr(entry.Value) 715 } 716 717 case *syntax.UnaryExpr: 718 r.expr(e.X) 719 720 case *syntax.BinaryExpr: 721 r.expr(e.X) 722 r.expr(e.Y) 723 724 case *syntax.DotExpr: 725 r.expr(e.X) 726 // ignore e.Name 727 728 case *syntax.CallExpr: 729 r.expr(e.Fn) 730 var seenVarargs, seenKwargs bool 731 var seenName map[string]bool 732 var n, p int 733 for _, arg := range e.Args { 734 pos, _ := arg.Span() 735 if unop, ok := arg.(*syntax.UnaryExpr); ok && unop.Op == syntax.STARSTAR { 736 // **kwargs 737 if seenKwargs { 738 r.errorf(pos, "multiple **kwargs not allowed") 739 } 740 seenKwargs = true 741 r.expr(arg) 742 } else if ok && unop.Op == syntax.STAR { 743 // *args 744 if seenKwargs { 745 r.errorf(pos, "*args may not follow **kwargs") 746 } else if seenVarargs { 747 r.errorf(pos, "multiple *args not allowed") 748 } 749 seenVarargs = true 750 r.expr(arg) 751 } else if binop, ok := arg.(*syntax.BinaryExpr); ok && binop.Op == syntax.EQ { 752 // k=v 753 n++ 754 if seenKwargs { 755 r.errorf(pos, "keyword argument may not follow **kwargs") 756 } else if seenVarargs { 757 r.errorf(pos, "keyword argument may not follow *args") 758 } 759 x := binop.X.(*syntax.Ident) 760 if seenName[x.Name] { 761 r.errorf(x.NamePos, "keyword argument %q is repeated", x.Name) 762 } else { 763 if seenName == nil { 764 seenName = make(map[string]bool) 765 } 766 seenName[x.Name] = true 767 } 768 r.expr(binop.Y) 769 } else { 770 // positional argument 771 p++ 772 if seenVarargs { 773 r.errorf(pos, "positional argument may not follow *args") 774 } else if seenKwargs { 775 r.errorf(pos, "positional argument may not follow **kwargs") 776 } else if len(seenName) > 0 { 777 r.errorf(pos, "positional argument may not follow named") 778 } 779 r.expr(arg) 780 } 781 } 782 783 // Fail gracefully if compiler-imposed limit is exceeded. 784 if p >= 256 { 785 pos, _ := e.Span() 786 r.errorf(pos, "%v positional arguments in call, limit is 255", p) 787 } 788 if n >= 256 { 789 pos, _ := e.Span() 790 r.errorf(pos, "%v keyword arguments in call, limit is 255", n) 791 } 792 793 case *syntax.LambdaExpr: 794 fn := &Function{ 795 Name: "lambda", 796 Pos: e.Lambda, 797 Params: e.Params, 798 Body: []syntax.Stmt{&syntax.ReturnStmt{Result: e.Body}}, 799 } 800 e.Function = fn 801 r.function(fn, e.Lambda) 802 803 case *syntax.ParenExpr: 804 r.expr(e.X) 805 806 default: 807 log.Panicf("unexpected expr %T", e) 808 } 809 } 810 811 func (r *resolver) function(function *Function, pos syntax.Position) { 812 // Resolve defaults in enclosing environment. 813 for _, param := range function.Params { 814 if binary, ok := param.(*syntax.BinaryExpr); ok { 815 r.expr(binary.Y) 816 } 817 } 818 819 // Enter function block. 820 b := &block{function: function} 821 r.push(b) 822 823 var seenOptional bool 824 var star *syntax.UnaryExpr // * or *args param 825 var starStar *syntax.Ident // **kwargs ident 826 var numKwonlyParams int 827 for _, param := range function.Params { 828 switch param := param.(type) { 829 case *syntax.Ident: 830 // e.g. x 831 if starStar != nil { 832 r.errorf(param.NamePos, "required parameter may not follow **%s", starStar.Name) 833 } else if star != nil { 834 numKwonlyParams++ 835 } else if seenOptional { 836 r.errorf(param.NamePos, "required parameter may not follow optional") 837 } 838 if r.bind(param) { 839 r.errorf(param.NamePos, "duplicate parameter: %s", param.Name) 840 } 841 842 case *syntax.BinaryExpr: 843 // e.g. y=dflt 844 if starStar != nil { 845 r.errorf(param.OpPos, "optional parameter may not follow **%s", starStar.Name) 846 } else if star != nil { 847 numKwonlyParams++ 848 } 849 if id := param.X.(*syntax.Ident); r.bind(id) { 850 r.errorf(param.OpPos, "duplicate parameter: %s", id.Name) 851 } 852 seenOptional = true 853 854 case *syntax.UnaryExpr: 855 // * or *args or **kwargs 856 if param.Op == syntax.STAR { 857 if starStar != nil { 858 r.errorf(param.OpPos, "* parameter may not follow **%s", starStar.Name) 859 } else if star != nil { 860 r.errorf(param.OpPos, "multiple * parameters not allowed") 861 } else { 862 star = param 863 } 864 } else { 865 if starStar != nil { 866 r.errorf(param.OpPos, "multiple ** parameters not allowed") 867 } 868 starStar = param.X.(*syntax.Ident) 869 } 870 } 871 } 872 873 // Bind the *args and **kwargs parameters at the end, 874 // so that regular parameters a/b/c are contiguous and 875 // there is no hole for the "*": 876 // def f(a, b, *args, c=0, **kwargs) 877 // def f(a, b, *, c=0, **kwargs) 878 if star != nil { 879 if id, _ := star.X.(*syntax.Ident); id != nil { 880 // *args 881 if r.bind(id) { 882 r.errorf(id.NamePos, "duplicate parameter: %s", id.Name) 883 } 884 function.HasVarargs = true 885 } else if numKwonlyParams == 0 { 886 r.errorf(star.OpPos, "bare * must be followed by keyword-only parameters") 887 } 888 } 889 if starStar != nil { 890 if r.bind(starStar) { 891 r.errorf(starStar.NamePos, "duplicate parameter: %s", starStar.Name) 892 } 893 function.HasKwargs = true 894 } 895 896 function.NumKwonlyParams = numKwonlyParams 897 r.stmts(function.Body) 898 899 // Resolve all uses of this function's local vars, 900 // and keep just the remaining uses of free/global vars. 901 b.resolveLocalUses() 902 903 // Leave function block. 904 r.pop() 905 906 // References within the function body to globals are not 907 // resolved until the end of the module. 908 } 909 910 func (r *resolver) resolveNonLocalUses(b *block) { 911 // First resolve inner blocks. 912 for _, child := range b.children { 913 r.resolveNonLocalUses(child) 914 } 915 for _, use := range b.uses { 916 use.id.Binding = r.lookupLexical(use, use.env) 917 } 918 } 919 920 // lookupLocal looks up an identifier within its immediately enclosing function. 921 func lookupLocal(use use) *Binding { 922 for env := use.env; env != nil; env = env.parent { 923 if bind, ok := env.bindings[use.id.Name]; ok { 924 if bind.Scope == Free { 925 // shouldn't exist till later 926 log.Panicf("%s: internal error: %s, %v", use.id.NamePos, use.id.Name, bind) 927 } 928 return bind // found 929 } 930 if env.function != nil { 931 break 932 } 933 } 934 return nil // not found in this function 935 } 936 937 // lookupLexical looks up an identifier use.id within its lexically enclosing environment. 938 // The use.env field captures the original environment for error reporting. 939 func (r *resolver) lookupLexical(use use, env *block) (bind *Binding) { 940 if debug { 941 fmt.Printf("lookupLexical %s in %s = ...\n", use.id.Name, env) 942 defer func() { fmt.Printf("= %v\n", bind) }() 943 } 944 945 // Is this the file block? 946 if env == r.file { 947 return r.useToplevel(use) // file-local, global, predeclared, or not found 948 } 949 950 // Defined in this block? 951 bind, ok := env.bindings[use.id.Name] 952 if !ok { 953 // Defined in parent block? 954 bind = r.lookupLexical(use, env.parent) 955 if env.function != nil && (bind.Scope == Local || bind.Scope == Free || bind.Scope == Cell) { 956 // Found in parent block, which belongs to enclosing function. 957 // Add the parent's binding to the function's freevars, 958 // and add a new 'free' binding to the inner function's block, 959 // and turn the parent's local into cell. 960 if bind.Scope == Local { 961 bind.Scope = Cell 962 } 963 index := len(env.function.FreeVars) 964 env.function.FreeVars = append(env.function.FreeVars, bind) 965 bind = &Binding{ 966 First: bind.First, 967 Scope: Free, 968 Index: index, 969 } 970 if debug { 971 fmt.Printf("creating freevar %v in function at %s: %s\n", 972 len(env.function.FreeVars), env.function.Pos, use.id.Name) 973 } 974 } 975 976 // Memoize, to avoid duplicate free vars 977 // and redundant global (failing) lookups. 978 env.bind(use.id.Name, bind) 979 } 980 return bind 981 }