diff --git a/parso/python/normalizer.py b/parso/python/normalizer.py index c32bb97..99153c1 100644 --- a/parso/python/normalizer.py +++ b/parso/python/normalizer.py @@ -238,6 +238,16 @@ class ErrorFinder(Normalizer): self._context = Context(parent_scope, self._add_syntax_error) self._indentation_count = 0 + def visit(self, node): + if node.type == 'error_node': + with self.visit_node(node): + # Don't need to investigate the inners of an error node. We + # might find errors in there that should be ignored, because + # the error node itself already shows that there's an issue. + return '' + return super(ErrorFinder, self).visit(node) + + @contextmanager def visit_node(self, node): if node.type == 'error_node': @@ -345,6 +355,19 @@ class ErrorFinder(Normalizer): if node.parent.children[1].type == 'comp_for': message = "iterable unpacking cannot be used in comprehension" self._add_syntax_error(message, node) + if self._version <= (3, 4): + from parso.python.tree import search_ancestor + n = search_ancestor(node, 'for_stmt', 'expr_stmt') + found_definition = False + if n is not None: + for name in n.get_defined_names(): + if node.start_pos < name.start_pos < node.end_pos: + found_definition = True + break + + if not found_definition: + message = "can use starred expression only as assignment target" + self._add_syntax_error(message, node) elif node.type == 'comp_for': # Some of the nodes here are already used, so no else if expr_list = node.children[1 + int(node.children[0] == 'async')] diff --git a/test/failing_examples.py b/test/failing_examples.py index 69f2211..ed87ef4 100644 --- a/test/failing_examples.py +++ b/test/failing_examples.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys from textwrap import dedent @@ -283,3 +284,11 @@ else: 'async def foo():\n yield x\n return 1', 'async def foo():\n yield x\n return 1', ] + + +if sys.version_info[:2] <= (3, 4): + # Python > 3.4 this is valid code. + FAILING_EXAMPLES += [ + 'a = *[1], 2', + '(*[1], 2)', + ] diff --git a/test/test_python_errors.py b/test/test_python_errors.py index 95e04e3..86457b0 100644 --- a/test/test_python_errors.py +++ b/test/test_python_errors.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Testing if parso finds syntax errors and indentation errors. """ @@ -21,6 +20,20 @@ def assert_comparison(code, error_code, positions): assert [(pos, error_code) for pos in positions] == errors +@pytest.mark.parametrize('code', FAILING_EXAMPLES) +def test_python_exception_matches(code): + wanted, line_nr = _get_actual_exception(code) + + errors = _get_error_list(code) + actual = None + if errors: + error, = errors + actual = error.message + assert actual in wanted + # Somehow in Python3.3 the SyntaxError().lineno is sometimes None + assert line_nr is None or line_nr == error.start_pos[0] + + @pytest.mark.parametrize( ('code', 'positions'), [ ('1 +', [(1, 3)]), @@ -56,20 +69,6 @@ def test_indentation_errors(code, positions): assert_comparison(code, 903, positions) -@pytest.mark.parametrize('code', FAILING_EXAMPLES) -def test_python_exception_matches(code): - wanted, line_nr = _get_actual_exception(code) - - errors = _get_error_list(code) - actual = None - if errors: - error, = errors - actual = error.message - assert actual in wanted - # Somehow in Python3.3 the SyntaxError().lineno is sometimes None - assert line_nr is None or line_nr == error.start_pos[0] - - def _get_actual_exception(code): with warnings.catch_warnings(): # We don't care about warnings where locals/globals misbehave here. @@ -105,8 +104,9 @@ def _get_actual_exception(code): # Python 2.6 does has a slightly different error. wanted = 'SyntaxError: cannot assign to __debug__' elif wanted == 'SyntaxError: can use starred expression only as assignment target': - # Python 3.4/3.4 have a bit of a different warning than 3.5/3.6 - wanted = "SyntaxError: can't use starred expression here" + # Python 3.4/3.4 have a bit of a different warning than 3.5/3.6 in + # certain places. But in others this error makes sense. + return [wanted, "SyntaxError: can't use starred expression here"], line_nr return [wanted], line_nr