mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:56 +00:00
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:
parent
ed1b4122d0
commit
15b73bdb8a
19 changed files with 25299 additions and 25824 deletions
|
@ -153,6 +153,36 @@ impl<'a> From<&'a ast::WithItem> for ComparableWithItem<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ComparablePatternArguments<'a> {
|
||||
patterns: Vec<ComparablePattern<'a>>,
|
||||
keywords: Vec<ComparablePatternKeyword<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::PatternArguments> for ComparablePatternArguments<'a> {
|
||||
fn from(parameters: &'a ast::PatternArguments) -> Self {
|
||||
Self {
|
||||
patterns: parameters.patterns.iter().map(Into::into).collect(),
|
||||
keywords: parameters.keywords.iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ComparablePatternKeyword<'a> {
|
||||
attr: &'a str,
|
||||
pattern: ComparablePattern<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::PatternKeyword> for ComparablePatternKeyword<'a> {
|
||||
fn from(keyword: &'a ast::PatternKeyword) -> Self {
|
||||
Self {
|
||||
attr: keyword.attr.as_str(),
|
||||
pattern: (&keyword.pattern).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct PatternMatchValue<'a> {
|
||||
value: ComparableExpr<'a>,
|
||||
|
@ -178,9 +208,7 @@ pub struct PatternMatchMapping<'a> {
|
|||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct PatternMatchClass<'a> {
|
||||
cls: ComparableExpr<'a>,
|
||||
patterns: Vec<ComparablePattern<'a>>,
|
||||
kwd_attrs: Vec<&'a str>,
|
||||
kwd_patterns: Vec<ComparablePattern<'a>>,
|
||||
arguments: ComparablePatternArguments<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
|
@ -240,18 +268,12 @@ impl<'a> From<&'a ast::Pattern> for ComparablePattern<'a> {
|
|||
patterns: patterns.iter().map(Into::into).collect(),
|
||||
rest: rest.as_deref(),
|
||||
}),
|
||||
ast::Pattern::MatchClass(ast::PatternMatchClass {
|
||||
cls,
|
||||
patterns,
|
||||
kwd_attrs,
|
||||
kwd_patterns,
|
||||
..
|
||||
}) => Self::MatchClass(PatternMatchClass {
|
||||
ast::Pattern::MatchClass(ast::PatternMatchClass { cls, arguments, .. }) => {
|
||||
Self::MatchClass(PatternMatchClass {
|
||||
cls: cls.into(),
|
||||
patterns: patterns.iter().map(Into::into).collect(),
|
||||
kwd_attrs: kwd_attrs.iter().map(ast::Identifier::as_str).collect(),
|
||||
kwd_patterns: kwd_patterns.iter().map(Into::into).collect(),
|
||||
}),
|
||||
arguments: arguments.into(),
|
||||
})
|
||||
}
|
||||
ast::Pattern::MatchStar(ast::PatternMatchStar { name, .. }) => {
|
||||
Self::MatchStar(PatternMatchStar {
|
||||
name: name.as_deref(),
|
||||
|
|
|
@ -279,19 +279,16 @@ where
|
|||
.iter()
|
||||
.any(|pattern| any_over_pattern(pattern, func))
|
||||
}
|
||||
Pattern::MatchClass(ast::PatternMatchClass {
|
||||
cls,
|
||||
patterns,
|
||||
kwd_patterns,
|
||||
..
|
||||
}) => {
|
||||
Pattern::MatchClass(ast::PatternMatchClass { cls, arguments, .. }) => {
|
||||
any_over_expr(cls, func)
|
||||
|| patterns
|
||||
|| arguments
|
||||
.patterns
|
||||
.iter()
|
||||
.any(|pattern| any_over_pattern(pattern, func))
|
||||
|| kwd_patterns
|
||||
|| arguments
|
||||
.keywords
|
||||
.iter()
|
||||
.any(|pattern| any_over_pattern(pattern, func))
|
||||
.any(|keyword| any_over_pattern(&keyword.pattern, func))
|
||||
}
|
||||
Pattern::MatchStar(_) => false,
|
||||
Pattern::MatchAs(ast::PatternMatchAs { pattern, .. }) => pattern
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::visitor::preorder::PreorderVisitor;
|
||||
use crate::{
|
||||
self as ast, Alias, Arguments, Comprehension, Decorator, ExceptHandler, Expr, Keyword,
|
||||
MatchCase, Mod, Parameter, ParameterWithDefault, Parameters, Pattern, Ranged, Stmt, TypeParam,
|
||||
TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, TypeParams, WithItem,
|
||||
MatchCase, Mod, Parameter, ParameterWithDefault, Parameters, Pattern, PatternArguments,
|
||||
PatternKeyword, Ranged, Stmt, TypeParam, TypeParamParamSpec, TypeParamTypeVar,
|
||||
TypeParamTypeVarTuple, TypeParams, WithItem,
|
||||
};
|
||||
use ruff_text_size::TextRange;
|
||||
use std::ptr::NonNull;
|
||||
|
@ -90,6 +91,8 @@ pub enum AnyNode {
|
|||
PatternMatchStar(ast::PatternMatchStar),
|
||||
PatternMatchAs(ast::PatternMatchAs),
|
||||
PatternMatchOr(ast::PatternMatchOr),
|
||||
PatternArguments(PatternArguments),
|
||||
PatternKeyword(PatternKeyword),
|
||||
Comprehension(Comprehension),
|
||||
Arguments(Arguments),
|
||||
Parameters(Parameters),
|
||||
|
@ -175,6 +178,8 @@ impl AnyNode {
|
|||
| AnyNode::PatternMatchStar(_)
|
||||
| AnyNode::PatternMatchAs(_)
|
||||
| AnyNode::PatternMatchOr(_)
|
||||
| AnyNode::PatternArguments(_)
|
||||
| AnyNode::PatternKeyword(_)
|
||||
| AnyNode::Comprehension(_)
|
||||
| AnyNode::Arguments(_)
|
||||
| AnyNode::Parameters(_)
|
||||
|
@ -260,6 +265,8 @@ impl AnyNode {
|
|||
| AnyNode::PatternMatchStar(_)
|
||||
| AnyNode::PatternMatchAs(_)
|
||||
| AnyNode::PatternMatchOr(_)
|
||||
| AnyNode::PatternArguments(_)
|
||||
| AnyNode::PatternKeyword(_)
|
||||
| AnyNode::Comprehension(_)
|
||||
| AnyNode::Arguments(_)
|
||||
| AnyNode::Parameters(_)
|
||||
|
@ -345,6 +352,8 @@ impl AnyNode {
|
|||
| AnyNode::PatternMatchStar(_)
|
||||
| AnyNode::PatternMatchAs(_)
|
||||
| AnyNode::PatternMatchOr(_)
|
||||
| AnyNode::PatternArguments(_)
|
||||
| AnyNode::PatternKeyword(_)
|
||||
| AnyNode::Comprehension(_)
|
||||
| AnyNode::Arguments(_)
|
||||
| AnyNode::Parameters(_)
|
||||
|
@ -430,6 +439,8 @@ impl AnyNode {
|
|||
| AnyNode::ExprSlice(_)
|
||||
| AnyNode::ExprIpyEscapeCommand(_)
|
||||
| AnyNode::ExceptHandlerExceptHandler(_)
|
||||
| AnyNode::PatternArguments(_)
|
||||
| AnyNode::PatternKeyword(_)
|
||||
| AnyNode::Comprehension(_)
|
||||
| AnyNode::Arguments(_)
|
||||
| AnyNode::Parameters(_)
|
||||
|
@ -515,6 +526,8 @@ impl AnyNode {
|
|||
| AnyNode::PatternMatchStar(_)
|
||||
| AnyNode::PatternMatchAs(_)
|
||||
| AnyNode::PatternMatchOr(_)
|
||||
| AnyNode::PatternArguments(_)
|
||||
| AnyNode::PatternKeyword(_)
|
||||
| AnyNode::Comprehension(_)
|
||||
| AnyNode::Arguments(_)
|
||||
| AnyNode::Parameters(_)
|
||||
|
@ -619,6 +632,8 @@ impl AnyNode {
|
|||
Self::PatternMatchStar(node) => AnyNodeRef::PatternMatchStar(node),
|
||||
Self::PatternMatchAs(node) => AnyNodeRef::PatternMatchAs(node),
|
||||
Self::PatternMatchOr(node) => AnyNodeRef::PatternMatchOr(node),
|
||||
Self::PatternArguments(node) => AnyNodeRef::PatternArguments(node),
|
||||
Self::PatternKeyword(node) => AnyNodeRef::PatternKeyword(node),
|
||||
Self::Comprehension(node) => AnyNodeRef::Comprehension(node),
|
||||
Self::Arguments(node) => AnyNodeRef::Arguments(node),
|
||||
Self::Parameters(node) => AnyNodeRef::Parameters(node),
|
||||
|
@ -3247,19 +3262,11 @@ impl AstNode for ast::PatternMatchClass {
|
|||
{
|
||||
let ast::PatternMatchClass {
|
||||
cls,
|
||||
patterns,
|
||||
kwd_attrs: _,
|
||||
kwd_patterns,
|
||||
arguments: parameters,
|
||||
range: _,
|
||||
} = self;
|
||||
visitor.visit_expr(cls);
|
||||
for pattern in patterns {
|
||||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
|
||||
for pattern in kwd_patterns {
|
||||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
visitor.visit_pattern_arguments(parameters);
|
||||
}
|
||||
}
|
||||
impl AstNode for ast::PatternMatchStar {
|
||||
|
@ -3378,6 +3385,94 @@ impl AstNode for ast::PatternMatchOr {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl AstNode for PatternArguments {
|
||||
fn cast(kind: AnyNode) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if let AnyNode::PatternArguments(node) = kind {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_ref(kind: AnyNodeRef) -> Option<&Self> {
|
||||
if let AnyNodeRef::PatternArguments(node) = kind {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any_node_ref(&self) -> AnyNodeRef {
|
||||
AnyNodeRef::from(self)
|
||||
}
|
||||
|
||||
fn into_any_node(self) -> AnyNode {
|
||||
AnyNode::from(self)
|
||||
}
|
||||
|
||||
fn visit_preorder<'a, V>(&'a self, visitor: &mut V)
|
||||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
{
|
||||
let PatternArguments {
|
||||
range: _,
|
||||
patterns,
|
||||
keywords,
|
||||
} = self;
|
||||
|
||||
for pattern in patterns {
|
||||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
|
||||
for keyword in keywords {
|
||||
visitor.visit_pattern_keyword(keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl AstNode for PatternKeyword {
|
||||
fn cast(kind: AnyNode) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if let AnyNode::PatternKeyword(node) = kind {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_ref(kind: AnyNodeRef) -> Option<&Self> {
|
||||
if let AnyNodeRef::PatternKeyword(node) = kind {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any_node_ref(&self) -> AnyNodeRef {
|
||||
AnyNodeRef::from(self)
|
||||
}
|
||||
|
||||
fn into_any_node(self) -> AnyNode {
|
||||
AnyNode::from(self)
|
||||
}
|
||||
|
||||
fn visit_preorder<'a, V>(&'a self, visitor: &mut V)
|
||||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
{
|
||||
let PatternKeyword {
|
||||
range: _,
|
||||
attr: _,
|
||||
pattern,
|
||||
} = self;
|
||||
|
||||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Comprehension {
|
||||
fn cast(kind: AnyNode) -> Option<Self>
|
||||
|
@ -4475,6 +4570,18 @@ impl From<ast::PatternMatchOr> for AnyNode {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<PatternArguments> for AnyNode {
|
||||
fn from(node: PatternArguments) -> Self {
|
||||
AnyNode::PatternArguments(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PatternKeyword> for AnyNode {
|
||||
fn from(node: PatternKeyword) -> Self {
|
||||
AnyNode::PatternKeyword(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Comprehension> for AnyNode {
|
||||
fn from(node: Comprehension) -> Self {
|
||||
AnyNode::Comprehension(node)
|
||||
|
@ -4615,6 +4722,8 @@ impl Ranged for AnyNode {
|
|||
AnyNode::PatternMatchStar(node) => node.range(),
|
||||
AnyNode::PatternMatchAs(node) => node.range(),
|
||||
AnyNode::PatternMatchOr(node) => node.range(),
|
||||
AnyNode::PatternArguments(node) => node.range(),
|
||||
AnyNode::PatternKeyword(node) => node.range(),
|
||||
AnyNode::Comprehension(node) => node.range(),
|
||||
AnyNode::Arguments(node) => node.range(),
|
||||
AnyNode::Parameters(node) => node.range(),
|
||||
|
@ -4700,6 +4809,8 @@ pub enum AnyNodeRef<'a> {
|
|||
PatternMatchStar(&'a ast::PatternMatchStar),
|
||||
PatternMatchAs(&'a ast::PatternMatchAs),
|
||||
PatternMatchOr(&'a ast::PatternMatchOr),
|
||||
PatternArguments(&'a ast::PatternArguments),
|
||||
PatternKeyword(&'a ast::PatternKeyword),
|
||||
Comprehension(&'a Comprehension),
|
||||
Arguments(&'a Arguments),
|
||||
Parameters(&'a Parameters),
|
||||
|
@ -4784,6 +4895,8 @@ impl AnyNodeRef<'_> {
|
|||
AnyNodeRef::PatternMatchStar(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::PatternMatchAs(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::PatternMatchOr(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::PatternArguments(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::PatternKeyword(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::Comprehension(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::Arguments(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::Parameters(node) => NonNull::from(*node).cast(),
|
||||
|
@ -4874,6 +4987,8 @@ impl AnyNodeRef<'_> {
|
|||
AnyNodeRef::PatternMatchStar(_) => NodeKind::PatternMatchStar,
|
||||
AnyNodeRef::PatternMatchAs(_) => NodeKind::PatternMatchAs,
|
||||
AnyNodeRef::PatternMatchOr(_) => NodeKind::PatternMatchOr,
|
||||
AnyNodeRef::PatternArguments(_) => NodeKind::PatternArguments,
|
||||
AnyNodeRef::PatternKeyword(_) => NodeKind::PatternKeyword,
|
||||
AnyNodeRef::Comprehension(_) => NodeKind::Comprehension,
|
||||
AnyNodeRef::Arguments(_) => NodeKind::Arguments,
|
||||
AnyNodeRef::Parameters(_) => NodeKind::Parameters,
|
||||
|
@ -4959,6 +5074,8 @@ impl AnyNodeRef<'_> {
|
|||
| AnyNodeRef::PatternMatchStar(_)
|
||||
| AnyNodeRef::PatternMatchAs(_)
|
||||
| AnyNodeRef::PatternMatchOr(_)
|
||||
| AnyNodeRef::PatternArguments(_)
|
||||
| AnyNodeRef::PatternKeyword(_)
|
||||
| AnyNodeRef::Comprehension(_)
|
||||
| AnyNodeRef::Arguments(_)
|
||||
| AnyNodeRef::Parameters(_)
|
||||
|
@ -5044,6 +5161,8 @@ impl AnyNodeRef<'_> {
|
|||
| AnyNodeRef::PatternMatchStar(_)
|
||||
| AnyNodeRef::PatternMatchAs(_)
|
||||
| AnyNodeRef::PatternMatchOr(_)
|
||||
| AnyNodeRef::PatternArguments(_)
|
||||
| AnyNodeRef::PatternKeyword(_)
|
||||
| AnyNodeRef::Comprehension(_)
|
||||
| AnyNodeRef::Arguments(_)
|
||||
| AnyNodeRef::Parameters(_)
|
||||
|
@ -5128,6 +5247,8 @@ impl AnyNodeRef<'_> {
|
|||
| AnyNodeRef::PatternMatchStar(_)
|
||||
| AnyNodeRef::PatternMatchAs(_)
|
||||
| AnyNodeRef::PatternMatchOr(_)
|
||||
| AnyNodeRef::PatternArguments(_)
|
||||
| AnyNodeRef::PatternKeyword(_)
|
||||
| AnyNodeRef::Comprehension(_)
|
||||
| AnyNodeRef::Arguments(_)
|
||||
| AnyNodeRef::Parameters(_)
|
||||
|
@ -5212,6 +5333,8 @@ impl AnyNodeRef<'_> {
|
|||
| AnyNodeRef::ExprTuple(_)
|
||||
| AnyNodeRef::ExprSlice(_)
|
||||
| AnyNodeRef::ExprIpyEscapeCommand(_)
|
||||
| AnyNodeRef::PatternArguments(_)
|
||||
| AnyNodeRef::PatternKeyword(_)
|
||||
| AnyNodeRef::ExceptHandlerExceptHandler(_)
|
||||
| AnyNodeRef::Comprehension(_)
|
||||
| AnyNodeRef::Arguments(_)
|
||||
|
@ -5298,6 +5421,8 @@ impl AnyNodeRef<'_> {
|
|||
| AnyNodeRef::PatternMatchStar(_)
|
||||
| AnyNodeRef::PatternMatchAs(_)
|
||||
| AnyNodeRef::PatternMatchOr(_)
|
||||
| AnyNodeRef::PatternArguments(_)
|
||||
| AnyNodeRef::PatternKeyword(_)
|
||||
| AnyNodeRef::Comprehension(_)
|
||||
| AnyNodeRef::Arguments(_)
|
||||
| AnyNodeRef::Parameters(_)
|
||||
|
@ -5411,6 +5536,8 @@ impl AnyNodeRef<'_> {
|
|||
AnyNodeRef::PatternMatchStar(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::PatternMatchAs(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::PatternMatchOr(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::PatternArguments(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::PatternKeyword(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::Comprehension(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::Arguments(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::Parameters(node) => node.visit_preorder(visitor),
|
||||
|
@ -5820,6 +5947,18 @@ impl<'a> From<&'a ast::PatternMatchOr> for AnyNodeRef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::PatternArguments> for AnyNodeRef<'a> {
|
||||
fn from(node: &'a ast::PatternArguments) -> Self {
|
||||
AnyNodeRef::PatternArguments(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::PatternKeyword> for AnyNodeRef<'a> {
|
||||
fn from(node: &'a ast::PatternKeyword) -> Self {
|
||||
AnyNodeRef::PatternKeyword(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Decorator> for AnyNodeRef<'a> {
|
||||
fn from(node: &'a Decorator) -> Self {
|
||||
AnyNodeRef::Decorator(node)
|
||||
|
@ -6073,6 +6212,8 @@ impl Ranged for AnyNodeRef<'_> {
|
|||
AnyNodeRef::PatternMatchStar(node) => node.range(),
|
||||
AnyNodeRef::PatternMatchAs(node) => node.range(),
|
||||
AnyNodeRef::PatternMatchOr(node) => node.range(),
|
||||
AnyNodeRef::PatternArguments(node) => node.range(),
|
||||
AnyNodeRef::PatternKeyword(node) => node.range(),
|
||||
AnyNodeRef::Comprehension(node) => node.range(),
|
||||
AnyNodeRef::Arguments(node) => node.range(),
|
||||
AnyNodeRef::Parameters(node) => node.range(),
|
||||
|
@ -6160,6 +6301,8 @@ pub enum NodeKind {
|
|||
PatternMatchStar,
|
||||
PatternMatchAs,
|
||||
PatternMatchOr,
|
||||
PatternArguments,
|
||||
PatternKeyword,
|
||||
TypeIgnoreTypeIgnore,
|
||||
Comprehension,
|
||||
Arguments,
|
||||
|
|
|
@ -1900,9 +1900,7 @@ impl From<PatternMatchMapping> for Pattern {
|
|||
pub struct PatternMatchClass {
|
||||
pub range: TextRange,
|
||||
pub cls: Box<Expr>,
|
||||
pub patterns: Vec<Pattern>,
|
||||
pub kwd_attrs: Vec<Identifier>,
|
||||
pub kwd_patterns: Vec<Pattern>,
|
||||
pub arguments: PatternArguments,
|
||||
}
|
||||
|
||||
impl From<PatternMatchClass> for Pattern {
|
||||
|
@ -1911,6 +1909,28 @@ impl From<PatternMatchClass> for Pattern {
|
|||
}
|
||||
}
|
||||
|
||||
/// An AST node to represent the arguments to a [`PatternMatchClass`], i.e., the
|
||||
/// parenthesized contents in `case Point(1, x=0, y=0)`.
|
||||
///
|
||||
/// Like [`Arguments`], but for [`PatternMatchClass`].
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PatternArguments {
|
||||
pub range: TextRange,
|
||||
pub patterns: Vec<Pattern>,
|
||||
pub keywords: Vec<PatternKeyword>,
|
||||
}
|
||||
|
||||
/// An AST node to represent the keyword arguments to a [`PatternMatchClass`], i.e., the
|
||||
/// `x=0` and `y=0` in `case Point(x=0, y=0)`.
|
||||
///
|
||||
/// Like [`Keyword`], but for [`PatternMatchClass`].
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PatternKeyword {
|
||||
pub range: TextRange,
|
||||
pub attr: Identifier,
|
||||
pub pattern: Pattern,
|
||||
}
|
||||
|
||||
/// See also [MatchStar](https://docs.python.org/3/library/ast.html#ast.MatchStar)
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PatternMatchStar {
|
||||
|
@ -3004,6 +3024,16 @@ impl Ranged for crate::Pattern {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl Ranged for crate::nodes::PatternArguments {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
impl Ranged for crate::nodes::PatternKeyword {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for crate::nodes::TypeParams {
|
||||
fn range(&self) -> TextRange {
|
||||
|
|
|
@ -5,7 +5,8 @@ pub mod preorder;
|
|||
use crate::{
|
||||
self as ast, Alias, Arguments, BoolOp, CmpOp, Comprehension, Decorator, ElifElseClause,
|
||||
ExceptHandler, Expr, ExprContext, Keyword, MatchCase, Operator, Parameter, Parameters, Pattern,
|
||||
Stmt, TypeParam, TypeParamTypeVar, TypeParams, UnaryOp, WithItem,
|
||||
PatternArguments, PatternKeyword, Stmt, TypeParam, TypeParamTypeVar, TypeParams, UnaryOp,
|
||||
WithItem,
|
||||
};
|
||||
|
||||
/// A trait for AST visitors. Visits all nodes in the AST recursively in evaluation-order.
|
||||
|
@ -82,6 +83,12 @@ pub trait Visitor<'a> {
|
|||
fn visit_pattern(&mut self, pattern: &'a Pattern) {
|
||||
walk_pattern(self, pattern);
|
||||
}
|
||||
fn visit_pattern_arguments(&mut self, pattern_arguments: &'a PatternArguments) {
|
||||
walk_pattern_arguments(self, pattern_arguments);
|
||||
}
|
||||
fn visit_pattern_keyword(&mut self, pattern_keyword: &'a PatternKeyword) {
|
||||
walk_pattern_keyword(self, pattern_keyword);
|
||||
}
|
||||
fn visit_body(&mut self, body: &'a [Stmt]) {
|
||||
walk_body(self, body);
|
||||
}
|
||||
|
@ -674,20 +681,9 @@ pub fn walk_pattern<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, pattern: &'a P
|
|||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
}
|
||||
Pattern::MatchClass(ast::PatternMatchClass {
|
||||
cls,
|
||||
patterns,
|
||||
kwd_patterns,
|
||||
..
|
||||
}) => {
|
||||
Pattern::MatchClass(ast::PatternMatchClass { cls, arguments, .. }) => {
|
||||
visitor.visit_expr(cls);
|
||||
for pattern in patterns {
|
||||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
|
||||
for pattern in kwd_patterns {
|
||||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
visitor.visit_pattern_arguments(arguments);
|
||||
}
|
||||
Pattern::MatchStar(_) => {}
|
||||
Pattern::MatchAs(ast::PatternMatchAs { pattern, .. }) => {
|
||||
|
@ -703,6 +699,25 @@ pub fn walk_pattern<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, pattern: &'a P
|
|||
}
|
||||
}
|
||||
|
||||
pub fn walk_pattern_arguments<'a, V: Visitor<'a> + ?Sized>(
|
||||
visitor: &mut V,
|
||||
pattern_arguments: &'a PatternArguments,
|
||||
) {
|
||||
for pattern in &pattern_arguments.patterns {
|
||||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
for keyword in &pattern_arguments.keywords {
|
||||
visitor.visit_pattern_keyword(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_pattern_keyword<'a, V: Visitor<'a> + ?Sized>(
|
||||
visitor: &mut V,
|
||||
pattern_keyword: &'a PatternKeyword,
|
||||
) {
|
||||
visitor.visit_pattern(&pattern_keyword.pattern);
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn walk_expr_context<'a, V: Visitor<'a> + ?Sized>(visitor: &V, expr_context: &'a ExprContext) {}
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ use crate::node::{AnyNodeRef, AstNode};
|
|||
use crate::{
|
||||
Alias, Arguments, BoolOp, CmpOp, Comprehension, Constant, Decorator, ElifElseClause,
|
||||
ExceptHandler, Expr, Keyword, MatchCase, Mod, Operator, Parameter, ParameterWithDefault,
|
||||
Parameters, Pattern, Stmt, TypeParam, TypeParams, UnaryOp, WithItem,
|
||||
Parameters, Pattern, PatternArguments, PatternKeyword, Stmt, TypeParam, TypeParams, UnaryOp,
|
||||
WithItem,
|
||||
};
|
||||
|
||||
/// Visitor that traverses all nodes recursively in pre-order.
|
||||
|
@ -132,6 +133,17 @@ pub trait PreorderVisitor<'a> {
|
|||
walk_pattern(self, pattern);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_pattern_arguments(&mut self, pattern_arguments: &'a PatternArguments) {
|
||||
walk_pattern_arguments(self, pattern_arguments);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
fn visit_pattern_keyword(&mut self, pattern_keyword: &'a PatternKeyword) {
|
||||
walk_pattern_keyword(self, pattern_keyword);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_body(&mut self, body: &'a [Stmt]) {
|
||||
walk_body(self, body);
|
||||
|
@ -460,6 +472,33 @@ where
|
|||
visitor.leave_node(node);
|
||||
}
|
||||
|
||||
pub fn walk_pattern_arguments<'a, V>(visitor: &mut V, pattern_arguments: &'a PatternArguments)
|
||||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
{
|
||||
let node = AnyNodeRef::from(pattern_arguments);
|
||||
if visitor.enter_node(node).is_traverse() {
|
||||
for pattern in &pattern_arguments.patterns {
|
||||
visitor.visit_pattern(pattern);
|
||||
}
|
||||
for keyword in &pattern_arguments.keywords {
|
||||
visitor.visit_pattern_keyword(keyword);
|
||||
}
|
||||
}
|
||||
visitor.leave_node(node);
|
||||
}
|
||||
|
||||
pub fn walk_pattern_keyword<'a, V>(visitor: &mut V, pattern_keyword: &'a PatternKeyword)
|
||||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
{
|
||||
let node = AnyNodeRef::from(pattern_keyword);
|
||||
if visitor.enter_node(node).is_traverse() {
|
||||
visitor.visit_pattern(&pattern_keyword.pattern);
|
||||
}
|
||||
visitor.leave_node(node);
|
||||
}
|
||||
|
||||
pub fn walk_bool_op<'a, V>(_visitor: &mut V, _bool_op: &'a BoolOp)
|
||||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
|
|
|
@ -458,3 +458,12 @@ match pattern_match_class:
|
|||
# e
|
||||
):
|
||||
pass
|
||||
|
||||
case A(
|
||||
# a
|
||||
b # b
|
||||
= # c
|
||||
2 # d
|
||||
# e
|
||||
):
|
||||
pass
|
||||
|
|
|
@ -174,7 +174,7 @@ fn handle_enclosed_comment<'a>(
|
|||
}
|
||||
})
|
||||
}
|
||||
AnyNodeRef::Arguments(_) | AnyNodeRef::TypeParams(_) => {
|
||||
AnyNodeRef::Arguments(_) | AnyNodeRef::TypeParams(_) | AnyNodeRef::PatternArguments(_) => {
|
||||
handle_bracketed_end_of_line_comment(comment, locator)
|
||||
}
|
||||
AnyNodeRef::Comprehension(comprehension) => {
|
||||
|
@ -220,9 +220,7 @@ fn handle_enclosed_comment<'a>(
|
|||
CommentPlacement::Default(comment)
|
||||
}
|
||||
}
|
||||
AnyNodeRef::PatternMatchClass(class) => {
|
||||
handle_pattern_match_class_comment(comment, class, locator)
|
||||
}
|
||||
AnyNodeRef::PatternMatchClass(class) => handle_pattern_match_class_comment(comment, class),
|
||||
AnyNodeRef::PatternMatchAs(_) => handle_pattern_match_as_comment(comment, locator),
|
||||
AnyNodeRef::PatternMatchStar(_) => handle_pattern_match_star_comment(comment),
|
||||
AnyNodeRef::PatternMatchMapping(pattern) => {
|
||||
|
@ -1233,75 +1231,23 @@ fn handle_with_item_comment<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Handles trailing comments after the `as` keyword of a pattern match item:
|
||||
///
|
||||
/// Handles trailing comments between the class name and its arguments in:
|
||||
/// ```python
|
||||
/// case (
|
||||
/// Pattern
|
||||
/// # dangling
|
||||
/// ( # dangling
|
||||
/// # dangling
|
||||
/// )
|
||||
/// (...)
|
||||
/// ): ...
|
||||
/// ```
|
||||
fn handle_pattern_match_class_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
class: &'a ast::PatternMatchClass,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
// Find the open parentheses on the arguments.
|
||||
let Some(left_paren) = SimpleTokenizer::starts_at(class.cls.end(), locator.contents())
|
||||
.skip_trivia()
|
||||
.find(|token| token.kind == SimpleTokenKind::LParen)
|
||||
else {
|
||||
return CommentPlacement::Default(comment);
|
||||
};
|
||||
|
||||
// If the comment appears before the open parenthesis, it's dangling:
|
||||
// ```python
|
||||
// case (
|
||||
// Pattern
|
||||
// # dangling
|
||||
// (...)
|
||||
// ): ...
|
||||
// ```
|
||||
if comment.end() < left_paren.start() {
|
||||
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||
}
|
||||
|
||||
let Some(first_item) = class
|
||||
.patterns
|
||||
.first()
|
||||
.map(Ranged::start)
|
||||
.or_else(|| class.kwd_attrs.first().map(Ranged::start))
|
||||
else {
|
||||
// If there are no items, then the comment must be dangling:
|
||||
// ```python
|
||||
// case (
|
||||
// Pattern(
|
||||
// # dangling
|
||||
// )
|
||||
// ): ...
|
||||
// ```
|
||||
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||
};
|
||||
|
||||
// If the comment appears before the first item or its parentheses, then it's dangling:
|
||||
// ```python
|
||||
// case (
|
||||
// Pattern( # dangling
|
||||
// 0,
|
||||
// 0,
|
||||
// )
|
||||
// ): ...
|
||||
// ```
|
||||
if comment.line_position().is_end_of_line() {
|
||||
if comment.end() < first_item {
|
||||
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||
}
|
||||
}
|
||||
|
||||
if class.cls.end() < comment.start() && comment.end() < class.arguments.start() {
|
||||
CommentPlacement::dangling(comment.enclosing_node(), comment)
|
||||
} else {
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles trailing comments after the `as` keyword of a pattern match item:
|
||||
|
|
|
@ -2256,6 +2256,78 @@ impl<'ast> IntoFormat<PyFormatContext<'ast>> for ast::PatternMatchOr {
|
|||
}
|
||||
}
|
||||
|
||||
impl FormatRule<ast::PatternArguments, PyFormatContext<'_>>
|
||||
for crate::pattern::pattern_arguments::FormatPatternArguments
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, node: &ast::PatternArguments, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
FormatNodeRule::<ast::PatternArguments>::fmt(self, node, f)
|
||||
}
|
||||
}
|
||||
impl<'ast> AsFormat<PyFormatContext<'ast>> for ast::PatternArguments {
|
||||
type Format<'a> = FormatRefWithRule<
|
||||
'a,
|
||||
ast::PatternArguments,
|
||||
crate::pattern::pattern_arguments::FormatPatternArguments,
|
||||
PyFormatContext<'ast>,
|
||||
>;
|
||||
fn format(&self) -> Self::Format<'_> {
|
||||
FormatRefWithRule::new(
|
||||
self,
|
||||
crate::pattern::pattern_arguments::FormatPatternArguments::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
impl<'ast> IntoFormat<PyFormatContext<'ast>> for ast::PatternArguments {
|
||||
type Format = FormatOwnedWithRule<
|
||||
ast::PatternArguments,
|
||||
crate::pattern::pattern_arguments::FormatPatternArguments,
|
||||
PyFormatContext<'ast>,
|
||||
>;
|
||||
fn into_format(self) -> Self::Format {
|
||||
FormatOwnedWithRule::new(
|
||||
self,
|
||||
crate::pattern::pattern_arguments::FormatPatternArguments::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatRule<ast::PatternKeyword, PyFormatContext<'_>>
|
||||
for crate::pattern::pattern_keyword::FormatPatternKeyword
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, node: &ast::PatternKeyword, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
FormatNodeRule::<ast::PatternKeyword>::fmt(self, node, f)
|
||||
}
|
||||
}
|
||||
impl<'ast> AsFormat<PyFormatContext<'ast>> for ast::PatternKeyword {
|
||||
type Format<'a> = FormatRefWithRule<
|
||||
'a,
|
||||
ast::PatternKeyword,
|
||||
crate::pattern::pattern_keyword::FormatPatternKeyword,
|
||||
PyFormatContext<'ast>,
|
||||
>;
|
||||
fn format(&self) -> Self::Format<'_> {
|
||||
FormatRefWithRule::new(
|
||||
self,
|
||||
crate::pattern::pattern_keyword::FormatPatternKeyword::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
impl<'ast> IntoFormat<PyFormatContext<'ast>> for ast::PatternKeyword {
|
||||
type Format = FormatOwnedWithRule<
|
||||
ast::PatternKeyword,
|
||||
crate::pattern::pattern_keyword::FormatPatternKeyword,
|
||||
PyFormatContext<'ast>,
|
||||
>;
|
||||
fn into_format(self) -> Self::Format {
|
||||
FormatOwnedWithRule::new(
|
||||
self,
|
||||
crate::pattern::pattern_keyword::FormatPatternKeyword::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatRule<ast::Comprehension, PyFormatContext<'_>>
|
||||
for crate::other::comprehension::FormatComprehension
|
||||
{
|
||||
|
|
|
@ -8,6 +8,8 @@ use crate::expression::parentheses::{
|
|||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub(crate) mod pattern_arguments;
|
||||
pub(crate) mod pattern_keyword;
|
||||
pub(crate) mod pattern_match_as;
|
||||
pub(crate) mod pattern_match_class;
|
||||
pub(crate) mod pattern_match_mapping;
|
||||
|
|
110
crates/ruff_python_formatter/src/pattern/pattern_arguments.rs
Normal file
110
crates/ruff_python_formatter/src/pattern/pattern_arguments.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_python_ast::{Pattern, PatternArguments, Ranged};
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
use crate::expression::parentheses::{empty_parenthesized, parenthesized, Parentheses};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatPatternArguments;
|
||||
|
||||
impl FormatNodeRule<PatternArguments> for FormatPatternArguments {
|
||||
fn fmt_fields(&self, item: &PatternArguments, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
// If there are no arguments, all comments are dangling:
|
||||
// ```python
|
||||
// case Point2D( # dangling
|
||||
// # dangling
|
||||
// )
|
||||
// ```
|
||||
if item.patterns.is_empty() && item.keywords.is_empty() {
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling = comments.dangling(item);
|
||||
return write!(f, [empty_parenthesized("(", dangling, ")")]);
|
||||
}
|
||||
|
||||
let all_arguments = format_with(|f: &mut PyFormatter| {
|
||||
let source = f.context().source();
|
||||
let mut joiner = f.join_comma_separated(item.end());
|
||||
match item.patterns.as_slice() {
|
||||
[pattern] if item.keywords.is_empty() => {
|
||||
let parentheses =
|
||||
if is_single_argument_parenthesized(pattern, item.end(), source) {
|
||||
Parentheses::Always
|
||||
} else {
|
||||
// Note: no need to handle opening-parenthesis comments, since
|
||||
// an opening-parenthesis comment implies that the argument is
|
||||
// parenthesized.
|
||||
Parentheses::Never
|
||||
};
|
||||
joiner.entry(pattern, &pattern.format().with_options(parentheses));
|
||||
}
|
||||
patterns => {
|
||||
joiner
|
||||
.entries(patterns.iter().map(|pattern| {
|
||||
(
|
||||
pattern,
|
||||
pattern.format().with_options(Parentheses::Preserve),
|
||||
)
|
||||
}))
|
||||
.nodes(item.keywords.iter());
|
||||
}
|
||||
}
|
||||
|
||||
joiner.finish()
|
||||
});
|
||||
|
||||
// If the arguments are non-empty, then a dangling comment indicates a comment on the
|
||||
// same line as the opening parenthesis, e.g.:
|
||||
// ```python
|
||||
// case Point2D( # dangling
|
||||
// ...
|
||||
// )
|
||||
// ```
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling_comments = comments.dangling(item.as_any_node_ref());
|
||||
|
||||
write!(
|
||||
f,
|
||||
[parenthesized("(", &group(&all_arguments), ")")
|
||||
.with_dangling_comments(dangling_comments)]
|
||||
)
|
||||
}
|
||||
|
||||
fn fmt_dangling_comments(
|
||||
&self,
|
||||
_dangling_comments: &[SourceComment],
|
||||
_f: &mut PyFormatter,
|
||||
) -> FormatResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the pattern (which is the only argument to a [`PatternMatchClass`]) is
|
||||
/// parenthesized. Used to avoid falsely assuming that `x` is parenthesized in cases like:
|
||||
/// ```python
|
||||
/// case Point2D(x): ...
|
||||
/// ```
|
||||
fn is_single_argument_parenthesized(pattern: &Pattern, call_end: TextSize, source: &str) -> bool {
|
||||
let mut has_seen_r_paren = false;
|
||||
for token in SimpleTokenizer::new(source, TextRange::new(pattern.end(), call_end)).skip_trivia()
|
||||
{
|
||||
match token.kind() {
|
||||
SimpleTokenKind::RParen => {
|
||||
if has_seen_r_paren {
|
||||
return true;
|
||||
}
|
||||
has_seen_r_paren = true;
|
||||
}
|
||||
// Skip over any trailing comma
|
||||
SimpleTokenKind::Comma => continue,
|
||||
_ => {
|
||||
// Passed the arguments
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
17
crates/ruff_python_formatter/src/pattern/pattern_keyword.rs
Normal file
17
crates/ruff_python_formatter/src/pattern/pattern_keyword.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use crate::prelude::*;
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::PatternKeyword;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatPatternKeyword;
|
||||
|
||||
impl FormatNodeRule<PatternKeyword> for FormatPatternKeyword {
|
||||
fn fmt_fields(&self, item: &PatternKeyword, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
let PatternKeyword {
|
||||
range: _,
|
||||
attr,
|
||||
pattern,
|
||||
} = item;
|
||||
write!(f, [attr.format(), text("="), pattern.format()])
|
||||
}
|
||||
}
|
|
@ -1,13 +1,9 @@
|
|||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::{Pattern, PatternMatchClass, Ranged};
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_python_ast::PatternMatchClass;
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
use crate::expression::parentheses::{
|
||||
empty_parenthesized, parenthesized, NeedsParentheses, OptionalParentheses, Parentheses,
|
||||
};
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -16,75 +12,23 @@ pub struct FormatPatternMatchClass;
|
|||
impl FormatNodeRule<PatternMatchClass> for FormatPatternMatchClass {
|
||||
fn fmt_fields(&self, item: &PatternMatchClass, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
let PatternMatchClass {
|
||||
range,
|
||||
range: _,
|
||||
cls,
|
||||
patterns,
|
||||
kwd_attrs,
|
||||
kwd_patterns,
|
||||
arguments,
|
||||
} = item;
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling = comments.dangling(item);
|
||||
|
||||
// Identify the dangling comments before and after the open parenthesis.
|
||||
let (before_parenthesis, after_parenthesis) = if let Some(left_paren) =
|
||||
SimpleTokenizer::starts_at(cls.end(), f.context().source())
|
||||
.find(|token| token.kind() == SimpleTokenKind::LParen)
|
||||
{
|
||||
dangling
|
||||
.split_at(dangling.partition_point(|comment| comment.start() < left_paren.start()))
|
||||
} else {
|
||||
(dangling, [].as_slice())
|
||||
};
|
||||
|
||||
write!(f, [cls.format(), dangling_comments(before_parenthesis)])?;
|
||||
|
||||
match (patterns.as_slice(), kwd_attrs.as_slice()) {
|
||||
([], []) => {
|
||||
// No patterns; render parentheses with any dangling comments.
|
||||
write!(f, [empty_parenthesized("(", after_parenthesis, ")")])
|
||||
}
|
||||
([pattern], []) => {
|
||||
// A single pattern. We need to take care not to re-parenthesize it, since our standard
|
||||
// parenthesis detection will false-positive here.
|
||||
let parentheses = if is_single_argument_parenthesized(
|
||||
pattern,
|
||||
item.end(),
|
||||
f.context().source(),
|
||||
) {
|
||||
Parentheses::Always
|
||||
} else {
|
||||
Parentheses::Never
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
parenthesized("(", &pattern.format().with_options(parentheses), ")")
|
||||
.with_dangling_comments(after_parenthesis)
|
||||
cls.format(),
|
||||
dangling_comments(dangling),
|
||||
arguments.format()
|
||||
]
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// Multiple patterns: standard logic.
|
||||
let items = format_with(|f| {
|
||||
let mut join = f.join_comma_separated(range.end());
|
||||
join.nodes(patterns.iter());
|
||||
for (key, value) in kwd_attrs.iter().zip(kwd_patterns.iter()) {
|
||||
join.entry(
|
||||
key,
|
||||
&format_with(|f| write!(f, [key.format(), text("="), value.format()])),
|
||||
);
|
||||
}
|
||||
join.finish()
|
||||
});
|
||||
write!(
|
||||
f,
|
||||
[parenthesized("(", &group(&items), ")")
|
||||
.with_dangling_comments(after_parenthesis)]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_dangling_comments(
|
||||
&self,
|
||||
|
@ -109,46 +53,10 @@ impl NeedsParentheses for PatternMatchClass {
|
|||
// (...)
|
||||
// ): ...
|
||||
// ```
|
||||
let dangling = context.comments().dangling(self);
|
||||
if !dangling.is_empty() {
|
||||
if let Some(left_paren) = SimpleTokenizer::starts_at(self.cls.end(), context.source())
|
||||
.find(|token| token.kind() == SimpleTokenKind::LParen)
|
||||
{
|
||||
if dangling
|
||||
.iter()
|
||||
.any(|comment| comment.start() < left_paren.start())
|
||||
{
|
||||
return OptionalParentheses::Multiline;
|
||||
};
|
||||
}
|
||||
}
|
||||
if context.comments().has_dangling(self) {
|
||||
OptionalParentheses::Multiline
|
||||
} else {
|
||||
OptionalParentheses::Never
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the pattern (which is the only argument to a [`PatternMatchClass`]) is
|
||||
/// parenthesized. Used to avoid falsely assuming that `x` is parenthesized in cases like:
|
||||
/// ```python
|
||||
/// case Point2D(x): ...
|
||||
/// ```
|
||||
fn is_single_argument_parenthesized(pattern: &Pattern, call_end: TextSize, source: &str) -> bool {
|
||||
let mut has_seen_r_paren = false;
|
||||
for token in SimpleTokenizer::new(source, TextRange::new(pattern.end(), call_end)).skip_trivia()
|
||||
{
|
||||
match token.kind() {
|
||||
SimpleTokenKind::RParen => {
|
||||
if has_seen_r_paren {
|
||||
return true;
|
||||
}
|
||||
has_seen_r_paren = true;
|
||||
}
|
||||
// Skip over any trailing comma
|
||||
SimpleTokenKind::Comma => continue,
|
||||
_ => {
|
||||
// Passed the arguments
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,15 +65,7 @@ match match(
|
|||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -6,30 +6,35 @@
|
||||
):
|
||||
print(1)
|
||||
case c(
|
||||
- very_complex=True,
|
||||
- perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1,
|
||||
+ very_complex=True, perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1
|
||||
):
|
||||
print(2)
|
||||
@@ -13,20 +13,26 @@
|
||||
case a:
|
||||
pass
|
||||
|
||||
|
@ -103,10 +95,6 @@ match match(
|
|||
re.match()
|
||||
match match():
|
||||
case case(
|
||||
- arg, # comment
|
||||
+ arg # comment
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
@ -120,7 +108,8 @@ match something:
|
|||
):
|
||||
print(1)
|
||||
case c(
|
||||
very_complex=True, perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1
|
||||
very_complex=True,
|
||||
perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1,
|
||||
):
|
||||
print(2)
|
||||
case a:
|
||||
|
@ -149,7 +138,7 @@ re.match(
|
|||
re.match()
|
||||
match match():
|
||||
case case(
|
||||
arg # comment
|
||||
arg, # comment
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
|
|
@ -464,6 +464,15 @@ match pattern_match_class:
|
|||
# e
|
||||
):
|
||||
pass
|
||||
|
||||
case A(
|
||||
# a
|
||||
b # b
|
||||
= # c
|
||||
2 # d
|
||||
# e
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
## Output
|
||||
|
@ -903,10 +912,8 @@ match pattern_match_class:
|
|||
):
|
||||
...
|
||||
|
||||
case Point2D(
|
||||
# end of line
|
||||
0,
|
||||
0,
|
||||
case Point2D( # end of line
|
||||
0, 0
|
||||
):
|
||||
...
|
||||
|
||||
|
@ -932,8 +939,7 @@ match pattern_match_class:
|
|||
case Bar(0, a=None, b="hello"):
|
||||
...
|
||||
|
||||
case FooBar(
|
||||
# leading
|
||||
case FooBar( # leading
|
||||
# leading
|
||||
# leading
|
||||
# leading
|
||||
|
@ -951,6 +957,15 @@ match pattern_match_class:
|
|||
# e
|
||||
):
|
||||
pass
|
||||
|
||||
case A(
|
||||
# a
|
||||
b=# b
|
||||
# c
|
||||
2 # d
|
||||
# e
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -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
|
@ -168,9 +168,11 @@ expression: parse_ast
|
|||
ctx: Load,
|
||||
},
|
||||
),
|
||||
arguments: PatternArguments {
|
||||
range: 130..132,
|
||||
patterns: [],
|
||||
kwd_attrs: [],
|
||||
kwd_patterns: [],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
MatchSingleton(
|
||||
|
|
|
@ -90,6 +90,8 @@ expression: parse_ast
|
|||
ctx: Load,
|
||||
},
|
||||
),
|
||||
arguments: PatternArguments {
|
||||
range: 149..152,
|
||||
patterns: [
|
||||
MatchAs(
|
||||
PatternMatchAs {
|
||||
|
@ -104,8 +106,8 @@ expression: parse_ast
|
|||
},
|
||||
),
|
||||
],
|
||||
kwd_attrs: [],
|
||||
kwd_patterns: [],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
guard: None,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue