mirror of
https://github.com/davidhalter/parso.git
synced 2026-08-13 03:21:02 +08:00
Check all arguments for unparenthesized generator expressions
Previously only the first argument on the argument list checked against the generator expressions, now all argumnets are controlled.
This commit is contained in:
+50
-53
@@ -779,62 +779,59 @@ class _ArglistRule(SyntaxRule):
|
|||||||
return "Generator expression must be parenthesized"
|
return "Generator expression must be parenthesized"
|
||||||
|
|
||||||
def is_issue(self, node):
|
def is_issue(self, node):
|
||||||
first_arg = node.children[0]
|
arg_set = set()
|
||||||
if first_arg.type == 'argument' \
|
kw_only = False
|
||||||
and first_arg.children[1].type in _COMP_FOR_TYPES:
|
kw_unpacking_only = False
|
||||||
# e.g. foo(x for x in [], b)
|
is_old_starred = False
|
||||||
return len(node.children) >= 2
|
# In python 3 this would be a bit easier (stars are part of
|
||||||
else:
|
# argument), but we have to understand both.
|
||||||
arg_set = set()
|
for argument in node.children:
|
||||||
kw_only = False
|
if argument == ',':
|
||||||
kw_unpacking_only = False
|
continue
|
||||||
is_old_starred = False
|
|
||||||
# In python 3 this would be a bit easier (stars are part of
|
|
||||||
# argument), but we have to understand both.
|
|
||||||
for argument in node.children:
|
|
||||||
if argument == ',':
|
|
||||||
continue
|
|
||||||
|
|
||||||
if argument in ('*', '**'):
|
if argument in ('*', '**'):
|
||||||
# Python < 3.5 has the order engraved in the grammar
|
# Python < 3.5 has the order engraved in the grammar
|
||||||
# file. No need to do anything here.
|
# file. No need to do anything here.
|
||||||
is_old_starred = True
|
is_old_starred = True
|
||||||
continue
|
continue
|
||||||
if is_old_starred:
|
if is_old_starred:
|
||||||
is_old_starred = False
|
is_old_starred = False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if argument.type == 'argument':
|
if argument.type == 'argument':
|
||||||
first = argument.children[0]
|
first = argument.children[0]
|
||||||
if first in ('*', '**'):
|
if argument.children[1].type in _COMP_FOR_TYPES and len(node.children) >= 2:
|
||||||
if first == '*':
|
# a(a, b for b in c)
|
||||||
if kw_unpacking_only:
|
return True
|
||||||
# foo(**kwargs, *args)
|
if first in ('*', '**'):
|
||||||
message = "iterable argument unpacking " \
|
if first == '*':
|
||||||
"follows keyword argument unpacking"
|
if kw_unpacking_only:
|
||||||
self.add_issue(argument, message=message)
|
# foo(**kwargs, *args)
|
||||||
|
message = "iterable argument unpacking " \
|
||||||
|
"follows keyword argument unpacking"
|
||||||
|
self.add_issue(argument, message=message)
|
||||||
|
else:
|
||||||
|
kw_unpacking_only = True
|
||||||
|
else: # Is a keyword argument.
|
||||||
|
kw_only = True
|
||||||
|
if first.type == 'name':
|
||||||
|
if first.value in arg_set:
|
||||||
|
# f(x=1, x=2)
|
||||||
|
message = "keyword argument repeated"
|
||||||
|
if self._normalizer.version >= (3, 9):
|
||||||
|
message += ": {}".format(first.value)
|
||||||
|
self.add_issue(first, message=message)
|
||||||
else:
|
else:
|
||||||
kw_unpacking_only = True
|
arg_set.add(first.value)
|
||||||
else: # Is a keyword argument.
|
else:
|
||||||
kw_only = True
|
if kw_unpacking_only:
|
||||||
if first.type == 'name':
|
# f(**x, y)
|
||||||
if first.value in arg_set:
|
message = "positional argument follows keyword argument unpacking"
|
||||||
# f(x=1, x=2)
|
self.add_issue(argument, message=message)
|
||||||
message = "keyword argument repeated"
|
elif kw_only:
|
||||||
if self._normalizer.version >= (3, 9):
|
# f(x=2, y)
|
||||||
message += ": {}".format(first.value)
|
message = "positional argument follows keyword argument"
|
||||||
self.add_issue(first, message=message)
|
self.add_issue(argument, message=message)
|
||||||
else:
|
|
||||||
arg_set.add(first.value)
|
|
||||||
else:
|
|
||||||
if kw_unpacking_only:
|
|
||||||
# f(**x, y)
|
|
||||||
message = "positional argument follows keyword argument unpacking"
|
|
||||||
self.add_issue(argument, message=message)
|
|
||||||
elif kw_only:
|
|
||||||
# f(x=2, y)
|
|
||||||
message = "positional argument follows keyword argument"
|
|
||||||
self.add_issue(argument, message=message)
|
|
||||||
|
|
||||||
|
|
||||||
@ErrorFinder.register_rule(type='parameters')
|
@ErrorFinder.register_rule(type='parameters')
|
||||||
|
|||||||
@@ -375,3 +375,15 @@ def test_repeated_kwarg():
|
|||||||
_get_error_list("f(q=1, q=2)", version="3.9")[0].message
|
_get_error_list("f(q=1, q=2)", version="3.9")[0].message
|
||||||
== "SyntaxError: keyword argument repeated: q"
|
== "SyntaxError: keyword argument repeated: q"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'source', [
|
||||||
|
'a(a for a in b,)'
|
||||||
|
'a(a for a in b, a)',
|
||||||
|
'a(a, a for a in b)',
|
||||||
|
'a(a, b, a for a in b, c, d)',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_unparenthesized_genexp(source):
|
||||||
|
assert _get_error_list(source)
|
||||||
|
|||||||
Reference in New Issue
Block a user