From e2e89bb4270083d6ea0882dfc05a2cc10a76d42a Mon Sep 17 00:00:00 2001 From: dylwil3 Date: Sat, 28 Mar 2026 22:28:27 -0500 Subject: [PATCH 1/3] disallow starred expressions as value of starred expression --- crates/ruff_python_parser/src/parser/expression.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/ruff_python_parser/src/parser/expression.rs b/crates/ruff_python_parser/src/parser/expression.rs index 0ab15f62bf01ea..9a5a21dd8a61e0 100644 --- a/crates/ruff_python_parser/src/parser/expression.rs +++ b/crates/ruff_python_parser/src/parser/expression.rs @@ -2545,9 +2545,10 @@ 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( + context.disallow_starred_expressions(), + ), StarredExpressionPrecedence::BitwiseOr => { self.parse_expression_with_bitwise_or_precedence() } @@ -2999,6 +3000,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 { From 60b780ead51d6d1d99cdb2b49da0e2fd519101b2 Mon Sep 17 00:00:00 2001 From: dylwil3 Date: Thu, 2 Apr 2026 07:22:40 -0500 Subject: [PATCH 2/3] add test --- .../inline/err/starred_starred_expression.py | 2 + .../src/parser/expression.rs | 3 + ..._syntax@starred_starred_expression.py.snap | 71 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 crates/ruff_python_parser/resources/inline/err/starred_starred_expression.py create mode 100644 crates/ruff_python_parser/tests/snapshots/invalid_syntax@starred_starred_expression.py.snap 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 00000000000000..b44ca7bc70f486 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/err/starred_starred_expression.py @@ -0,0 +1,2 @@ +print(* +*[]) diff --git a/crates/ruff_python_parser/src/parser/expression.rs b/crates/ruff_python_parser/src/parser/expression.rs index 9a5a21dd8a61e0..1ad2e9934a0c9a 100644 --- a/crates/ruff_python_parser/src/parser/expression.rs +++ b/crates/ruff_python_parser/src/parser/expression.rs @@ -2547,6 +2547,9 @@ impl<'src> Parser<'src> { let parsed_expr = match context.starred_expression_precedence() { StarredExpressionPrecedence::Conditional => self .parse_conditional_expression_or_higher_impl( + // test_err starred_starred_expression + // print(* + // *[]) context.disallow_starred_expressions(), ), StarredExpressionPrecedence::BitwiseOr => { 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 00000000000000..3c94233c5fe0c4 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@starred_starred_expression.py.snap @@ -0,0 +1,71 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +--- +## AST + +``` +Module( + ModModule { + node_index: NodeIndex(None), + range: 0..13, + 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: [], + }, + }, + ), + }, + ), + ], + }, +) +``` +## Errors + + | +1 | print(* +2 | *[]) + | ^^^ Syntax Error: Starred expression cannot be used here + | From f3a8a2201bb5676fdc3172b1c8c6a48ab0cba56a Mon Sep 17 00:00:00 2001 From: dylwil3 Date: Thu, 2 Apr 2026 09:51:37 -0500 Subject: [PATCH 3/3] add another test --- .../inline/err/starred_starred_expression.py | 1 + .../src/parser/expression.rs | 1 + ..._syntax@starred_starred_expression.py.snap | 60 ++++++++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) 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 index b44ca7bc70f486..eb2d261a12d0d3 100644 --- a/crates/ruff_python_parser/resources/inline/err/starred_starred_expression.py +++ b/crates/ruff_python_parser/resources/inline/err/starred_starred_expression.py @@ -1,2 +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 1ad2e9934a0c9a..0e40e5186fe92c 100644 --- a/crates/ruff_python_parser/src/parser/expression.rs +++ b/crates/ruff_python_parser/src/parser/expression.rs @@ -2550,6 +2550,7 @@ impl<'src> Parser<'src> { // test_err starred_starred_expression // print(* // *[]) + // print(* *[]) context.disallow_starred_expressions(), ), StarredExpressionPrecedence::BitwiseOr => { 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 index 3c94233c5fe0c4..ed69185e2c38e5 100644 --- 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 @@ -7,7 +7,7 @@ source: crates/ruff_python_parser/tests/fixtures.rs Module( ModModule { node_index: NodeIndex(None), - range: 0..13, + range: 0..26, body: [ Expr( StmtExpr { @@ -58,6 +58,55 @@ Module( ), }, ), + 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: [], + }, + }, + ), + }, + ), ], }, ) @@ -68,4 +117,13 @@ Module( 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 |