ruff/crates/ruff_python_formatter/src
Charlie Marsh 11287f944f
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** |
2023-09-14 05:05:37 -04:00
..
comments Introduce IndentWidth (#7301) 2023-09-13 14:52:24 +02:00
expression Avoid re-parenthesizing call chains whose inner values are parenthesized (#7373) 2023-09-14 05:05:37 -04:00
module Format empty lines in stub files like black's preview style (#7206) 2023-09-11 08:03:59 +00:00
other Don't reorder parameters in function calls (#7268) 2023-09-13 09:01:49 +00:00
pattern Introduce Token element (#7048) 2023-09-02 10:05:47 +02:00
snapshots Remove exception-handler lexing from unused-bound-exception fix (#5851) 2023-07-18 18:27:46 +00:00
statement Don't reorder parameters in function calls (#7268) 2023-09-13 09:01:49 +00:00
type_param Introduce Token element (#7048) 2023-09-02 10:05:47 +02:00
builders.rs Introduce Token element (#7048) 2023-09-02 10:05:47 +02:00
cli.rs Show header for formatter comment decoration info (#7228) 2023-09-08 09:25:06 +00:00
context.rs Implement DerefMut for WithNodeLevel (#6443) 2023-08-11 10:41:48 +00:00
generated.rs Introduce AST nodes for PatternMatchClass arguments (#6881) 2023-08-26 14:45:44 +00:00
lib.rs Add PreviewMode option to formatter 2023-09-08 12:04:28 +02:00
main.rs Formatter: Add SourceType to context to enable special formatting for stub files (#6331) 2023-08-04 11:52:26 +00:00
options.rs Introduce IndentWidth (#7301) 2023-09-13 14:52:24 +02:00
prelude.rs Accept any Into<AnyNodeRef> as Comments arguments (#5205) 2023-06-20 16:49:21 +00:00
verbatim.rs Memoize text width (#6552) 2023-09-06 07:10:13 +00:00