blob: ce6711050ecba72f20843d819170ea3c732a4f29 [file] [log] [blame]
# 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