Introduce AST nodes for PatternMatchClass arguments (#6881)

## Summary

This PR introduces two new AST nodes to improve the representation of
`PatternMatchClass`. As a reminder, `PatternMatchClass` looks like this:

```python
case Point2D(0, 0, x=1, y=2):
  ...
```

Historically, this was represented as a vector of patterns (for the `0,
0` portion) and parallel vectors of keyword names (for `x` and `y`) and
values (for `1` and `2`). This introduces a bunch of challenges for the
formatter, but importantly, it's also really different from how we
represent similar nodes, like arguments (`func(0, 0, x=1, y=2)`) or
parameters (`def func(x, y)`).

So, firstly, we now use a single node (`PatternArguments`) for the
entire parenthesized region, making it much more consistent with our
other nodes. So, above, `PatternArguments` would be `(0, 0, x=1, y=2)`.

Secondly, we now have a `PatternKeyword` node for `x=1` and `y=2`. This
is much more similar to the how `Keyword` is represented within
`Arguments` for call expressions.

Closes https://github.com/astral-sh/ruff/issues/6866.

Closes https://github.com/astral-sh/ruff/issues/6880.
This commit is contained in:
Charlie Marsh 2023-08-26 10:45:44 -04:00 committed by GitHub
parent ed1b4122d0
commit 15b73bdb8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 25299 additions and 25824 deletions

View file

@ -790,97 +790,62 @@ MappingPattern: ast::Pattern = {
},
}
MatchKeywordEntry: (ast::Identifier, ast::Pattern) = {
<k:Identifier> "=" <v:Pattern> => (k, v),
MatchKeywordEntry: ast::PatternKeyword = {
<location:@L> <attr:Identifier> "=" <pattern:Pattern> <end_location:@R> => ast::PatternKeyword {
attr,
pattern,
range: (location..end_location).into()
},
};
ClassPattern: ast::Pattern = {
<location:@L> <e:MatchName> "(" <patterns: OneOrMore<Pattern>> "," <kwds:OneOrMore<MatchKeywordEntry>> ","? ")" <end_location:@R> => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
<location:@L> <cls:MatchName> <arguments:PatternArguments> <end_location:@R> => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns,
kwd_attrs,
kwd_patterns,
cls: Box::new(cls),
arguments,
range: (location..end_location).into()
}.into()
},
<location:@L> <e:MatchName> "(" <patterns: OneOrMore<Pattern>> ","? ")" <end_location:@R> => {
<location:@L> <cls:MatchNameOrAttr> <arguments:PatternArguments> <end_location:@R> => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns,
kwd_attrs: vec![],
kwd_patterns: vec![],
range: (location..end_location).into()
}.into()
},
<location:@L> <e:MatchName> "(" <kwds:OneOrMore<MatchKeywordEntry>> ","? ")" <end_location:@R> => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternMatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs,
kwd_patterns,
range: (location..end_location).into()
}.into()
},
<location:@L> <e:MatchName> "(" ")" <end_location:@R> => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs: vec![],
kwd_patterns: vec![],
range: (location..end_location).into()
}.into()
},
<location:@L> <e:MatchNameOrAttr> "(" <patterns: OneOrMore<Pattern>> "," <kwds:OneOrMore<MatchKeywordEntry>> ","? ")" <end_location:@R> => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternMatchClass {
cls: Box::new(e),
patterns,
kwd_attrs,
kwd_patterns,
range: (location..end_location).into()
}.into()
},
<location:@L> <e:MatchNameOrAttr> "(" <patterns: OneOrMore<Pattern>> ","? ")" <end_location:@R> => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns,
kwd_attrs: vec![],
kwd_patterns: vec![],
range: (location..end_location).into()
}.into()
},
<location:@L> <e:MatchNameOrAttr> "(" <kwds:OneOrMore<MatchKeywordEntry>> ","? ")" <end_location:@R> => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternMatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs,
kwd_patterns,
range: (location..end_location).into()
}.into()
},
<location:@L> <e:MatchNameOrAttr> "(" ")" <end_location:@R> => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs: vec![],
kwd_patterns: vec![],
cls: Box::new(cls),
arguments,
range: (location..end_location).into()
}.into()
},
}
PatternArguments: ast::PatternArguments = {
<location:@L> "(" <patterns: OneOrMore<Pattern>> "," <keywords:OneOrMore<MatchKeywordEntry>> ","? ")" <end_location:@R> => {
ast::PatternArguments {
patterns,
keywords,
range: (location..end_location).into()
}
},
<location:@L> "(" <patterns: OneOrMore<Pattern>> ","? ")" <end_location:@R> => {
ast::PatternArguments {
patterns,
keywords: vec![],
range: (location..end_location).into()
}
},
<location:@L> "(" <keywords:OneOrMore<MatchKeywordEntry>> ","? ")" <end_location:@R> => {
ast::PatternArguments {
patterns: vec![],
keywords,
range: (location..end_location).into()
}
},
<location:@L> "(" ")" <end_location:@R> => {
ast::PatternArguments {
patterns: vec![],
keywords: vec![],
range: (location..end_location).into()
}
},
};
IfStatement: ast::Stmt = {
<location:@L> "if" <test:NamedExpressionTest> ":" <body:Suite> <s2:(<@L> "elif" <NamedExpressionTest> ":" <Suite>)*> <s3:(<@L> "else" ":" <Suite>)?> => {
let elif_else_clauses: Vec<_> = s2.into_iter().map(|(start, test, body)| ast::ElifElseClause {

File diff suppressed because it is too large Load diff

View file

@ -168,9 +168,11 @@ expression: parse_ast
ctx: Load,
},
),
patterns: [],
kwd_attrs: [],
kwd_patterns: [],
arguments: PatternArguments {
range: 130..132,
patterns: [],
keywords: [],
},
},
),
MatchSingleton(

View file

@ -90,22 +90,24 @@ expression: parse_ast
ctx: Load,
},
),
patterns: [
MatchAs(
PatternMatchAs {
range: 150..151,
pattern: None,
name: Some(
Identifier {
id: "z",
range: 150..151,
},
),
},
),
],
kwd_attrs: [],
kwd_patterns: [],
arguments: PatternArguments {
range: 149..152,
patterns: [
MatchAs(
PatternMatchAs {
range: 150..151,
pattern: None,
name: Some(
Identifier {
id: "z",
range: 150..151,
},
),
},
),
],
keywords: [],
},
},
),
guard: None,