mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00
[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:
parent
8aa5686e63
commit
6a07dd227d
5 changed files with 132 additions and 48 deletions
|
@ -5,6 +5,6 @@ match 2:
|
|||
case {1: x, 2: x}: ... # MatchMapping duplicate pattern
|
||||
case {1: x, **x}: ... # MatchMapping duplicate in **rest
|
||||
case Class(x, x): ... # MatchClass positional
|
||||
case Class(x=1, x=2): ... # MatchClass keyword
|
||||
case [x] | {1: x} | Class(x=1, x=2): ... # MatchOr
|
||||
case Class(y=x, z=x): ... # MatchClass keyword
|
||||
case [x] | {1: x} | Class(y=x, z=x): ... # MatchOr
|
||||
case x as x: ... # MatchAs
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
match 2:
|
||||
case Class(x=x): ...
|
|
@ -794,6 +794,10 @@ struct MultipleCaseAssignmentVisitor<'a, Ctx> {
|
|||
|
||||
impl<'a, Ctx: SemanticSyntaxContext> MultipleCaseAssignmentVisitor<'a, Ctx> {
|
||||
fn visit_pattern(&mut self, pattern: &'a Pattern) {
|
||||
// test_ok class_keyword_in_case_pattern
|
||||
// match 2:
|
||||
// case Class(x=x): ...
|
||||
|
||||
// test_err multiple_assignment_in_case_pattern
|
||||
// match 2:
|
||||
// case [y, z, y]: ... # MatchSequence
|
||||
|
@ -802,8 +806,8 @@ impl<'a, Ctx: SemanticSyntaxContext> MultipleCaseAssignmentVisitor<'a, Ctx> {
|
|||
// case {1: x, 2: x}: ... # MatchMapping duplicate pattern
|
||||
// case {1: x, **x}: ... # MatchMapping duplicate in **rest
|
||||
// case Class(x, x): ... # MatchClass positional
|
||||
// case Class(x=1, x=2): ... # MatchClass keyword
|
||||
// case [x] | {1: x} | Class(x=1, x=2): ... # MatchOr
|
||||
// case Class(y=x, z=x): ... # MatchClass keyword
|
||||
// case [x] | {1: x} | Class(y=x, z=x): ... # MatchOr
|
||||
// case x as x: ... # MatchAs
|
||||
match pattern {
|
||||
Pattern::MatchValue(_) | Pattern::MatchSingleton(_) => {}
|
||||
|
@ -830,7 +834,6 @@ impl<'a, Ctx: SemanticSyntaxContext> MultipleCaseAssignmentVisitor<'a, Ctx> {
|
|||
self.visit_pattern(pattern);
|
||||
}
|
||||
for keyword in &arguments.keywords {
|
||||
self.insert(&keyword.attr);
|
||||
self.visit_pattern(&keyword.pattern);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
```
|
Loading…
Add table
Add a link
Reference in a new issue