diff --git a/crates/ruff_python_parser/resources/inline/err/starred_starred_expression.py b/crates/ruff_python_parser/resources/inline/err/starred_starred_expression.py new file mode 100644 index 0000000000000..eb2d261a12d0d --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/err/starred_starred_expression.py @@ -0,0 +1,3 @@ +print(* +*[]) +print(* *[]) diff --git a/crates/ruff_python_parser/src/parser/expression.rs b/crates/ruff_python_parser/src/parser/expression.rs index 0ab15f62bf01e..0e40e5186fe92 100644 --- a/crates/ruff_python_parser/src/parser/expression.rs +++ b/crates/ruff_python_parser/src/parser/expression.rs @@ -2545,9 +2545,14 @@ impl<'src> Parser<'src> { self.bump(TokenKind::Star); let parsed_expr = match context.starred_expression_precedence() { - StarredExpressionPrecedence::Conditional => { - self.parse_conditional_expression_or_higher_impl(context) - } + StarredExpressionPrecedence::Conditional => self + .parse_conditional_expression_or_higher_impl( + // test_err starred_starred_expression + // print(* + // *[]) + // print(* *[]) + context.disallow_starred_expressions(), + ), StarredExpressionPrecedence::BitwiseOr => { self.parse_expression_with_bitwise_or_precedence() } @@ -2999,6 +3004,11 @@ impl ExpressionContext { ExpressionContext::starred_bitwise_or().with_yield_expression_allowed() } + pub(super) fn disallow_starred_expressions(self) -> Self { + let flags = self.0 & !ExpressionContextFlags::ALLOW_STARRED_EXPRESSION; + ExpressionContext(flags) + } + /// Returns a new [`ExpressionContext`] which allows starred expression with the given /// precedence. fn with_starred_expression_allowed(self, precedence: StarredExpressionPrecedence) -> Self { diff --git a/crates/ruff_python_parser/tests/snapshots/invalid_syntax@starred_starred_expression.py.snap b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@starred_starred_expression.py.snap new file mode 100644 index 0000000000000..ed69185e2c38e --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@starred_starred_expression.py.snap @@ -0,0 +1,129 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +--- +## AST + +``` +Module( + ModModule { + node_index: NodeIndex(None), + range: 0..26, + body: [ + Expr( + StmtExpr { + node_index: NodeIndex(None), + range: 0..12, + value: Call( + ExprCall { + node_index: NodeIndex(None), + range: 0..12, + func: Name( + ExprName { + node_index: NodeIndex(None), + range: 0..5, + id: Name("print"), + ctx: Load, + }, + ), + arguments: Arguments { + range: 5..12, + node_index: NodeIndex(None), + args: [ + Starred( + ExprStarred { + node_index: NodeIndex(None), + range: 6..11, + value: Starred( + ExprStarred { + node_index: NodeIndex(None), + range: 8..11, + value: List( + ExprList { + node_index: NodeIndex(None), + range: 9..11, + elts: [], + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + keywords: [], + }, + }, + ), + }, + ), + Expr( + StmtExpr { + node_index: NodeIndex(None), + range: 13..25, + value: Call( + ExprCall { + node_index: NodeIndex(None), + range: 13..25, + func: Name( + ExprName { + node_index: NodeIndex(None), + range: 13..18, + id: Name("print"), + ctx: Load, + }, + ), + arguments: Arguments { + range: 18..25, + node_index: NodeIndex(None), + args: [ + Starred( + ExprStarred { + node_index: NodeIndex(None), + range: 19..24, + value: Starred( + ExprStarred { + node_index: NodeIndex(None), + range: 21..24, + value: List( + ExprList { + node_index: NodeIndex(None), + range: 22..24, + elts: [], + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + keywords: [], + }, + }, + ), + }, + ), + ], + }, +) +``` +## Errors + + | +1 | print(* +2 | *[]) + | ^^^ Syntax Error: Starred expression cannot be used here +3 | print(* *[]) + | + + + | +1 | print(* +2 | *[]) +3 | print(* *[]) + | ^^^ Syntax Error: Starred expression cannot be used here + |