Preserve parentheses around left side of binary expression

<!--
Thank you for contributing to Ruff! To help us out with reviewing, please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

This PR fixes an issue where the binary expression formatting removed parentheses around the left hand side of an expression.

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

I added a new regression test and re-ran the ecosystem check. It brings down the `check-formatter-stability` output from a 3.4MB file down to 900KB. 

<!-- How was it tested? -->
This commit is contained in:
Micha Reiser 2023-06-30 09:52:14 +02:00 committed by GitHub
parent ae25638b0b
commit 9c2a75284b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 19 deletions

View file

@ -205,3 +205,9 @@ if (
# Unstable formatting in https://github.com/realtyem/synapse-unraid/blob/unraid_develop/synapse/handlers/presence.py
for user_id in set(target_user_ids) - {u.user_id for u in updates}:
updates.append(UserPresenceState.default(user_id))
# Keeps parenthesized left hand sides
(
log(self.price / self.strike)
+ (self.risk_free - self.div_cont + 0.5 * (self.sigma**2)) * self.exp_time
) / self.sigmaT

View file

@ -1,12 +1,14 @@
use crate::comments::{trailing_comments, trailing_node_comments, Comments};
use crate::expression::binary_like::{BinaryLayout, FormatBinaryLike};
use crate::expression::parentheses::{
default_expression_needs_parentheses, NeedsParentheses, Parenthesize,
default_expression_needs_parentheses, is_expression_parenthesized, NeedsParentheses,
Parenthesize,
};
use crate::expression::Parentheses;
use crate::prelude::*;
use crate::FormatNodeRule;
use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions};
use ruff_python_ast::node::AstNode;
use rustpython_parser::ast::{
Constant, Expr, ExprAttribute, ExprBinOp, ExprConstant, ExprUnaryOp, Operator, UnaryOp,
};
@ -44,9 +46,18 @@ impl<'ast> FormatBinaryLike<'ast> for ExprBinOp {
fn fmt_default(&self, f: &mut PyFormatter<'ast, '_>) -> FormatResult<()> {
let comments = f.context().comments().clone();
let format_inner = format_with(|f| {
let binary_chain: SmallVec<[&ExprBinOp; 4]> =
iter::successors(Some(self), |parent| parent.left.as_bin_op_expr()).collect();
let format_inner = format_with(|f: &mut PyFormatter| {
let source = f.context().contents();
let binary_chain: SmallVec<[&ExprBinOp; 4]> = iter::successors(Some(self), |parent| {
parent.left.as_bin_op_expr().and_then(|bin_expression| {
if is_expression_parenthesized(bin_expression.as_any_node_ref(), source) {
None
} else {
Some(bin_expression)
}
})
})
.collect();
// SAFETY: `binary_chain` is guaranteed not to be empty because it always contains the current expression.
let left_most = binary_chain.last().unwrap();

View file

@ -274,20 +274,11 @@ last_call()
Name
None
True
@@ -24,40 +24,46 @@
1 >> v2
1 % finished
1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8
-((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8)
+(1 + v2 - (v3 * 4)) ^ (5**v6 / 7 // 8)
not great
~great
+value
@@ -31,33 +31,39 @@
-1
~int and not v1 ^ 123 + v2 | True
-(~int) and (not ((v1 ^ (123 + v2)) | True))
(~int) and (not ((v1 ^ (123 + v2)) | True))
-+(really ** -(confusing ** ~(operator**-precedence)))
+(~int) and (not (v1 ^ (123 + v2) | True))
++really ** -confusing ** ~operator**-precedence
flags & ~select.EPOLLIN and waiters.write_task is not None
-lambda arg: None
@ -650,13 +641,13 @@ v1 << 2
1 >> v2
1 % finished
1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8
(1 + v2 - (v3 * 4)) ^ (5**v6 / 7 // 8)
((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8)
not great
~great
+value
-1
~int and not v1 ^ 123 + v2 | True
(~int) and (not (v1 ^ (123 + v2) | True))
(~int) and (not ((v1 ^ (123 + v2)) | True))
+really ** -confusing ** ~operator**-precedence
flags & ~select.EPOLLIN and waiters.write_task is not None
lambda x: True

View file

@ -211,6 +211,12 @@ if (
# Unstable formatting in https://github.com/realtyem/synapse-unraid/blob/unraid_develop/synapse/handlers/presence.py
for user_id in set(target_user_ids) - {u.user_id for u in updates}:
updates.append(UserPresenceState.default(user_id))
# Keeps parenthesized left hand sides
(
log(self.price / self.strike)
+ (self.risk_free - self.div_cont + 0.5 * (self.sigma**2)) * self.exp_time
) / self.sigmaT
```
## Output
@ -468,6 +474,12 @@ for user_id in set(
target_user_ids
) - {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set}:
updates.append(UserPresenceState.default(user_id))
# Keeps parenthesized left hand sides
(
log(self.price / self.strike)
+ (self.risk_free - self.div_cont + 0.5 * (self.sigma**2)) * self.exp_time
) / self.sigmaT
```

View file

@ -257,8 +257,10 @@ if aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa & (
pass
if (
not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
not (
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
)
& aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
):
pass