Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
case [[x] | [x]] | x: ...
match 42:
case [[x | x] | [x]] | x: ...
match 42:
case ast.Subscript(n, ast.Constant() | ast.Slice()) | ast.Attribute(n): ...
4 changes: 3 additions & 1 deletion crates/ruff_python_parser/src/semantic_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1868,14 +1868,16 @@ impl<'a, Ctx: SemanticSyntaxContext> MatchPatternVisitor<'a, Ctx> {
// case [[x] | [x]] | x: ...
// match 42:
// case [[x | x] | [x]] | x: ...
// match 42:
// case ast.Subscript(n, ast.Constant() | ast.Slice()) | ast.Attribute(n): ...
SemanticSyntaxChecker::add_error(
self.ctx,
SemanticSyntaxErrorKind::DifferentMatchPatternBindings,
*range,
);
break;
}
self.names = visitor.names;
self.names.extend(visitor.names);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you say a bit more about the fix. My understanding (and why I recommended it) was that using union or intersection would only matter for the error recovery case. But it seems that using union over intersection is important for valid cases too or are we only fixing a symptom here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the union is correct because there can be existing patterns in self.names before visiting nested alternative patterns. We need to compare all of the names we visit in that arm to all of the names we visit in the next arm, not just the set of names visited in the last, innermost alternative pattern.

That's what's happening in this case because we first visit the outer class pattern, which captures n, then we were overwriting that n with the empty set when visiting the Constant | Slice part of the pattern. Instead we need the union of n and the empty set to compare to the Attribute(n) branch.

match 42:
    case ast.Subscript(n, ast.Constant() | ast.Slice()) 
       | ast.Attribute(n): ...

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ input_file: crates/ruff_python_parser/resources/inline/ok/nested_alternative_pat
Module(
ModModule {
node_index: NodeIndex(None),
range: 0..181,
range: 0..271,
body: [
Match(
StmtMatch {
Expand Down Expand Up @@ -489,6 +489,216 @@ Module(
],
},
),
Match(
StmtMatch {
node_index: NodeIndex(None),
range: 181..270,
subject: NumberLiteral(
ExprNumberLiteral {
node_index: NodeIndex(None),
range: 187..189,
value: Int(
42,
),
},
),
cases: [
MatchCase {
range: 195..270,
node_index: NodeIndex(None),
pattern: MatchOr(
PatternMatchOr {
node_index: NodeIndex(None),
range: 200..265,
patterns: [
MatchClass(
PatternMatchClass {
node_index: NodeIndex(None),
range: 200..246,
cls: Attribute(
ExprAttribute {
node_index: NodeIndex(None),
range: 200..213,
value: Name(
ExprName {
node_index: NodeIndex(None),
range: 200..203,
id: Name("ast"),
ctx: Load,
},
),
attr: Identifier {
id: Name("Subscript"),
range: 204..213,
node_index: NodeIndex(None),
},
ctx: Load,
},
),
arguments: PatternArguments {
range: 213..246,
node_index: NodeIndex(None),
patterns: [
MatchAs(
PatternMatchAs {
node_index: NodeIndex(None),
range: 214..215,
pattern: None,
name: Some(
Identifier {
id: Name("n"),
range: 214..215,
node_index: NodeIndex(None),
},
),
},
),
MatchOr(
PatternMatchOr {
node_index: NodeIndex(None),
range: 217..245,
patterns: [
MatchClass(
PatternMatchClass {
node_index: NodeIndex(None),
range: 217..231,
cls: Attribute(
ExprAttribute {
node_index: NodeIndex(None),
range: 217..229,
value: Name(
ExprName {
node_index: NodeIndex(None),
range: 217..220,
id: Name("ast"),
ctx: Load,
},
),
attr: Identifier {
id: Name("Constant"),
range: 221..229,
node_index: NodeIndex(None),
},
ctx: Load,
},
),
arguments: PatternArguments {
range: 229..231,
node_index: NodeIndex(None),
patterns: [],
keywords: [],
},
},
),
MatchClass(
PatternMatchClass {
node_index: NodeIndex(None),
range: 234..245,
cls: Attribute(
ExprAttribute {
node_index: NodeIndex(None),
range: 234..243,
value: Name(
ExprName {
node_index: NodeIndex(None),
range: 234..237,
id: Name("ast"),
ctx: Load,
},
),
attr: Identifier {
id: Name("Slice"),
range: 238..243,
node_index: NodeIndex(None),
},
ctx: Load,
},
),
arguments: PatternArguments {
range: 243..245,
node_index: NodeIndex(None),
patterns: [],
keywords: [],
},
},
),
],
},
),
],
keywords: [],
},
},
),
MatchClass(
PatternMatchClass {
node_index: NodeIndex(None),
range: 249..265,
cls: Attribute(
ExprAttribute {
node_index: NodeIndex(None),
range: 249..262,
value: Name(
ExprName {
node_index: NodeIndex(None),
range: 249..252,
id: Name("ast"),
ctx: Load,
},
),
attr: Identifier {
id: Name("Attribute"),
range: 253..262,
node_index: NodeIndex(None),
},
ctx: Load,
},
),
arguments: PatternArguments {
range: 262..265,
node_index: NodeIndex(None),
patterns: [
MatchAs(
PatternMatchAs {
node_index: NodeIndex(None),
range: 263..264,
pattern: None,
name: Some(
Identifier {
id: Name("n"),
range: 263..264,
node_index: NodeIndex(None),
},
),
},
),
],
keywords: [],
},
},
),
],
},
),
guard: None,
body: [
Expr(
StmtExpr {
node_index: NodeIndex(None),
range: 267..270,
value: EllipsisLiteral(
ExprEllipsisLiteral {
node_index: NodeIndex(None),
range: 267..270,
},
),
},
),
],
},
],
},
),
],
},
)
Expand Down