mirror of
https://github.com/davidhalter/parso.git
synced 2025-12-10 06:31:57 +08:00
Check for some lambda and comprehension errors when assigning to them.
This commit is contained in:
@@ -32,6 +32,21 @@ def _iter_stmts(scope):
|
|||||||
yield child
|
yield child
|
||||||
|
|
||||||
|
|
||||||
|
def _get_comprehension_type(atom):
|
||||||
|
first, second = atom.children[:2]
|
||||||
|
if second.type == 'testlist_comp' and second.children[1].type == 'comp_for':
|
||||||
|
if first == '[':
|
||||||
|
return 'list comprehension'
|
||||||
|
else:
|
||||||
|
return 'generator expression'
|
||||||
|
elif second.type == 'dictorsetmaker' and second.children[-1].type == 'comp_for':
|
||||||
|
if second.children[1] == ':':
|
||||||
|
return 'dict comprehension'
|
||||||
|
else:
|
||||||
|
return 'set comprehension'
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _is_future_import(import_from):
|
def _is_future_import(import_from):
|
||||||
# It looks like a __future__ import that is relative is still a future
|
# It looks like a __future__ import that is relative is still a future
|
||||||
# import. That feels kind of odd, but whatever.
|
# import. That feels kind of odd, but whatever.
|
||||||
@@ -86,9 +101,13 @@ class Context(object):
|
|||||||
def is_async_funcdef(self):
|
def is_async_funcdef(self):
|
||||||
# Stupidly enough async funcdefs can have two different forms,
|
# Stupidly enough async funcdefs can have two different forms,
|
||||||
# depending if a decorator is used or not.
|
# depending if a decorator is used or not.
|
||||||
return self.node.type == 'funcdef' \
|
return self.is_function() \
|
||||||
and self.node.parent.type in ('async_funcdef', 'async_stmt')
|
and self.node.parent.type in ('async_funcdef', 'async_stmt')
|
||||||
|
|
||||||
|
|
||||||
|
def is_function(self):
|
||||||
|
return self.node.type == 'funcdef'
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def add_block(self, node):
|
def add_block(self, node):
|
||||||
self.blocks.append(node)
|
self.blocks.append(node)
|
||||||
@@ -317,6 +336,29 @@ class ErrorFinder(Normalizer):
|
|||||||
if self._context.parent_context is None:
|
if self._context.parent_context is None:
|
||||||
message = "nonlocal declaration not allowed at module level"
|
message = "nonlocal declaration not allowed at module level"
|
||||||
self._add_syntax_error(message, node)
|
self._add_syntax_error(message, node)
|
||||||
|
elif self._context.is_function():
|
||||||
|
for nonlocal_name in node.children[1::2]:
|
||||||
|
param_names = [p.name.value for p in self._context.node.params]
|
||||||
|
if nonlocal_name.value == node:
|
||||||
|
pass
|
||||||
|
elif node.type == 'expr_stmt':
|
||||||
|
for before_equal in node.children[:-2:2]:
|
||||||
|
self._check_assignment(before_equal)
|
||||||
|
elif node.type == 'with_item':
|
||||||
|
self._check_assignment(node.children[2])
|
||||||
|
elif node.type == 'del_stmt':
|
||||||
|
child = node.children[1]
|
||||||
|
if child.type != 'expr_list': # Already handled.
|
||||||
|
self._check_assignment(child, is_deletion=True)
|
||||||
|
elif node.type == 'expr_list':
|
||||||
|
for expr in node.children[::2]:
|
||||||
|
self._check_assignment(expr)
|
||||||
|
|
||||||
|
# Some of the nodes here are already used, so no else if
|
||||||
|
if node.type in ('for_stmt', 'comp_for', 'list_for'):
|
||||||
|
child = node.children[1]
|
||||||
|
if child.type != 'expr_list': # Already handled.
|
||||||
|
self._check_assignment(child)
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
@@ -392,8 +434,21 @@ class ErrorFinder(Normalizer):
|
|||||||
# TODO probably this should get a better end_pos including
|
# TODO probably this should get a better end_pos including
|
||||||
# the next sibling of leaf.
|
# the next sibling of leaf.
|
||||||
self._add_syntax_error(message, leaf)
|
self._add_syntax_error(message, leaf)
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
def _check_assignment(self, node, is_deletion=False):
|
||||||
|
error = None
|
||||||
|
if node.type == 'lambdef':
|
||||||
|
error = 'lambda'
|
||||||
|
elif node.type == 'atom':
|
||||||
|
first, second = node.children[:2]
|
||||||
|
error = _get_comprehension_type(node)
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
message = "can't %s %s" % ("delete" if is_deletion else "assign to", error)
|
||||||
|
self._add_syntax_error(message, node)
|
||||||
|
|
||||||
def _add_indentation_error(self, message, spacing):
|
def _add_indentation_error(self, message, spacing):
|
||||||
self._add_error(903, "IndentationError: " + message, spacing)
|
self._add_error(903, "IndentationError: " + message, spacing)
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ def test_indentation_errors(code, positions):
|
|||||||
'return',
|
'return',
|
||||||
'yield',
|
'yield',
|
||||||
'try: pass\nexcept: pass\nexcept X: pass',
|
'try: pass\nexcept: pass\nexcept X: pass',
|
||||||
|
|
||||||
|
# SyntaxError from Python/ast.c
|
||||||
'f(x for x in bar, 1)',
|
'f(x for x in bar, 1)',
|
||||||
'from foo import a,',
|
'from foo import a,',
|
||||||
'from __future__ import whatever',
|
'from __future__ import whatever',
|
||||||
@@ -101,6 +103,11 @@ def test_indentation_errors(code, positions):
|
|||||||
'f(x=2, y)',
|
'f(x=2, y)',
|
||||||
'f(**x, *y)',
|
'f(**x, *y)',
|
||||||
'f(**x, y=3, z)',
|
'f(**x, y=3, z)',
|
||||||
|
'lambda a: 1 = 1',
|
||||||
|
'[x for x in y] = 1',
|
||||||
|
'{x for x in y} = 1',
|
||||||
|
'{x:x for x in y} = 1',
|
||||||
|
'(x for x in y) = 1',
|
||||||
|
|
||||||
# SyntaxErrors from Python/symtable.c
|
# SyntaxErrors from Python/symtable.c
|
||||||
'def f(x, x): pass',
|
'def f(x, x): pass',
|
||||||
|
|||||||
Reference in New Issue
Block a user