Avoid omitting optional parentheses for argument-less parentheses (#6484)

## Summary

This PR fixes some misformattings around optional parentheses for
expressions.

I first noticed that we were misformatting this:

```python
return (
    unicodedata.normalize("NFKC", s1).casefold()
    == unicodedata.normalize("NFKC", s2).casefold()
)
```

The above is stable Black formatting, but we were doing:
```python
return unicodedata.normalize("NFKC", s1).casefold() == unicodedata.normalize(
    "NFKC", s2
).casefold()
```

Above, the "last" expression is a function call, so our
`can_omit_optional_parentheses` was returning `true`...

However, it turns out that Black treats function calls differently
depending on whether or not they have arguments -- presumedly because
they'll never split empty parentheses, and so they're functionally
non-useful. On further investigation, I believe this applies to all
parenthesized expressions. If Black can't split on the parentheses, it
doesn't leverage them when removing optional parentheses.

## Test Plan

Nice increase in similarity scores.

Before:

- `zulip`: 0.99702
- `django`: 0.99784
- `warehouse`: 0.99585
- `build`: 0.75623
- `transformers`: 0.99470
- `cpython`: 0.75989
- `typeshed`: 0.74853

After:

- `zulip`: 0.99705
- `django`: 0.99795
- `warehouse`: 0.99600
- `build`: 0.75623
- `transformers`: 0.99471
- `cpython`: 0.75989
- `typeshed`: 0.74853
This commit is contained in:
Charlie Marsh 2023-08-11 13:58:42 -04:00 committed by GitHub
parent 7c4aa3948b
commit d616c9b870
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 428 additions and 36 deletions

View file

@ -1,6 +1,5 @@
use ruff_python_ast::{Expr, StmtAssign};
use ruff_formatter::{format_args, write, FormatError};
use ruff_python_ast::{Expr, StmtAssign};
use crate::context::{NodeLevel, WithNodeLevel};
use crate::expression::parentheses::{Parentheses, Parenthesize};
@ -52,7 +51,7 @@ struct FormatTargets<'a> {
impl Format<PyFormatContext<'_>> for FormatTargets<'_> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
if let Some((first, rest)) = self.targets.split_first() {
let can_omit_parentheses = has_own_parentheses(first);
let can_omit_parentheses = has_own_parentheses(first, f.context()).is_some();
let group_id = if can_omit_parentheses {
Some(f.group_id("assignment_parentheses"))