mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-22 16:22:10 +00:00
## Summary
This PR fixes a bug to raise a syntax error when an unparenthesized
generator expression is used as an argument to a call when there are
more than one argument.
For reference, the grammar is:
```
primary:
| ...
| primary genexp
| primary '(' [arguments] ')'
| ...
genexp:
| '(' ( assignment_expression | expression !':=') for_if_clauses ')'
```
The `genexp` requires the parenthesis as mentioned in the grammar. So,
the grammar for a call expression is either a name followed by a
generator expression or a name followed by a list of argument. In the
former case, the parenthesis are excluded because the generator
expression provides them while in the later case, the parenthesis are
explicitly provided for a list of arguments which means that the
generator expression requires it's own parenthesis.
This was discovered in https://github.com/astral-sh/ruff/issues/12420.
## Test Plan
Add test cases for valid and invalid syntax.
Make sure that the parser from CPython also raises this at the parsing
step:
```console
$ python3.13 -m ast parser/_.py
File "parser/_.py", line 1
total(1, 2, x for x in range(5), 6)
^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized
$ python3.13 -m ast parser/_.py
File "parser/_.py", line 1
sum(x for x in range(10), 10)
^^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized
```
|
||
|---|---|---|
| .. | ||
| ambiguous_lpar_with_items_binary_expr.py | ||
| ambiguous_lpar_with_items_if_expr.py | ||
| ann_assign_stmt_simple_target.py | ||
| args_unparenthesized_generator.py | ||
| assign_targets_terminator.py | ||
| async_for_statement.py | ||
| async_function_definition.py | ||
| async_with_statement.py | ||
| class_def_arguments.py | ||
| comma_separated_regular_list_terminator.py | ||
| decorator_async_function.py | ||
| del_targets_terminator.py | ||
| dotted_name_normalized_spaces.py | ||
| except_stmt_as_name_soft_keyword.py | ||
| for_in_target_valid_expr.py | ||
| from_import_no_space.py | ||
| from_import_soft_keyword_module_name.py | ||
| from_import_stmt_terminator.py | ||
| fstring_format_spec_terminator.py | ||
| function_def_parameter_range.py | ||
| function_def_parenthesized_return_types.py | ||
| function_def_valid_return_expr.py | ||
| global_stmt.py | ||
| import_as_name_soft_keyword.py | ||
| import_stmt_terminator.py | ||
| lambda_with_no_parameters.py | ||
| lambda_with_valid_body.py | ||
| match_as_pattern.py | ||
| match_as_pattern_soft_keyword.py | ||
| match_attr_pattern_soft_keyword.py | ||
| match_classify_as_identifier_1.py | ||
| match_classify_as_identifier_2.py | ||
| match_classify_as_keyword_1.py | ||
| match_classify_as_keyword_2.py | ||
| match_classify_as_keyword_or_identifier.py | ||
| match_sequence_pattern_parentheses_terminator.py | ||
| match_sequence_pattern_terminator.py | ||
| match_stmt_subject_expr.py | ||
| match_stmt_valid_guard_expr.py | ||
| nonlocal_stmt.py | ||
| param_with_annotation.py | ||
| param_with_default.py | ||
| param_with_star_annotation.py | ||
| params_non_default_after_star.py | ||
| params_seen_keyword_only_param_after_star.py | ||
| simple_stmts_in_block.py | ||
| simple_stmts_with_semicolons.py | ||
| type_param_param_spec.py | ||
| type_param_type_var.py | ||
| type_param_type_var_tuple.py | ||