[syntax-errors] Fix multiple assignment for class keyword argument (#17184)

Summary
--

Fixes #17181. The cases being tested with multiple *keys* being equal
are actually a slightly different error, more like the error for
`MatchMapping` than like the other multiple assignment errors:

```pycon
>>> match x:
...     case Class(x=x, x=x): ...
...
  File "<python-input-249>", line 2
    case Class(x=x, x=x): ...
                      ^
SyntaxError: attribute name repeated in class pattern: x
>>> match x:
...     case {"x": 1, "x": 2}: ...
...
  File "<python-input-251>", line 2
    case {"x": 1, "x": 2}: ...
         ^^^^^^^^^^^^^^^^
SyntaxError: mapping pattern checks duplicate key ('x')
>>> match x:
...     case [x, x]: ...
...
  File "<python-input-252>", line 2
    case [x, x]: ...
             ^
SyntaxError: multiple assignments to name 'x' in pattern
```

This PR just stops the false positive reported in the issue, but I will
quickly follow it up with a new rule (or possibly combined with the
mapping rule) catching the repeated attributes separately.

Test Plan
--

New inline `test_ok` and updating the `test_err` cases to have duplicate
values instead of keys.
This commit is contained in:
Brent Westbrook 2025-04-03 17:32:39 -04:00 committed by GitHub
parent 8aa5686e63
commit 6a07dd227d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 132 additions and 48 deletions

View file

@ -391,18 +391,17 @@ Module(
PatternKeyword {
range: 331..334,
attr: Identifier {
id: Name("x"),
id: Name("y"),
range: 331..332,
},
pattern: MatchValue(
PatternMatchValue {
pattern: MatchAs(
PatternMatchAs {
range: 333..334,
value: NumberLiteral(
ExprNumberLiteral {
pattern: None,
name: Some(
Identifier {
id: Name("x"),
range: 333..334,
value: Int(
1,
),
},
),
},
@ -411,18 +410,17 @@ Module(
PatternKeyword {
range: 336..339,
attr: Identifier {
id: Name("x"),
id: Name("z"),
range: 336..337,
},
pattern: MatchValue(
PatternMatchValue {
pattern: MatchAs(
PatternMatchAs {
range: 338..339,
value: NumberLiteral(
ExprNumberLiteral {
pattern: None,
name: Some(
Identifier {
id: Name("x"),
range: 338..339,
value: Int(
2,
),
},
),
},
@ -518,18 +516,17 @@ Module(
PatternKeyword {
range: 398..401,
attr: Identifier {
id: Name("x"),
id: Name("y"),
range: 398..399,
},
pattern: MatchValue(
PatternMatchValue {
pattern: MatchAs(
PatternMatchAs {
range: 400..401,
value: NumberLiteral(
ExprNumberLiteral {
pattern: None,
name: Some(
Identifier {
id: Name("x"),
range: 400..401,
value: Int(
1,
),
},
),
},
@ -538,18 +535,17 @@ Module(
PatternKeyword {
range: 403..406,
attr: Identifier {
id: Name("x"),
id: Name("z"),
range: 403..404,
},
pattern: MatchValue(
PatternMatchValue {
pattern: MatchAs(
PatternMatchAs {
range: 405..406,
value: NumberLiteral(
ExprNumberLiteral {
pattern: None,
name: Some(
Identifier {
id: Name("x"),
range: 405..406,
value: Int(
2,
),
},
),
},
@ -681,7 +677,7 @@ Module(
6 | case {1: x, **x}: ... # MatchMapping duplicate in **rest
| ^ Syntax Error: multiple assignments to name `x` in pattern
7 | case Class(x, x): ... # MatchClass positional
8 | case Class(x=1, x=2): ... # MatchClass keyword
8 | case Class(y=x, z=x): ... # MatchClass keyword
|
@ -690,33 +686,33 @@ Module(
6 | case {1: x, **x}: ... # MatchMapping duplicate in **rest
7 | case Class(x, x): ... # MatchClass positional
| ^ Syntax Error: multiple assignments to name `x` in pattern
8 | case Class(x=1, x=2): ... # MatchClass keyword
9 | case [x] | {1: x} | Class(x=1, x=2): ... # MatchOr
8 | case Class(y=x, z=x): ... # MatchClass keyword
9 | case [x] | {1: x} | Class(y=x, z=x): ... # MatchOr
|
|
6 | case {1: x, **x}: ... # MatchMapping duplicate in **rest
7 | case Class(x, x): ... # MatchClass positional
8 | case Class(x=1, x=2): ... # MatchClass keyword
| ^ Syntax Error: multiple assignments to name `x` in pattern
9 | case [x] | {1: x} | Class(x=1, x=2): ... # MatchOr
8 | case Class(y=x, z=x): ... # MatchClass keyword
| ^ Syntax Error: multiple assignments to name `x` in pattern
9 | case [x] | {1: x} | Class(y=x, z=x): ... # MatchOr
10 | case x as x: ... # MatchAs
|
|
7 | case Class(x, x): ... # MatchClass positional
8 | case Class(x=1, x=2): ... # MatchClass keyword
9 | case [x] | {1: x} | Class(x=1, x=2): ... # MatchOr
| ^ Syntax Error: multiple assignments to name `x` in pattern
8 | case Class(y=x, z=x): ... # MatchClass keyword
9 | case [x] | {1: x} | Class(y=x, z=x): ... # MatchOr
| ^ Syntax Error: multiple assignments to name `x` in pattern
10 | case x as x: ... # MatchAs
|
|
8 | case Class(x=1, x=2): ... # MatchClass keyword
9 | case [x] | {1: x} | Class(x=1, x=2): ... # MatchOr
8 | case Class(y=x, z=x): ... # MatchClass keyword
9 | case [x] | {1: x} | Class(y=x, z=x): ... # MatchOr
10 | case x as x: ... # MatchAs
| ^ Syntax Error: multiple assignments to name `x` in pattern
|

View file

@ -0,0 +1,83 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/ok/class_keyword_in_case_pattern.py
---
## AST
```
Module(
ModModule {
range: 0..34,
body: [
Match(
StmtMatch {
range: 0..33,
subject: NumberLiteral(
ExprNumberLiteral {
range: 6..7,
value: Int(
2,
),
},
),
cases: [
MatchCase {
range: 13..33,
pattern: MatchClass(
PatternMatchClass {
range: 18..28,
cls: Name(
ExprName {
range: 18..23,
id: Name("Class"),
ctx: Load,
},
),
arguments: PatternArguments {
range: 23..28,
patterns: [],
keywords: [
PatternKeyword {
range: 24..27,
attr: Identifier {
id: Name("x"),
range: 24..25,
},
pattern: MatchAs(
PatternMatchAs {
range: 26..27,
pattern: None,
name: Some(
Identifier {
id: Name("x"),
range: 26..27,
},
),
},
),
},
],
},
},
),
guard: None,
body: [
Expr(
StmtExpr {
range: 30..33,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 30..33,
},
),
},
),
],
},
],
},
),
],
},
)
```