Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raise syntax error for unparenthesized generator expr in multi-argument call #12445

Merged
merged 1 commit into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
sum(x.val for x in bar)
min(x.val for x in bar)
max(x.val for x in bar)
sum(x.val for x in bar, 0)
sum((x.val for x in bar), 0)

# Multi-line
sum(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
sum(x for x in range(10), 5)
total(1, 2, x for x in range(5), 6)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sum(x for x in range(10))
5 changes: 5 additions & 0 deletions crates/ruff_python_parser/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ pub enum ParseErrorType {
UnparenthesizedNamedExpression,
/// An unparenthesized tuple expression was found where it is not allowed.
UnparenthesizedTupleExpression,
/// An unparenthesized generator expression was found where it is not allowed.
UnparenthesizedGeneratorExpression,

/// An invalid usage of a lambda expression was found.
InvalidLambdaExpressionUsage,
Expand Down Expand Up @@ -216,6 +218,9 @@ impl std::fmt::Display for ParseErrorType {
ParseErrorType::UnparenthesizedTupleExpression => {
f.write_str("Unparenthesized tuple expression cannot be used here")
}
ParseErrorType::UnparenthesizedGeneratorExpression => {
f.write_str("Unparenthesized generator expression cannot be used here")
}
ParseErrorType::InvalidYieldExpressionUsage => {
f.write_str("Yield expression cannot be used here")
}
Expand Down
26 changes: 23 additions & 3 deletions crates/ruff_python_parser/src/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2272,9 +2272,10 @@ impl<'src> Parser<'src> {
command
}

/// Validate that the given arguments doesn't have any duplicate keyword argument.
///
/// Report errors for all the duplicate names found.
/// Performs the following validations on the function call arguments:
/// 1. There aren't any duplicate keyword argument
/// 2. If there are more than one argument (positional or keyword), all generator expressions
/// present should be parenthesized.
fn validate_arguments(&mut self, arguments: &ast::Arguments) {
let mut all_arg_names =
FxHashSet::with_capacity_and_hasher(arguments.keywords.len(), FxBuildHasher);
Expand All @@ -2292,6 +2293,25 @@ impl<'src> Parser<'src> {
);
}
}

if arguments.len() > 1 {
for arg in arguments.args.iter() {
if let Some(ast::ExprGenerator {
range,
parenthesized: false,
..
}) = arg.as_generator_expr()
{
// test_ok args_unparenthesized_generator
// sum(x for x in range(10))

// test_err args_unparenthesized_generator
// sum(x for x in range(10), 5)
// total(1, 2, x for x in range(5), 6)
self.add_error(ParseErrorType::UnparenthesizedGeneratorExpression, range);
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/args_unparenthesized_generator.py
---
## AST

```
Module(
ModModule {
range: 0..65,
body: [
Expr(
StmtExpr {
range: 0..28,
value: Call(
ExprCall {
range: 0..28,
func: Name(
ExprName {
range: 0..3,
id: Name("sum"),
ctx: Load,
},
),
arguments: Arguments {
range: 3..28,
args: [
Generator(
ExprGenerator {
range: 4..24,
elt: Name(
ExprName {
range: 4..5,
id: Name("x"),
ctx: Load,
},
),
generators: [
Comprehension {
range: 6..24,
target: Name(
ExprName {
range: 10..11,
id: Name("x"),
ctx: Store,
},
),
iter: Call(
ExprCall {
range: 15..24,
func: Name(
ExprName {
range: 15..20,
id: Name("range"),
ctx: Load,
},
),
arguments: Arguments {
range: 20..24,
args: [
NumberLiteral(
ExprNumberLiteral {
range: 21..23,
value: Int(
10,
),
},
),
],
keywords: [],
},
},
),
ifs: [],
is_async: false,
},
],
parenthesized: false,
},
),
NumberLiteral(
ExprNumberLiteral {
range: 26..27,
value: Int(
5,
),
},
),
],
keywords: [],
},
},
),
},
),
Expr(
StmtExpr {
range: 29..64,
value: Call(
ExprCall {
range: 29..64,
func: Name(
ExprName {
range: 29..34,
id: Name("total"),
ctx: Load,
},
),
arguments: Arguments {
range: 34..64,
args: [
NumberLiteral(
ExprNumberLiteral {
range: 35..36,
value: Int(
1,
),
},
),
NumberLiteral(
ExprNumberLiteral {
range: 38..39,
value: Int(
2,
),
},
),
Generator(
ExprGenerator {
range: 41..60,
elt: Name(
ExprName {
range: 41..42,
id: Name("x"),
ctx: Load,
},
),
generators: [
Comprehension {
range: 43..60,
target: Name(
ExprName {
range: 47..48,
id: Name("x"),
ctx: Store,
},
),
iter: Call(
ExprCall {
range: 52..60,
func: Name(
ExprName {
range: 52..57,
id: Name("range"),
ctx: Load,
},
),
arguments: Arguments {
range: 57..60,
args: [
NumberLiteral(
ExprNumberLiteral {
range: 58..59,
value: Int(
5,
),
},
),
],
keywords: [],
},
},
),
ifs: [],
is_async: false,
},
],
parenthesized: false,
},
),
NumberLiteral(
ExprNumberLiteral {
range: 62..63,
value: Int(
6,
),
},
),
],
keywords: [],
},
},
),
},
),
],
},
)
```
## Errors

|
1 | sum(x for x in range(10), 5)
| ^^^^^^^^^^^^^^^^^^^^ Syntax Error: Unparenthesized generator expression cannot be used here
2 | total(1, 2, x for x in range(5), 6)
|


|
1 | sum(x for x in range(10), 5)
2 | total(1, 2, x for x in range(5), 6)
| ^^^^^^^^^^^^^^^^^^^ Syntax Error: Unparenthesized generator expression cannot be used here
|
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/ok/args_unparenthesized_generator.py
---
## AST

```
Module(
ModModule {
range: 0..26,
body: [
Expr(
StmtExpr {
range: 0..25,
value: Call(
ExprCall {
range: 0..25,
func: Name(
ExprName {
range: 0..3,
id: Name("sum"),
ctx: Load,
},
),
arguments: Arguments {
range: 3..25,
args: [
Generator(
ExprGenerator {
range: 4..24,
elt: Name(
ExprName {
range: 4..5,
id: Name("x"),
ctx: Load,
},
),
generators: [
Comprehension {
range: 6..24,
target: Name(
ExprName {
range: 10..11,
id: Name("x"),
ctx: Store,
},
),
iter: Call(
ExprCall {
range: 15..24,
func: Name(
ExprName {
range: 15..20,
id: Name("range"),
ctx: Load,
},
),
arguments: Arguments {
range: 20..24,
args: [
NumberLiteral(
ExprNumberLiteral {
range: 21..23,
value: Int(
10,
),
},
),
],
keywords: [],
},
},
),
ifs: [],
is_async: false,
},
],
parenthesized: false,
},
),
],
keywords: [],
},
},
),
},
),
],
},
)
```
Loading