Fix attribute chain own line comments (#5340)

## Motation

Previously,
```python
x = (
    a1
    .a2
    # a
    .  # b
    # c
    a3
)
```
got formatted as
```python
x = a1.a2
# a
.  # b
# c
a3
```
which is invalid syntax. This fixes that.

## Summary

This implements a basic form of attribute chaining
(<https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#call-chains>)
by checking if any inner attribute access contains an own line comment,
and if this is the case, adds parentheses around the outermost attribute
access while disabling parentheses for all inner attribute expressions.
We want to replace this with an implementation that uses recursion or a
stack while formatting instead of in `needs_parentheses` and also
includes calls rather sooner than later, but i'm fixing this now because
i'm uncomfortable with having known invalid syntax generation in the
formatter.

## Test Plan

I added new fixtures.
This commit is contained in:
konstin 2023-06-26 11:13:07 +02:00 committed by GitHub
parent 8879927b9a
commit a52cd47c7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 258 additions and 18 deletions

View file

@ -4,6 +4,10 @@ input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression
---
## Input
```py
from argparse import Namespace
a = Namespace()
(
a
# comment
@ -32,13 +36,87 @@ input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression
)
aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaaaaaaaaaaaiiiiiiiiiiiiiiiiiiiiiiinnnnnnnn.ooooooooooooooooooooooooofffffffff.aaaaaaaaaattr
a.aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaaaaaaaaaaaiiiiiiiiiiiiiiiiiiiiiiinnnnnnnn.ooooooooooooooooooooooooofffffffff.aaaaaaaaaattr
# 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 = (
a
.b
# comment 1
. # comment 2
# comment 3
c
.d
)
x20 = (
a
.b
)
x21 = (
# leading name own line
a # trailing name end-of-line
.b
)
x22 = (
a
# outermost leading own line
.b # outermost trailing end-of-line
)
x31 = (
a
# own line between nodes 1
.b
)
x321 = (
a
. # end-of-line dot comment
b
)
x322 = (
a
. # end-of-line dot comment 2
b
.c
)
x331 = (
a.
# own line between nodes 3
b
)
x332 = (
""
# own line between nodes
.find
)
x8 = (
(a + a)
.b
)
x51 = (
a.b.c
)
x52 = a.askjdfahdlskjflsajfadhsaf.akjdsf.aksjdlfadhaljsashdfljaf.askjdflhasfdlashdlfaskjfd.asdkjfksahdfkjafs
x53 = (
a.askjdfahdlskjflsajfadhsaf.akjdsf.aksjdlfadhaljsashdfljaf.askjdflhasfdlashdlfaskjfd.asdkjfksahdfkjafs
)
```
## Output
```py
NOT_YET_IMPLEMENTED_StmtImportFrom
a = NOT_IMPLEMENTED_call()
(
a
# comment
@ -61,13 +139,60 @@ aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaaaa
(
a
# comment
. # trailing dot comment
.
# in between
b # trailing identifier comment
b # trailing dot comment # trailing identifier comment
)
aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaaaaaaaaaaaiiiiiiiiiiiiiiiiiiiiiiinnnnnnnn.ooooooooooooooooooooooooofffffffff.aaaaaaaaaattr
a.aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaaaaaaaaaaaiiiiiiiiiiiiiiiiiiiiiiinnnnnnnn.ooooooooooooooooooooooooofffffffff.aaaaaaaaaattr
# 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 = (
a.b
# comment 1
.
# comment 3
c.d # comment 2
)
x20 = a.b
x21 = (
# leading name own line
a.b # trailing name end-of-line
)
x22 = (
a
# outermost leading own line
.b # outermost trailing end-of-line
)
x31 = (
a
# own line between nodes 1
.b
)
x321 = a.b # end-of-line dot comment
x322 = a.b.c # end-of-line dot comment 2
x331 = (
a.
# own line between nodes 3
b
)
x332 = (
""
# own line between nodes
.find
)
x8 = (a + a).b
x51 = a.b.c
x52 = a.askjdfahdlskjflsajfadhsaf.akjdsf.aksjdlfadhaljsashdfljaf.askjdflhasfdlashdlfaskjfd.asdkjfksahdfkjafs
x53 = a.askjdfahdlskjflsajfadhsaf.akjdsf.aksjdlfadhaljsashdfljaf.askjdflhasfdlashdlfaskjfd.asdkjfksahdfkjafs
```