Better checking of annotations/params and nonlocals/globals.

This commit is contained in:
Dave Halter
2017-07-30 12:23:17 +02:00
parent b7726d05cf
commit 2420f57a5c
3 changed files with 71 additions and 6 deletions

View File

@@ -129,23 +129,47 @@ class Context(object):
self._analyze_names(self._nonlocal_names, 'nonlocal') self._analyze_names(self._nonlocal_names, 'nonlocal')
def _analyze_names(self, globals_or_nonlocals, type_): def _analyze_names(self, globals_or_nonlocals, type_):
def raise_(message):
self._add_syntax_error(message % (base_name.value, type_), base_name)
params = []
if self.node.type == 'funcdef':
params = self.node.params
found_global_or_nonlocal = False
for base_name in globals_or_nonlocals: for base_name in globals_or_nonlocals:
search = base_name.value search = base_name.value
# Somehow Python does it the reversed way. # Somehow Python does it the reversed way.
for name in reversed(self._used_name_dict.get(search, [])): for name in reversed(self._used_name_dict.get(search, [])):
print(name, name.parent)
if name.start_pos > base_name.start_pos: if name.start_pos > base_name.start_pos:
# All following names don't have to be checked. # All following names don't have to be checked.
break found_global_or_nonlocal = True
parent = name.parent
if parent.type == 'param' and parent.name == name:
# Skip those here, these definitions belong to the next
# scope.
continue
if name.is_definition(): if name.is_definition():
if parent.type == 'expr_stmt' \
and parent.children[1].type == 'annassign':
raise_("annotated name '%s' can't be %s")
break
else:
message = "name '%s' is assigned to before %s declaration" message = "name '%s' is assigned to before %s declaration"
else: else:
message = "name '%s' is used prior to %s declaration" message = "name '%s' is used prior to %s declaration"
self._add_syntax_error(message % (name.value, type_), base_name)
if not found_global_or_nonlocal:
raise_(message)
# Only add an error for the first occurence. # Only add an error for the first occurence.
break break
for param in params:
if param.name.value == base_name.value:
raise_("name '%s' is parameter and %s"),
@contextmanager @contextmanager
def add_block(self, node): def add_block(self, node):
self.blocks.append(node) self.blocks.append(node)

View File

@@ -49,3 +49,8 @@ except ZeroDivisionError:
class X(): class X():
nonlocal a nonlocal a
def glob():
global x
y: foo = x

View File

@@ -176,6 +176,42 @@ def test_indentation_errors(code, positions):
def glob(): def glob():
x x
nonlocal x'''), nonlocal x'''),
# Annotation issues
dedent('''
def glob():
x[0]: foo
global x'''),
dedent('''
def glob():
x.a: foo
global x'''),
dedent('''
def glob():
x: foo
global x'''),
dedent('''
def glob():
x: foo = 5
global x'''),
dedent('''
def glob():
x: foo = 5
x
global x'''),
dedent('''
def glob():
global x
x: foo = 3
'''),
# global/nonlocal + param
dedent('''
def glob(x):
global x
'''),
dedent('''
def glob(x):
nonlocal x
'''),
# IndentationError # IndentationError
' foo', ' foo',