Skip to content

Commit 3a32e56

Browse files
authored
[syntax-errors] Unparenthesized assignment expressions in sets and indexes (#16404)
## Summary This PR detects unparenthesized assignment expressions used in set literals and comprehensions and in sequence indexes. The link to the release notes in #6591 just has this entry: > * Assignment expressions can now be used unparenthesized within set literals and set comprehensions, as well as in sequence indexes (but not slices). with no other information, so hopefully the test cases I came up with cover all of the changes. I also tested these out in the Python REPL and they actually worked in Python 3.9 too. I'm guessing this may be another case that was "formally made part of the language spec in Python 3.10, but usable -- and commonly used -- in Python >=3.9" as @AlexWaygood added to the body of #6591 for context managers. So we may want to change the version cutoff, but I've gone along with the release notes for now. ## Test Plan New inline parser tests and linter CLI tests.
1 parent b9d7c36 commit 3a32e56

20 files changed

+1220
-9
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# even after 3.9, an unparenthesized named expression is not allowed in a slice
2+
lst[x:=1:-1]
3+
lst[1:x:=1]
4+
lst[1:3:x:=1]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# parse_options: {"target-version": "3.8"}
2+
# before 3.9, only emit the parse error, not the unsupported syntax error
3+
lst[x:=1:-1]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# parse_options: {"target-version": "3.8"}
2+
lst[x:=1]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# parse_options: {"target-version": "3.8"}
2+
{last := x for x in range(3)}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# parse_options: {"target-version": "3.8"}
2+
{x := 1, 2, 3}
3+
{1, x := 2, 3}
4+
{1, 2, x := 3}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# parse_options: {"target-version": "3.8"}
2+
lst[(x:=1)]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# parse_options: {"target-version": "3.8"}
2+
{(x := 1), 2, 3}
3+
{(last := x) for x in range(3)}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# parse_options: {"target-version": "3.9"}
2+
lst[x:=1]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# parse_options: {"target-version": "3.9"}
2+
{x := 1, 2, 3}
3+
{last := x for x in range(3)}

crates/ruff_python_parser/src/error.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,11 +452,55 @@ pub enum StarTupleKind {
452452
Yield,
453453
}
454454

455+
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
456+
pub enum UnparenthesizedNamedExprKind {
457+
SequenceIndex,
458+
SetLiteral,
459+
SetComprehension,
460+
}
461+
455462
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
456463
pub enum UnsupportedSyntaxErrorKind {
457464
Match,
458465
Walrus,
459466
ExceptStar,
467+
/// Represents the use of an unparenthesized named expression (`:=`) in a set literal, set
468+
/// comprehension, or sequence index before Python 3.10.
469+
///
470+
/// ## Examples
471+
///
472+
/// These are allowed on Python 3.10:
473+
///
474+
/// ```python
475+
/// {x := 1, 2, 3} # set literal
476+
/// {last := x for x in range(3)} # set comprehension
477+
/// lst[x := 1] # sequence index
478+
/// ```
479+
///
480+
/// But on Python 3.9 the named expression needs to be parenthesized:
481+
///
482+
/// ```python
483+
/// {(x := 1), 2, 3} # set literal
484+
/// {(last := x) for x in range(3)} # set comprehension
485+
/// lst[(x := 1)] # sequence index
486+
/// ```
487+
///
488+
/// However, unparenthesized named expressions are never allowed in slices:
489+
///
490+
/// ```python
491+
/// lst[x:=1:-1] # syntax error
492+
/// lst[1:x:=1] # syntax error
493+
/// lst[1:3:x:=1] # syntax error
494+
///
495+
/// lst[(x:=1):-1] # ok
496+
/// lst[1:(x:=1)] # ok
497+
/// lst[1:3:(x:=1)] # ok
498+
/// ```
499+
///
500+
/// ## References
501+
///
502+
/// - [Python 3.10 Other Language Changes](https://docs.python.org/3/whatsnew/3.10.html#other-language-changes)
503+
UnparenthesizedNamedExpr(UnparenthesizedNamedExprKind),
460504

461505
/// Represents the use of a parenthesized keyword argument name after Python 3.8.
462506
///
@@ -706,6 +750,15 @@ impl Display for UnsupportedSyntaxError {
706750
UnsupportedSyntaxErrorKind::Match => "Cannot use `match` statement",
707751
UnsupportedSyntaxErrorKind::Walrus => "Cannot use named assignment expression (`:=`)",
708752
UnsupportedSyntaxErrorKind::ExceptStar => "Cannot use `except*`",
753+
UnsupportedSyntaxErrorKind::UnparenthesizedNamedExpr(
754+
UnparenthesizedNamedExprKind::SequenceIndex,
755+
) => "Cannot use unparenthesized assignment expression in a sequence index",
756+
UnsupportedSyntaxErrorKind::UnparenthesizedNamedExpr(
757+
UnparenthesizedNamedExprKind::SetLiteral,
758+
) => "Cannot use unparenthesized assignment expression as an element in a set literal",
759+
UnsupportedSyntaxErrorKind::UnparenthesizedNamedExpr(
760+
UnparenthesizedNamedExprKind::SetComprehension,
761+
) => "Cannot use unparenthesized assignment expression as an element in a set comprehension",
709762
UnsupportedSyntaxErrorKind::ParenthesizedKeywordArgumentName => {
710763
"Cannot use parenthesized keyword argument name"
711764
}
@@ -765,6 +818,9 @@ impl UnsupportedSyntaxErrorKind {
765818
UnsupportedSyntaxErrorKind::Match => Change::Added(PythonVersion::PY310),
766819
UnsupportedSyntaxErrorKind::Walrus => Change::Added(PythonVersion::PY38),
767820
UnsupportedSyntaxErrorKind::ExceptStar => Change::Added(PythonVersion::PY311),
821+
UnsupportedSyntaxErrorKind::UnparenthesizedNamedExpr(_) => {
822+
Change::Added(PythonVersion::PY39)
823+
}
768824
UnsupportedSyntaxErrorKind::StarTuple(_) => Change::Added(PythonVersion::PY38),
769825
UnsupportedSyntaxErrorKind::RelaxedDecorator => Change::Added(PythonVersion::PY39),
770826
UnsupportedSyntaxErrorKind::PositionalOnlyParameter => {

0 commit comments

Comments
 (0)