From 2420f57a5cc601f5f8aed7b9b98ae31527d022f7 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 30 Jul 2017 12:23:17 +0200 Subject: [PATCH] Better checking of annotations/params and nonlocals/globals. --- parso/python/normalizer.py | 36 +++++++++++++++---- test/normalizer_issue_files/allowed_syntax.py | 5 +++ test/test_python_errors.py | 36 +++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/parso/python/normalizer.py b/parso/python/normalizer.py index 4d872bd..26c068d 100644 --- a/parso/python/normalizer.py +++ b/parso/python/normalizer.py @@ -129,22 +129,46 @@ class Context(object): self._analyze_names(self._nonlocal_names, 'nonlocal') 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: search = base_name.value # Somehow Python does it the reversed way. for name in reversed(self._used_name_dict.get(search, [])): - print(name, name.parent) if name.start_pos > base_name.start_pos: # 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(): - message = "name '%s' is assigned to before %s declaration" + 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" else: message = "name '%s' is used prior to %s declaration" - self._add_syntax_error(message % (name.value, type_), base_name) - # Only add an error for the first occurence. - break + + if not found_global_or_nonlocal: + raise_(message) + # Only add an error for the first occurence. + break + + for param in params: + if param.name.value == base_name.value: + raise_("name '%s' is parameter and %s"), @contextmanager def add_block(self, node): diff --git a/test/normalizer_issue_files/allowed_syntax.py b/test/normalizer_issue_files/allowed_syntax.py index 2ed0d48..3607450 100644 --- a/test/normalizer_issue_files/allowed_syntax.py +++ b/test/normalizer_issue_files/allowed_syntax.py @@ -49,3 +49,8 @@ except ZeroDivisionError: class X(): nonlocal a + + +def glob(): + global x + y: foo = x diff --git a/test/test_python_errors.py b/test/test_python_errors.py index 4a9f2f8..44db064 100644 --- a/test/test_python_errors.py +++ b/test/test_python_errors.py @@ -176,6 +176,42 @@ def test_indentation_errors(code, positions): def glob(): 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 ' foo',