-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle pattern parentheses in FormatPattern
#6800
Conversation
2c3727a
to
b5b42ad
Compare
62857fc
to
c9e664d
Compare
b5b42ad
to
f5fab0d
Compare
crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap
Outdated
Show resolved
Hide resolved
f5fab0d
to
f221e7a
Compare
crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap
Show resolved
Hide resolved
PR Check ResultsBenchmarkLinux
Windows
|
c9e664d
to
382e30f
Compare
f221e7a
to
f457285
Compare
+ case ["go", NOT_YET_IMPLEMENTED_PatternMatchOf | (y)]: | ||
+ case ["go", (NOT_YET_IMPLEMENTED_PatternMatchOf | (y))]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the addition of parentheses around unions, I believe that will be handled in MatchOr
?
case ( | ||
4 as d, | ||
5 as e, | ||
NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as g, | ||
*NOT_YET_IMPLEMENTED_PatternMatchStar, | ||
): | ||
case 4 as d, (5 as e), ( | ||
NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as g | ||
), *NOT_YET_IMPLEMENTED_PatternMatchStar: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated but shouldn't we consider the magic trailing comma and keep each pattern on a separate line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should
parenthesized( | ||
"(", | ||
&pattern.format().with_options(Parentheses::Never), | ||
")", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually need to preserve the parentheses? Black does but in other nodes we don't:
while (True):
pass
Gets formatted to:
while True:
pass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our rule for expressions are:
- Top level expression (inside a statement): Remove unnecessary parentheses, see your
while (True)
example - nested expressions: Preserve the parentheses (except for
await
where Black removes parentheses).
IMO the behavior should be the same for Patterns
. Remove unnecessary parentheses around the outermost pattern but preserve them for nested patterns. See #6753
Meaning, we should use parenthesize_if_expands
here instead of preserving the parentheses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try out parenthesize_if_expands
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this also requires knowing whether the pattern has its own parentheses (e.g., for sequence patterns).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this address #6753 ? If not, what's missing? Could we implement the required changes to match the expected output
parenthesized( | ||
"(", | ||
&pattern.format().with_options(Parentheses::Never), | ||
")", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our rule for expressions are:
- Top level expression (inside a statement): Remove unnecessary parentheses, see your
while (True)
example - nested expressions: Preserve the parentheses (except for
await
where Black removes parentheses).
IMO the behavior should be the same for Patterns
. Remove unnecessary parentheses around the outermost pattern but preserve them for nested patterns. See #6753
Meaning, we should use parenthesize_if_expands
here instead of preserving the parentheses.
case ( | ||
4 as d, | ||
5 as e, | ||
NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as g, | ||
*NOT_YET_IMPLEMENTED_PatternMatchStar, | ||
): | ||
case 4 as d, (5 as e), ( | ||
NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as g | ||
), *NOT_YET_IMPLEMENTED_PatternMatchStar: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should
crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap
Outdated
Show resolved
Hide resolved
@@ -39,9 +39,13 @@ impl FormatNodeRule<MatchCase> for FormatMatchCase { | |||
write!(f, [text("case"), space()])?; | |||
|
|||
if is_match_case_pattern_parenthesized(item, pattern, f.context())? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why calling pattern.format
isn't sufficient? Why can the pattern and the MatchCase
both have trailing opening parentheses comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider:
match thing:
case ( # outer
[ # inner
1
]
)
The # outer
is attached to the MatchCase
, but the # inner
is part of the pattern itself. (Not all patterns have this behavior.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, so it depends on whether the pattern has its own parentheses? How does this work if you have nested pattern, which doesn't go through the match case formatting?
match thing:
case [
( # outer
[ # inner
1,
]
)
]: ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both of these cases work as expected as of the following branch (#6801):
match thing:
case [
( # outer
[ # inner
1,
]
)
]: ...
case [ # outer
( # inner outer
[ # inner
1,
]
)
]: ...
# inner outer
gets assigned as a leading end-of-line comment via our standard parenthesized comment handling, which then gets rendered as an open parenthesis comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, let me confirm that that's why it works, hang on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Err, I think it works because it's a leading end-of-line comment which we always treat as an open parenthesis comment. (And the comments on the inner square brackets work because they're correctly marked as dangling for the sequence pattern type.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we kind of get this wrong:
case [ # outer
# own line
( # inner outer
[ # inner
1,
]
)
]:
pass
It renders as:
case [ # outer
(
# own line
# inner outer
[ # inner
1,
]
)
]:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might be able to remove this, I'm not sure yet, I'll explore it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'd thought we wanted to preserve the parentheses here always which complicated this a bit.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I was able to remove the dangling comment from match_case
.
382e30f
to
fb314e6
Compare
5d9d616
to
161e3c5
Compare
Yes I believe it should (sorry, I missed that issue, I stumbled upon this independently). Added a test + linked the issue. |
I still need to figure out the |
161e3c5
to
e3ce944
Compare
Okay @MichaReiser I think this is ready for review based on your feedback. |
OptionalParentheses::Never => { | ||
pattern.format().with_options(Parentheses::Never).fmt(f)?; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is relatively similar to maybe_parenthesize_expression
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I love it that we can reuse the NeedsParentheses
concept
635c173
to
728e378
Compare
Summary
This PR fixes the duplicate-parenthesis problem that's visible in the tests from #6799. The issue is that we might have parentheses around the entire match-case pattern, like in
(1)
here:In this case, the inner expression (
1
) will think it's parenthesized, but we'll also detect the parentheses at the case level -- so they get rendered by the case, then again by the expression. Instead, if we detect parentheses at the case level, we can force-off the parentheses for the pattern using a design similar to the way we handle parentheses on expressions.Closes #6753.
Test Plan
cargo test