From 6c14225c662148d680f7026d52d01de2e47f3938 Mon Sep 17 00:00:00 2001 From: Brent Westbrook <36778786+ntBre@users.noreply.github.com> Date: Thu, 6 Mar 2025 11:57:20 -0500 Subject: [PATCH] [syntax-errors] Tuple unpacking in `return` and `yield` before Python 3.8 (#16485) Summary -- Checks for tuple unpacking in `return` and `yield` statements before Python 3.8, as described [here]. Test Plan -- Inline tests. [here]: https://github.com/python/cpython/issues/76298 --- .../inline/err/iter_unpack_return_py37.py | 3 + .../inline/err/iter_unpack_yield_py37.py | 4 + .../inline/ok/iter_unpack_return_py37.py | 3 + .../inline/ok/iter_unpack_return_py38.py | 3 + .../inline/ok/iter_unpack_yield_py37.py | 3 + .../inline/ok/iter_unpack_yield_py38.py | 4 + crates/ruff_python_parser/src/error.rs | 66 +++++ .../src/parser/expression.rs | 26 +- .../src/parser/statement.rs | 51 +++- ...lid_syntax@iter_unpack_return_py37.py.snap | 146 ++++++++++ ...alid_syntax@iter_unpack_yield_py37.py.snap | 257 ++++++++++++++++++ ...lid_syntax@iter_unpack_return_py37.py.snap | 138 ++++++++++ ...lid_syntax@iter_unpack_return_py38.py.snap | 138 ++++++++++ ...alid_syntax@iter_unpack_yield_py37.py.snap | 143 ++++++++++ ...alid_syntax@iter_unpack_yield_py38.py.snap | 240 ++++++++++++++++ 15 files changed, 1217 insertions(+), 8 deletions(-) create mode 100644 crates/ruff_python_parser/resources/inline/err/iter_unpack_return_py37.py create mode 100644 crates/ruff_python_parser/resources/inline/err/iter_unpack_yield_py37.py create mode 100644 crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py37.py create mode 100644 crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py38.py create mode 100644 crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py37.py create mode 100644 crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py38.py create mode 100644 crates/ruff_python_parser/tests/snapshots/invalid_syntax@iter_unpack_return_py37.py.snap create mode 100644 crates/ruff_python_parser/tests/snapshots/invalid_syntax@iter_unpack_yield_py37.py.snap create mode 100644 crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_return_py37.py.snap create mode 100644 crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_return_py38.py.snap create mode 100644 crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_yield_py37.py.snap create mode 100644 crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_yield_py38.py.snap diff --git a/crates/ruff_python_parser/resources/inline/err/iter_unpack_return_py37.py b/crates/ruff_python_parser/resources/inline/err/iter_unpack_return_py37.py new file mode 100644 index 0000000000..5e382adf1f --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/err/iter_unpack_return_py37.py @@ -0,0 +1,3 @@ +# parse_options: {"target-version": "3.7"} +rest = (4, 5, 6) +def f(): return 1, 2, 3, *rest diff --git a/crates/ruff_python_parser/resources/inline/err/iter_unpack_yield_py37.py b/crates/ruff_python_parser/resources/inline/err/iter_unpack_yield_py37.py new file mode 100644 index 0000000000..974c9d81e0 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/err/iter_unpack_yield_py37.py @@ -0,0 +1,4 @@ +# parse_options: {"target-version": "3.7"} +rest = (4, 5, 6) +def g(): yield 1, 2, 3, *rest +def h(): yield 1, (yield 2, *rest), 3 diff --git a/crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py37.py b/crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py37.py new file mode 100644 index 0000000000..b12330e2d1 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py37.py @@ -0,0 +1,3 @@ +# parse_options: {"target-version": "3.7"} +rest = (4, 5, 6) +def f(): return (1, 2, 3, *rest) diff --git a/crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py38.py b/crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py38.py new file mode 100644 index 0000000000..7ed1afde88 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py38.py @@ -0,0 +1,3 @@ +# parse_options: {"target-version": "3.8"} +rest = (4, 5, 6) +def f(): return 1, 2, 3, *rest diff --git a/crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py37.py b/crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py37.py new file mode 100644 index 0000000000..3595931df9 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py37.py @@ -0,0 +1,3 @@ +# parse_options: {"target-version": "3.7"} +rest = (4, 5, 6) +def g(): yield (1, 2, 3, *rest) diff --git a/crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py38.py b/crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py38.py new file mode 100644 index 0000000000..f108ca2e66 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py38.py @@ -0,0 +1,4 @@ +# parse_options: {"target-version": "3.8"} +rest = (4, 5, 6) +def g(): yield 1, 2, 3, *rest +def h(): yield 1, (yield 2, *rest), 3 diff --git a/crates/ruff_python_parser/src/error.rs b/crates/ruff_python_parser/src/error.rs index 4cdb4ab814..79acba4616 100644 --- a/crates/ruff_python_parser/src/error.rs +++ b/crates/ruff_python_parser/src/error.rs @@ -444,11 +444,68 @@ pub struct UnsupportedSyntaxError { pub target_version: PythonVersion, } +/// The type of tuple unpacking for [`UnsupportedSyntaxErrorKind::StarTuple`]. +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum StarTupleKind { + Return, + Yield, +} + #[derive(Debug, PartialEq, Clone, Copy)] pub enum UnsupportedSyntaxErrorKind { Match, Walrus, ExceptStar, + + /// Represents the use of unparenthesized tuple unpacking in a `return` statement or `yield` + /// expression before Python 3.8. + /// + /// ## Examples + /// + /// Before Python 3.8, this syntax was allowed: + /// + /// ```python + /// rest = (4, 5, 6) + /// + /// def f(): + /// t = 1, 2, 3, *rest + /// return t + /// + /// def g(): + /// t = 1, 2, 3, *rest + /// yield t + /// ``` + /// + /// But this was not: + /// + /// ```python + /// rest = (4, 5, 6) + /// + /// def f(): + /// return 1, 2, 3, *rest + /// + /// def g(): + /// yield 1, 2, 3, *rest + /// ``` + /// + /// Instead, parentheses were required in the `return` and `yield` cases: + /// + /// ```python + /// rest = (4, 5, 6) + /// + /// def f(): + /// return (1, 2, 3, *rest) + /// + /// def g(): + /// yield (1, 2, 3, *rest) + /// ``` + /// + /// This was reported in [BPO 32117] and updated in Python 3.8 to allow the unparenthesized + /// form. + /// + /// [BPO 32117]: https://github.com/python/cpython/issues/76298 + StarTuple(StarTupleKind), + /// Represents the use of a "relaxed" [PEP 614] decorator before Python 3.9. /// /// ## Examples @@ -480,6 +537,7 @@ pub enum UnsupportedSyntaxErrorKind { /// [`dotted_name`]: https://docs.python.org/3.8/reference/compound_stmts.html#grammar-token-dotted-name /// [decorator grammar]: https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-decorator RelaxedDecorator, + /// Represents the use of a [PEP 570] positional-only parameter before Python 3.8. /// /// ## Examples @@ -506,6 +564,7 @@ pub enum UnsupportedSyntaxErrorKind { /// /// [PEP 570]: https://peps.python.org/pep-0570/ PositionalOnlyParameter, + /// Represents the use of a [type parameter list] before Python 3.12. /// /// ## Examples @@ -544,6 +603,12 @@ impl Display for UnsupportedSyntaxError { UnsupportedSyntaxErrorKind::Match => "Cannot use `match` statement", UnsupportedSyntaxErrorKind::Walrus => "Cannot use named assignment expression (`:=`)", UnsupportedSyntaxErrorKind::ExceptStar => "Cannot use `except*`", + UnsupportedSyntaxErrorKind::StarTuple(StarTupleKind::Return) => { + "Cannot use iterable unpacking in return statements" + } + UnsupportedSyntaxErrorKind::StarTuple(StarTupleKind::Yield) => { + "Cannot use iterable unpacking in yield expressions" + } UnsupportedSyntaxErrorKind::RelaxedDecorator => "Unsupported expression in decorators", UnsupportedSyntaxErrorKind::PositionalOnlyParameter => { "Cannot use positional-only parameter separator" @@ -570,6 +635,7 @@ impl UnsupportedSyntaxErrorKind { UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310, UnsupportedSyntaxErrorKind::Walrus => PythonVersion::PY38, UnsupportedSyntaxErrorKind::ExceptStar => PythonVersion::PY311, + UnsupportedSyntaxErrorKind::StarTuple(_) => PythonVersion::PY38, UnsupportedSyntaxErrorKind::RelaxedDecorator => PythonVersion::PY39, UnsupportedSyntaxErrorKind::PositionalOnlyParameter => PythonVersion::PY38, UnsupportedSyntaxErrorKind::TypeParameterList => PythonVersion::PY312, diff --git a/crates/ruff_python_parser/src/parser/expression.rs b/crates/ruff_python_parser/src/parser/expression.rs index 957b1d277e..9452d607eb 100644 --- a/crates/ruff_python_parser/src/parser/expression.rs +++ b/crates/ruff_python_parser/src/parser/expression.rs @@ -11,6 +11,7 @@ use ruff_python_ast::{ }; use ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; +use crate::error::StarTupleKind; use crate::parser::progress::ParserProgress; use crate::parser::{helpers, FunctionKind, Parser}; use crate::string::{parse_fstring_literal_element, parse_string_literal, StringType}; @@ -2089,10 +2090,27 @@ impl<'src> Parser<'src> { } let value = self.at_expr().then(|| { - Box::new( - self.parse_expression_list(ExpressionContext::starred_bitwise_or()) - .expr, - ) + let parsed_expr = self.parse_expression_list(ExpressionContext::starred_bitwise_or()); + + // test_ok iter_unpack_yield_py37 + // # parse_options: {"target-version": "3.7"} + // rest = (4, 5, 6) + // def g(): yield (1, 2, 3, *rest) + + // test_ok iter_unpack_yield_py38 + // # parse_options: {"target-version": "3.8"} + // rest = (4, 5, 6) + // def g(): yield 1, 2, 3, *rest + // def h(): yield 1, (yield 2, *rest), 3 + + // test_err iter_unpack_yield_py37 + // # parse_options: {"target-version": "3.7"} + // rest = (4, 5, 6) + // def g(): yield 1, 2, 3, *rest + // def h(): yield 1, (yield 2, *rest), 3 + self.check_tuple_unpacking(&parsed_expr, StarTupleKind::Yield); + + Box::new(parsed_expr.expr) }); Expr::Yield(ast::ExprYield { diff --git a/crates/ruff_python_parser/src/parser/statement.rs b/crates/ruff_python_parser/src/parser/statement.rs index bd569f613d..4a0d9f048e 100644 --- a/crates/ruff_python_parser/src/parser/statement.rs +++ b/crates/ruff_python_parser/src/parser/statement.rs @@ -10,6 +10,7 @@ use ruff_python_ast::{ }; use ruff_text_size::{Ranged, TextRange, TextSize}; +use crate::error::StarTupleKind; use crate::parser::expression::{ParsedExpr, EXPR_SET}; use crate::parser::progress::ParserProgress; use crate::parser::{ @@ -389,10 +390,25 @@ impl<'src> Parser<'src> { // return x := 1 // return *x and y let value = self.at_expr().then(|| { - Box::new( - self.parse_expression_list(ExpressionContext::starred_bitwise_or()) - .expr, - ) + let parsed_expr = self.parse_expression_list(ExpressionContext::starred_bitwise_or()); + + // test_ok iter_unpack_return_py37 + // # parse_options: {"target-version": "3.7"} + // rest = (4, 5, 6) + // def f(): return (1, 2, 3, *rest) + + // test_ok iter_unpack_return_py38 + // # parse_options: {"target-version": "3.8"} + // rest = (4, 5, 6) + // def f(): return 1, 2, 3, *rest + + // test_err iter_unpack_return_py37 + // # parse_options: {"target-version": "3.7"} + // rest = (4, 5, 6) + // def f(): return 1, 2, 3, *rest + self.check_tuple_unpacking(&parsed_expr, StarTupleKind::Return); + + Box::new(parsed_expr.expr) }); ast::StmtReturn { @@ -401,6 +417,33 @@ impl<'src> Parser<'src> { } } + /// Report [`UnsupportedSyntaxError`]s for each starred element in `expr` if it is an + /// unparenthesized tuple. + /// + /// This method can be used to check for tuple unpacking in return and yield statements, which + /// are only allowed in Python 3.8 and later: . + pub(crate) fn check_tuple_unpacking(&mut self, expr: &Expr, kind: StarTupleKind) { + let kind = UnsupportedSyntaxErrorKind::StarTuple(kind); + if self.options.target_version >= kind.minimum_version() { + return; + } + + let Expr::Tuple(ast::ExprTuple { + elts, + parenthesized: false, + .. + }) = expr + else { + return; + }; + + for elt in elts { + if elt.is_starred_expr() { + self.add_unsupported_syntax_error(kind, elt.range()); + } + } + } + /// Parses a `raise` statement. /// /// # Panics diff --git a/crates/ruff_python_parser/tests/snapshots/invalid_syntax@iter_unpack_return_py37.py.snap b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@iter_unpack_return_py37.py.snap new file mode 100644 index 0000000000..0aa69e8be1 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@iter_unpack_return_py37.py.snap @@ -0,0 +1,146 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/err/iter_unpack_return_py37.py +--- +## AST + +``` +Module( + ModModule { + range: 0..91, + body: [ + Assign( + StmtAssign { + range: 43..59, + targets: [ + Name( + ExprName { + range: 43..47, + id: Name("rest"), + ctx: Store, + }, + ), + ], + value: Tuple( + ExprTuple { + range: 50..59, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 51..52, + value: Int( + 4, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 54..55, + value: Int( + 5, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 57..58, + value: Int( + 6, + ), + }, + ), + ], + ctx: Load, + parenthesized: true, + }, + ), + }, + ), + FunctionDef( + StmtFunctionDef { + range: 60..90, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("f"), + range: 64..65, + }, + type_params: None, + parameters: Parameters { + range: 65..67, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Return( + StmtReturn { + range: 69..90, + value: Some( + Tuple( + ExprTuple { + range: 76..90, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 76..77, + value: Int( + 1, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 79..80, + value: Int( + 2, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 82..83, + value: Int( + 3, + ), + }, + ), + Starred( + ExprStarred { + range: 85..90, + value: Name( + ExprName { + range: 86..90, + id: Name("rest"), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + ctx: Load, + parenthesized: false, + }, + ), + ), + }, + ), + ], + }, + ), + ], + }, +) +``` +## Unsupported Syntax Errors + + | +1 | # parse_options: {"target-version": "3.7"} +2 | rest = (4, 5, 6) +3 | def f(): return 1, 2, 3, *rest + | ^^^^^ Syntax Error: Cannot use iterable unpacking in return statements on Python 3.7 (syntax was added in Python 3.8) + | diff --git a/crates/ruff_python_parser/tests/snapshots/invalid_syntax@iter_unpack_yield_py37.py.snap b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@iter_unpack_yield_py37.py.snap new file mode 100644 index 0000000000..44b0fe51e3 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@iter_unpack_yield_py37.py.snap @@ -0,0 +1,257 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/err/iter_unpack_yield_py37.py +--- +## AST + +``` +Module( + ModModule { + range: 0..128, + body: [ + Assign( + StmtAssign { + range: 43..59, + targets: [ + Name( + ExprName { + range: 43..47, + id: Name("rest"), + ctx: Store, + }, + ), + ], + value: Tuple( + ExprTuple { + range: 50..59, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 51..52, + value: Int( + 4, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 54..55, + value: Int( + 5, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 57..58, + value: Int( + 6, + ), + }, + ), + ], + ctx: Load, + parenthesized: true, + }, + ), + }, + ), + FunctionDef( + StmtFunctionDef { + range: 60..89, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("g"), + range: 64..65, + }, + type_params: None, + parameters: Parameters { + range: 65..67, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Expr( + StmtExpr { + range: 69..89, + value: Yield( + ExprYield { + range: 69..89, + value: Some( + Tuple( + ExprTuple { + range: 75..89, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 75..76, + value: Int( + 1, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 78..79, + value: Int( + 2, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 81..82, + value: Int( + 3, + ), + }, + ), + Starred( + ExprStarred { + range: 84..89, + value: Name( + ExprName { + range: 85..89, + id: Name("rest"), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + ctx: Load, + parenthesized: false, + }, + ), + ), + }, + ), + }, + ), + ], + }, + ), + FunctionDef( + StmtFunctionDef { + range: 90..127, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("h"), + range: 94..95, + }, + type_params: None, + parameters: Parameters { + range: 95..97, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Expr( + StmtExpr { + range: 99..127, + value: Yield( + ExprYield { + range: 99..127, + value: Some( + Tuple( + ExprTuple { + range: 105..127, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 105..106, + value: Int( + 1, + ), + }, + ), + Yield( + ExprYield { + range: 109..123, + value: Some( + Tuple( + ExprTuple { + range: 115..123, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 115..116, + value: Int( + 2, + ), + }, + ), + Starred( + ExprStarred { + range: 118..123, + value: Name( + ExprName { + range: 119..123, + id: Name("rest"), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + ctx: Load, + parenthesized: false, + }, + ), + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 126..127, + value: Int( + 3, + ), + }, + ), + ], + ctx: Load, + parenthesized: false, + }, + ), + ), + }, + ), + }, + ), + ], + }, + ), + ], + }, +) +``` +## Unsupported Syntax Errors + + | +1 | # parse_options: {"target-version": "3.7"} +2 | rest = (4, 5, 6) +3 | def g(): yield 1, 2, 3, *rest + | ^^^^^ Syntax Error: Cannot use iterable unpacking in yield expressions on Python 3.7 (syntax was added in Python 3.8) +4 | def h(): yield 1, (yield 2, *rest), 3 + | + + + | +2 | rest = (4, 5, 6) +3 | def g(): yield 1, 2, 3, *rest +4 | def h(): yield 1, (yield 2, *rest), 3 + | ^^^^^ Syntax Error: Cannot use iterable unpacking in yield expressions on Python 3.7 (syntax was added in Python 3.8) + | diff --git a/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_return_py37.py.snap b/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_return_py37.py.snap new file mode 100644 index 0000000000..a925e03d2e --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_return_py37.py.snap @@ -0,0 +1,138 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py37.py +--- +## AST + +``` +Module( + ModModule { + range: 0..93, + body: [ + Assign( + StmtAssign { + range: 43..59, + targets: [ + Name( + ExprName { + range: 43..47, + id: Name("rest"), + ctx: Store, + }, + ), + ], + value: Tuple( + ExprTuple { + range: 50..59, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 51..52, + value: Int( + 4, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 54..55, + value: Int( + 5, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 57..58, + value: Int( + 6, + ), + }, + ), + ], + ctx: Load, + parenthesized: true, + }, + ), + }, + ), + FunctionDef( + StmtFunctionDef { + range: 60..92, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("f"), + range: 64..65, + }, + type_params: None, + parameters: Parameters { + range: 65..67, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Return( + StmtReturn { + range: 69..92, + value: Some( + Tuple( + ExprTuple { + range: 76..92, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 77..78, + value: Int( + 1, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 80..81, + value: Int( + 2, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 83..84, + value: Int( + 3, + ), + }, + ), + Starred( + ExprStarred { + range: 86..91, + value: Name( + ExprName { + range: 87..91, + id: Name("rest"), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + ctx: Load, + parenthesized: true, + }, + ), + ), + }, + ), + ], + }, + ), + ], + }, +) +``` diff --git a/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_return_py38.py.snap b/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_return_py38.py.snap new file mode 100644 index 0000000000..d1fbfcc158 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_return_py38.py.snap @@ -0,0 +1,138 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/ok/iter_unpack_return_py38.py +--- +## AST + +``` +Module( + ModModule { + range: 0..91, + body: [ + Assign( + StmtAssign { + range: 43..59, + targets: [ + Name( + ExprName { + range: 43..47, + id: Name("rest"), + ctx: Store, + }, + ), + ], + value: Tuple( + ExprTuple { + range: 50..59, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 51..52, + value: Int( + 4, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 54..55, + value: Int( + 5, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 57..58, + value: Int( + 6, + ), + }, + ), + ], + ctx: Load, + parenthesized: true, + }, + ), + }, + ), + FunctionDef( + StmtFunctionDef { + range: 60..90, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("f"), + range: 64..65, + }, + type_params: None, + parameters: Parameters { + range: 65..67, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Return( + StmtReturn { + range: 69..90, + value: Some( + Tuple( + ExprTuple { + range: 76..90, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 76..77, + value: Int( + 1, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 79..80, + value: Int( + 2, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 82..83, + value: Int( + 3, + ), + }, + ), + Starred( + ExprStarred { + range: 85..90, + value: Name( + ExprName { + range: 86..90, + id: Name("rest"), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + ctx: Load, + parenthesized: false, + }, + ), + ), + }, + ), + ], + }, + ), + ], + }, +) +``` diff --git a/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_yield_py37.py.snap b/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_yield_py37.py.snap new file mode 100644 index 0000000000..e27bc0edc0 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_yield_py37.py.snap @@ -0,0 +1,143 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py37.py +--- +## AST + +``` +Module( + ModModule { + range: 0..92, + body: [ + Assign( + StmtAssign { + range: 43..59, + targets: [ + Name( + ExprName { + range: 43..47, + id: Name("rest"), + ctx: Store, + }, + ), + ], + value: Tuple( + ExprTuple { + range: 50..59, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 51..52, + value: Int( + 4, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 54..55, + value: Int( + 5, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 57..58, + value: Int( + 6, + ), + }, + ), + ], + ctx: Load, + parenthesized: true, + }, + ), + }, + ), + FunctionDef( + StmtFunctionDef { + range: 60..91, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("g"), + range: 64..65, + }, + type_params: None, + parameters: Parameters { + range: 65..67, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Expr( + StmtExpr { + range: 69..91, + value: Yield( + ExprYield { + range: 69..91, + value: Some( + Tuple( + ExprTuple { + range: 75..91, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 76..77, + value: Int( + 1, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 79..80, + value: Int( + 2, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 82..83, + value: Int( + 3, + ), + }, + ), + Starred( + ExprStarred { + range: 85..90, + value: Name( + ExprName { + range: 86..90, + id: Name("rest"), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + ctx: Load, + parenthesized: true, + }, + ), + ), + }, + ), + }, + ), + ], + }, + ), + ], + }, +) +``` diff --git a/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_yield_py38.py.snap b/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_yield_py38.py.snap new file mode 100644 index 0000000000..4aa3293a64 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/valid_syntax@iter_unpack_yield_py38.py.snap @@ -0,0 +1,240 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/ok/iter_unpack_yield_py38.py +--- +## AST + +``` +Module( + ModModule { + range: 0..128, + body: [ + Assign( + StmtAssign { + range: 43..59, + targets: [ + Name( + ExprName { + range: 43..47, + id: Name("rest"), + ctx: Store, + }, + ), + ], + value: Tuple( + ExprTuple { + range: 50..59, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 51..52, + value: Int( + 4, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 54..55, + value: Int( + 5, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 57..58, + value: Int( + 6, + ), + }, + ), + ], + ctx: Load, + parenthesized: true, + }, + ), + }, + ), + FunctionDef( + StmtFunctionDef { + range: 60..89, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("g"), + range: 64..65, + }, + type_params: None, + parameters: Parameters { + range: 65..67, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Expr( + StmtExpr { + range: 69..89, + value: Yield( + ExprYield { + range: 69..89, + value: Some( + Tuple( + ExprTuple { + range: 75..89, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 75..76, + value: Int( + 1, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 78..79, + value: Int( + 2, + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 81..82, + value: Int( + 3, + ), + }, + ), + Starred( + ExprStarred { + range: 84..89, + value: Name( + ExprName { + range: 85..89, + id: Name("rest"), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + ctx: Load, + parenthesized: false, + }, + ), + ), + }, + ), + }, + ), + ], + }, + ), + FunctionDef( + StmtFunctionDef { + range: 90..127, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("h"), + range: 94..95, + }, + type_params: None, + parameters: Parameters { + range: 95..97, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Expr( + StmtExpr { + range: 99..127, + value: Yield( + ExprYield { + range: 99..127, + value: Some( + Tuple( + ExprTuple { + range: 105..127, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 105..106, + value: Int( + 1, + ), + }, + ), + Yield( + ExprYield { + range: 109..123, + value: Some( + Tuple( + ExprTuple { + range: 115..123, + elts: [ + NumberLiteral( + ExprNumberLiteral { + range: 115..116, + value: Int( + 2, + ), + }, + ), + Starred( + ExprStarred { + range: 118..123, + value: Name( + ExprName { + range: 119..123, + id: Name("rest"), + ctx: Load, + }, + ), + ctx: Load, + }, + ), + ], + ctx: Load, + parenthesized: false, + }, + ), + ), + }, + ), + NumberLiteral( + ExprNumberLiteral { + range: 126..127, + value: Int( + 3, + ), + }, + ), + ], + ctx: Load, + parenthesized: false, + }, + ), + ), + }, + ), + }, + ), + ], + }, + ), + ], + }, +) +```