mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00
Avoid re-parenthesizing call chains whose inner values are parenthesized (#7373)
## Summary Given a statement like: ```python result = ( f(111111111111111111111111111111111111111111111111111111111111111111111111111111111) + 1 )() ``` When we go to parenthesize the target of the assignment, we use `maybe_parenthesize_expression` with `Parenthesize::IfBreaks`. This then checks if the call on the right-hand side needs to be parenthesized, the implementation of which looks like: ```rust impl NeedsParentheses for ExprCall { fn needs_parentheses( &self, _parent: AnyNodeRef, context: &PyFormatContext, ) -> OptionalParentheses { if CallChainLayout::from_expression(self.into(), context.source()) == CallChainLayout::Fluent { OptionalParentheses::Multiline } else if context.comments().has_dangling(self) { OptionalParentheses::Always } else { self.func.needs_parentheses(self.into(), context) } } } ``` Checking for `self.func.needs_parentheses(self.into(), context)` is problematic, since, as in the example above, `self.func` may _already_ be parenthesized -- in which case, we _don't_ want to parenthesize the entire expression. If we do, we end up with this non-ideal formatting: ```python result = ( ( f( 111111111111111111111111111111111111111111111111111111111111111111111111111111111 ) + 1 )() ) ``` This PR modifies the `NeedsParentheses` implementations for call chain expressions to return `Never` if the inner expression has its own parentheses, in which case, the formatting implementations for those expressions will preserve them anyway. Closes https://github.com/astral-sh/ruff/issues/7370. ## Test Plan Zulip improves a bit, everything else is unchanged. Before: | project | similarity index | total files | changed files | |--------------|------------------:|------------------:|------------------:| | cpython | 0.76083 | 1789 | 1632 | | django | 0.99981 | 2760 | 40 | | transformers | 0.99944 | 2587 | 413 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99983 | 3496 | 18 | | warehouse | 0.99834 | 648 | 20 | | zulip | 0.99956 | 1437 | 23 | After: | project | similarity index | total files | changed files | |--------------|------------------:|------------------:|------------------:| | cpython | 0.76083 | 1789 | 1632 | | django | 0.99981 | 2760 | 40 | | transformers | 0.99944 | 2587 | 413 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99983 | 3496 | 18 | | warehouse | 0.99834 | 648 | 20 | | **zulip** | **0.99962** | **1437** | **22** |
This commit is contained in:
parent
a65efcf459
commit
11287f944f
9 changed files with 139 additions and 8 deletions
|
@ -36,7 +36,7 @@ a.aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaa
|
|||
# Test that we add parentheses around the outermost attribute access in an attribute
|
||||
# chain if and only if we need them, that is if there are own line comments inside
|
||||
# the chain.
|
||||
x1 = (
|
||||
x10 = (
|
||||
a
|
||||
.b
|
||||
# comment 1
|
||||
|
@ -46,6 +46,27 @@ x1 = (
|
|||
.d
|
||||
)
|
||||
|
||||
x11 = (
|
||||
a
|
||||
.b
|
||||
# comment 1
|
||||
. # comment 2
|
||||
# comment 3
|
||||
c
|
||||
).d
|
||||
|
||||
x12 = (
|
||||
(
|
||||
a
|
||||
.b
|
||||
# comment 1
|
||||
. # comment 2
|
||||
# comment 3
|
||||
c
|
||||
)
|
||||
.d
|
||||
)
|
||||
|
||||
x20 = (
|
||||
a
|
||||
.b
|
||||
|
@ -106,7 +127,7 @@ x6 = (
|
|||
a.b
|
||||
)
|
||||
|
||||
# regression: https://github.com/astral-sh/ruff/issues/6181
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/6181
|
||||
(#
|
||||
()).a
|
||||
|
||||
|
@ -125,3 +146,9 @@ x6 = (
|
|||
# dangling before dot own-line
|
||||
.b
|
||||
)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
).bit_length()
|
||||
|
|
|
@ -265,3 +265,8 @@ f( # a
|
|||
kwargs,
|
||||
)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
)()
|
||||
|
|
5
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/subscript.py
vendored
Normal file
5
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/subscript.py
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
)[0]
|
|
@ -150,6 +150,8 @@ impl NeedsParentheses for ExprAttribute {
|
|||
OptionalParentheses::Always
|
||||
} else if self.value.is_name_expr() {
|
||||
OptionalParentheses::BestFit
|
||||
} else if is_expression_parenthesized(self.value.as_ref().into(), context.source()) {
|
||||
OptionalParentheses::Never
|
||||
} else {
|
||||
self.value.needs_parentheses(self.into(), context)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ use ruff_python_ast::node::AnyNodeRef;
|
|||
use ruff_python_ast::{Expr, ExprCall};
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
|
||||
use crate::expression::parentheses::{
|
||||
is_expression_parenthesized, NeedsParentheses, OptionalParentheses,
|
||||
};
|
||||
use crate::expression::CallChainLayout;
|
||||
use crate::prelude::*;
|
||||
|
||||
|
@ -86,6 +88,8 @@ impl NeedsParentheses for ExprCall {
|
|||
OptionalParentheses::Multiline
|
||||
} else if context.comments().has_dangling(self) {
|
||||
OptionalParentheses::Always
|
||||
} else if is_expression_parenthesized(self.func.as_ref().into(), context.source()) {
|
||||
OptionalParentheses::Never
|
||||
} else {
|
||||
self.func.needs_parentheses(self.into(), context)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ use ruff_python_ast::{Expr, ExprSubscript};
|
|||
|
||||
use crate::comments::SourceComment;
|
||||
use crate::expression::expr_tuple::TupleParentheses;
|
||||
use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses};
|
||||
use crate::expression::parentheses::{
|
||||
is_expression_parenthesized, parenthesized, NeedsParentheses, OptionalParentheses,
|
||||
};
|
||||
use crate::expression::CallChainLayout;
|
||||
use crate::prelude::*;
|
||||
|
||||
|
@ -81,6 +83,8 @@ impl NeedsParentheses for ExprSubscript {
|
|||
== CallChainLayout::Fluent
|
||||
{
|
||||
OptionalParentheses::Multiline
|
||||
} else if is_expression_parenthesized(self.value.as_ref().into(), context.source()) {
|
||||
OptionalParentheses::Never
|
||||
} else {
|
||||
match self.value.needs_parentheses(self.into(), context) {
|
||||
OptionalParentheses::BestFit => OptionalParentheses::Never,
|
||||
|
|
|
@ -42,7 +42,7 @@ a.aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaa
|
|||
# Test that we add parentheses around the outermost attribute access in an attribute
|
||||
# chain if and only if we need them, that is if there are own line comments inside
|
||||
# the chain.
|
||||
x1 = (
|
||||
x10 = (
|
||||
a
|
||||
.b
|
||||
# comment 1
|
||||
|
@ -52,6 +52,27 @@ x1 = (
|
|||
.d
|
||||
)
|
||||
|
||||
x11 = (
|
||||
a
|
||||
.b
|
||||
# comment 1
|
||||
. # comment 2
|
||||
# comment 3
|
||||
c
|
||||
).d
|
||||
|
||||
x12 = (
|
||||
(
|
||||
a
|
||||
.b
|
||||
# comment 1
|
||||
. # comment 2
|
||||
# comment 3
|
||||
c
|
||||
)
|
||||
.d
|
||||
)
|
||||
|
||||
x20 = (
|
||||
a
|
||||
.b
|
||||
|
@ -112,7 +133,7 @@ x6 = (
|
|||
a.b
|
||||
)
|
||||
|
||||
# regression: https://github.com/astral-sh/ruff/issues/6181
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/6181
|
||||
(#
|
||||
()).a
|
||||
|
||||
|
@ -131,6 +152,12 @@ x6 = (
|
|||
# dangling before dot own-line
|
||||
.b
|
||||
)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
).bit_length()
|
||||
```
|
||||
|
||||
## Output
|
||||
|
@ -173,7 +200,7 @@ a.aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaa
|
|||
# Test that we add parentheses around the outermost attribute access in an attribute
|
||||
# chain if and only if we need them, that is if there are own line comments inside
|
||||
# the chain.
|
||||
x1 = (
|
||||
x10 = (
|
||||
a.b
|
||||
# comment 1
|
||||
. # comment 2
|
||||
|
@ -181,6 +208,22 @@ x1 = (
|
|||
c.d
|
||||
)
|
||||
|
||||
x11 = (
|
||||
a.b
|
||||
# comment 1
|
||||
. # comment 2
|
||||
# comment 3
|
||||
c
|
||||
).d
|
||||
|
||||
x12 = (
|
||||
a.b
|
||||
# comment 1
|
||||
. # comment 2
|
||||
# comment 3
|
||||
c
|
||||
).d
|
||||
|
||||
x20 = a.b
|
||||
x21 = (
|
||||
# leading name own line
|
||||
|
@ -227,7 +270,7 @@ x6 = (
|
|||
a.b
|
||||
)
|
||||
|
||||
# regression: https://github.com/astral-sh/ruff/issues/6181
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/6181
|
||||
( #
|
||||
()
|
||||
).a
|
||||
|
@ -245,6 +288,12 @@ x6 = (
|
|||
# dangling before dot own-line
|
||||
.b
|
||||
)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
).bit_length()
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -271,6 +271,11 @@ f( # a
|
|||
kwargs,
|
||||
)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
)()
|
||||
```
|
||||
|
||||
## Output
|
||||
|
@ -537,6 +542,12 @@ f( # a
|
|||
** # h
|
||||
kwargs,
|
||||
)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
)()
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/subscript.py
|
||||
---
|
||||
## Input
|
||||
```py
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
)[0]
|
||||
```
|
||||
|
||||
## Output
|
||||
```py
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7370
|
||||
result = (
|
||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||
+ 1
|
||||
)[0]
|
||||
```
|
||||
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue