mirror of
https://github.com/davidhalter/parso.git
synced 2026-05-25 09:48:53 +08:00
Merge pull request #119 from isidentical/check-all-args
Check all arguments for unparenthesized generator expressions
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,19 @@ 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', 'no_errors'), [
|
||||||
|
('a(a for a in b,)', False),
|
||||||
|
('a(a for a in b, a)', False),
|
||||||
|
('a(a, a for a in b)', False),
|
||||||
|
('a(a, b, a for a in b, c, d)', False),
|
||||||
|
('a(a for a in b)', True),
|
||||||
|
('a((a for a in b), c)', True),
|
||||||
|
('a(c, (a for a in b))', True),
|
||||||
|
('a(a, b, (a for a in b), c, d)', True),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_unparenthesized_genexp(source, no_errors):
|
||||||
|
assert bool(_get_error_list(source)) ^ no_errors
|
||||||
|
|||||||
Reference in New Issue
Block a user