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 ResultsBenchmarkLinuxWindows |
c9e664d to
382e30f
Compare
f221e7a to
f457285
Compare
| match command.split(): | ||
| - case ["go", ("north" | "south" | "east" | "west")]: | ||
| + case ["go", NOT_YET_IMPLEMENTED_PatternMatchOf | (y)]: | ||
| + case ["go", (NOT_YET_IMPLEMENTED_PatternMatchOf | (y))]: |
There was a problem hiding this comment.
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.
Unrelated but shouldn't we consider the magic trailing comma and keep each pattern on a separate line?
| parenthesized( | ||
| "(", | ||
| &pattern.format().with_options(Parentheses::Never), | ||
| ")", | ||
| ) |
There was a problem hiding this comment.
Do we actually need to preserve the parentheses? Black does but in other nodes we don't:
while (True):
passGets formatted to:
while True:
passThere was a problem hiding this comment.
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
awaitwhere 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.
I'll try out parenthesize_if_expands.
There was a problem hiding this comment.
I think this also requires knowing whether the pattern has its own parentheses (e.g., for sequence patterns).
MichaReiser
left a comment
There was a problem hiding this comment.
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.
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
awaitwhere 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: |
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.
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.
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.
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.
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.
Actually, let me confirm that that's why it works, hang on.
There was a problem hiding this comment.
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.
I guess we kind of get this wrong:
case [ # outer
# own line
( # inner outer
[ # inner
1,
]
)
]:
passIt renders as:
case [ # outer
(
# own line
# inner outer
[ # inner
1,
]
)
]:There was a problem hiding this comment.
We might be able to remove this, I'm not sure yet, I'll explore it.
There was a problem hiding this comment.
(I'd thought we wanted to preserve the parentheses here always which complicated this a bit.)
There was a problem hiding this comment.
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.
This is relatively similar to maybe_parenthesize_expression...
MichaReiser
left a comment
There was a problem hiding this comment.
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