| # Tests of resolver errors. |
| # |
| # The initial environment contains the predeclared names "M" |
| # (module-specific) and "U" (universal). This distinction |
| # should be unobservable to the Starlark program. |
| |
| # use of declared global |
| x = 1 |
| _ = x |
| |
| --- |
| # premature use of global is not a static error; |
| # see github.com/google/skylark/issues/116. |
| _ = x |
| x = 1 |
| |
| --- |
| # use of undefined global |
| _ = x ### "undefined: x" |
| |
| --- |
| # redeclaration of global |
| x = 1 |
| x = 2 ### "cannot reassign global x declared at .*resolve.star:23:1" |
| |
| --- |
| # Redeclaration of predeclared names is allowed. |
| # |
| # This rule permits tool maintainers to add members to the predeclared |
| # environment without breaking existing programs. |
| |
| # module-specific predeclared name |
| M = 1 # ok |
| M = 2 ### "cannot reassign global M declared at .*/resolve.star" |
| |
| # universal predeclared name |
| U = 1 # ok |
| U = 1 ### "cannot reassign global U declared at .*/resolve.star" |
| |
| --- |
| # A global declaration shadows all references to a predeclared; |
| # see github.com/google/skylark/issues/116. |
| |
| a = U # ok: U is a reference to the global defined on the next line. |
| U = 1 |
| |
| --- |
| # reference to predeclared name |
| M() |
| |
| --- |
| # locals may be referenced before they are defined |
| |
| def f(): |
| M(x) # dynamic error |
| x = 1 |
| |
| --- |
| # Various forms of assignment: |
| |
| def f(x): # parameter |
| M(x) |
| M(y) ### "undefined: y" |
| |
| (a, b) = 1, 2 |
| M(a) |
| M(b) |
| M(c) ### "undefined: c" |
| |
| [p, q] = 1, 2 |
| M(p) |
| M(q) |
| M(r) ### "undefined: r" |
| |
| --- |
| # a comprehension introduces a separate lexical block |
| |
| _ = [x for x in "abc"] |
| M(x) ### "undefined: x" |
| |
| --- |
| # Functions may have forward refs. |
| def f(): |
| g() |
| h() ### "undefined: h" |
| def inner(): |
| i() |
| i = lambda: 0 |
| |
| def g(): |
| f() |
| |
| --- |
| # It is not permitted to rebind a global using a += assignment. |
| |
| x = [1] |
| x.extend([2]) # ok |
| x += [3] ### `cannot reassign global x` |
| |
| def f(): |
| x += [4] # x is local to f |
| |
| y = 1 |
| y += 2 ### `cannot reassign global y` |
| z += 3 # ok (but fails dynamically because z is undefined) |
| |
| --- |
| def f(a): |
| if 1==1: |
| b = 1 |
| c = 1 |
| M(a) # ok: param |
| M(b) # ok: maybe bound local |
| M(c) # ok: bound local |
| M(d) # NB: we don't do a use-before-def check on local vars! |
| M(e) # ok: global |
| M(f) # ok: global |
| d = 1 |
| |
| e = 1 |
| |
| --- |
| # This program should resolve successfully but fail dynamically. |
| x = 1 |
| |
| def f(): |
| M(x) # dynamic error: reference to undefined local |
| x = 2 |
| |
| f() |
| |
| --- |
| load("module", "name") # ok |
| |
| def f(): |
| load("foo", "bar") ### "load statement within a function" |
| |
| load("foo", |
| "", ### "load: empty identifier" |
| "_a", ### "load: names with leading underscores are not exported: _a" |
| b="", ### "load: empty identifier" |
| c="_d", ### "load: names with leading underscores are not exported: _d" |
| _e="f") # ok |
| |
| --- |
| # option:globalreassign |
| if M: |
| load("foo", "bar") ### "load statement within a conditional" |
| |
| --- |
| # option:globalreassign |
| for x in M: |
| load("foo", "bar") ### "load statement within a loop" |
| |
| --- |
| # option:recursion option:globalreassign |
| while M: |
| load("foo", "bar") ### "load statement within a loop" |
| |
| --- |
| # return statements must be within a function |
| |
| return ### "return statement not within a function" |
| |
| --- |
| # if-statements and for-loops at top-level are forbidden |
| # (without globalreassign option) |
| |
| for x in "abc": ### "for loop not within a function" |
| pass |
| |
| if x: ### "if statement not within a function" |
| pass |
| |
| --- |
| # option:globalreassign |
| |
| for x in "abc": # ok |
| pass |
| |
| if x: # ok |
| pass |
| |
| --- |
| # while loops are forbidden (without -recursion option) |
| |
| def f(): |
| while U: ### "dialect does not support while loops" |
| pass |
| |
| --- |
| # option:recursion |
| |
| def f(): |
| while U: # ok |
| pass |
| |
| while U: ### "while loop not within a function" |
| pass |
| |
| --- |
| # option:globalreassign option:recursion |
| |
| while U: # ok |
| pass |
| |
| --- |
| # The parser allows any expression on the LHS of an assignment. |
| |
| 1 = 0 ### "can't assign to literal" |
| 1+2 = 0 ### "can't assign to binaryexpr" |
| f() = 0 ### "can't assign to callexpr" |
| |
| [a, b] = 0 |
| [c, d] += 0 ### "can't use list expression in augmented assignment" |
| (e, f) += 0 ### "can't use tuple expression in augmented assignment" |
| |
| [] = 0 # ok |
| () = 0 # ok |
| |
| --- |
| # break and continue statements must appear within a loop |
| |
| break ### "break not in a loop" |
| |
| continue ### "continue not in a loop" |
| |
| pass |
| |
| --- |
| # Positional arguments (and required parameters) |
| # must appear before named arguments (and optional parameters). |
| |
| M(x=1, 2) ### `positional argument may not follow named` |
| |
| def f(x=1, y): pass ### `required parameter may not follow optional` |
| --- |
| # No parameters may follow **kwargs in a declaration. |
| |
| def f(**kwargs, x): ### `parameter may not follow \*\*kwargs` |
| pass |
| |
| def g(**kwargs, *args): ### `\* parameter may not follow \*\*kwargs` |
| pass |
| |
| def h(**kwargs1, **kwargs2): ### `multiple \*\* parameters not allowed` |
| pass |
| |
| --- |
| # Only keyword-only params and **kwargs may follow *args in a declaration. |
| |
| def f(*args, x): # ok |
| pass |
| |
| def g(*args1, *args2): ### `multiple \* parameters not allowed` |
| pass |
| |
| def h(*, ### `bare \* must be followed by keyword-only parameters` |
| *): ### `multiple \* parameters not allowed` |
| pass |
| |
| def i(*args, *): ### `multiple \* parameters not allowed` |
| pass |
| |
| def j(*, ### `bare \* must be followed by keyword-only parameters` |
| *args): ### `multiple \* parameters not allowed` |
| pass |
| |
| def k(*, **kwargs): ### `bare \* must be followed by keyword-only parameters` |
| pass |
| |
| def l(*): ### `bare \* must be followed by keyword-only parameters` |
| pass |
| |
| def m(*args, a=1, **kwargs): # ok |
| pass |
| |
| def n(*, a=1, **kwargs): # ok |
| pass |
| |
| --- |
| # No arguments may follow **kwargs in a call. |
| def f(*args, **kwargs): |
| pass |
| |
| f(**{}, 1) ### `argument may not follow \*\*kwargs` |
| f(**{}, x=1) ### `argument may not follow \*\*kwargs` |
| f(**{}, *[]) ### `\*args may not follow \*\*kwargs` |
| f(**{}, **{}) ### `multiple \*\*kwargs not allowed` |
| |
| --- |
| # Only **kwargs may follow *args in a call. |
| def f(*args, **kwargs): |
| pass |
| |
| f(*[], 1) ### `positional argument may not follow \*args` |
| f(*[], a=1) ### `keyword argument may not follow \*args` |
| f(*[], *[]) ### `multiple \*args not allowed` |
| f(*[], **{}) # ok |
| |
| --- |
| # Parameter names must be unique. |
| |
| def f(a, b, a): pass ### "duplicate parameter: a" |
| def g(args, b, *args): pass ### "duplicate parameter: args" |
| def h(kwargs, a, **kwargs): pass ### "duplicate parameter: kwargs" |
| def i(*x, **x): pass ### "duplicate parameter: x" |
| |
| --- |
| # Floating-point support is now standard. |
| a = float("3.141") |
| b = 1 / 2 |
| c = 3.141 |
| |
| --- |
| # option:globalreassign |
| # Legacy Bazel (and Python) semantics: def must precede use even for globals. |
| |
| _ = x ### `undefined: x` |
| x = 1 |
| |
| --- |
| # option:globalreassign |
| # Legacy Bazel (and Python) semantics: reassignment of globals is allowed. |
| x = 1 |
| x = 2 # ok |
| |
| --- |
| # option:globalreassign |
| # Redeclaration of predeclared names is allowed. |
| |
| # module-specific predeclared name |
| M = 1 # ok |
| M = 2 # ok (legacy) |
| |
| # universal predeclared name |
| U = 1 # ok |
| U = 1 # ok (legacy) |
| |
| --- |
| # https://github.com/bazelbuild/starlark/starlark/issues/21 |
| def f(**kwargs): pass |
| f(a=1, a=1) ### `keyword argument a repeated` |
| |
| |
| --- |
| # spelling |
| |
| print = U |
| |
| hello = 1 |
| print(hollo) ### `undefined: hollo \(did you mean hello\?\)` |
| |
| def f(abc): |
| print(abd) ### `undefined: abd \(did you mean abc\?\)` |
| print(goodbye) ### `undefined: goodbye$` |
| |
| --- |
| load("module", "x") # ok |
| x = 1 ### `cannot reassign local x` |
| load("module", "x") ### `cannot reassign top-level x` |
| |
| --- |
| # option:loadbindsglobally |
| load("module", "x") # ok |
| x = 1 ### `cannot reassign global x` |
| load("module", "x") ### `cannot reassign global x` |
| |
| --- |
| # option:globalreassign |
| load("module", "x") # ok |
| x = 1 # ok |
| load("module", "x") # ok |
| |
| --- |
| # option:globalreassign option:loadbindsglobally |
| load("module", "x") # ok |
| x = 1 |
| load("module", "x") # ok |
| |
| --- |
| _ = x # forward ref to file-local |
| load("module", "x") # ok |