mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-23 04:55:09 +00:00
Fix formatting of chained boolean operations (#6394)
Closes https://github.com/astral-sh/ruff/issues/6068 These commits are kind of a mess as I did some stumbling around here. Unrolls formatting of chained boolean operations to prevent nested grouping which gives us Black-compatible formatting where each boolean operation is on a new line.
This commit is contained in:
parent
63ffadf0b8
commit
999d88e773
5 changed files with 177 additions and 16 deletions
|
@ -62,3 +62,35 @@ if (
|
||||||
and [dddddddddddddd, eeeeeeeeee, fffffffffffffff]
|
and [dddddddddddddd, eeeeeeeeee, fffffffffffffff]
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/6068
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) or numpy and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) and numpy or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) or xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) and xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) or (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy) and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) and (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy) or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
|
@ -5,18 +5,27 @@ use crate::expression::parentheses::{
|
||||||
};
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions};
|
use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions};
|
||||||
use ruff_python_ast::node::AnyNodeRef;
|
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||||
use ruff_python_ast::{BoolOp, ExprBoolOp};
|
use ruff_python_ast::{BoolOp, Expr, ExprBoolOp};
|
||||||
|
|
||||||
|
use super::parentheses::is_expression_parenthesized;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatExprBoolOp {
|
pub struct FormatExprBoolOp {
|
||||||
parentheses: Option<Parentheses>,
|
parentheses: Option<Parentheses>,
|
||||||
|
chained: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BoolOpLayout {
|
||||||
|
pub(crate) parentheses: Option<Parentheses>,
|
||||||
|
pub(crate) chained: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatRuleWithOptions<ExprBoolOp, PyFormatContext<'_>> for FormatExprBoolOp {
|
impl FormatRuleWithOptions<ExprBoolOp, PyFormatContext<'_>> for FormatExprBoolOp {
|
||||||
type Options = Option<Parentheses>;
|
type Options = BoolOpLayout;
|
||||||
fn with_options(mut self, options: Self::Options) -> Self {
|
fn with_options(mut self, options: Self::Options) -> Self {
|
||||||
self.parentheses = options;
|
self.parentheses = options.parentheses;
|
||||||
|
self.chained = options.chained;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +46,7 @@ impl FormatNodeRule<ExprBoolOp> for FormatExprBoolOp {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, [in_parentheses_only_group(&first.format())])?;
|
FormatValue { value: first }.fmt(f)?;
|
||||||
|
|
||||||
for value in values {
|
for value in values {
|
||||||
let leading_value_comments = comments.leading_comments(value);
|
let leading_value_comments = comments.leading_comments(value);
|
||||||
|
@ -51,20 +60,20 @@ impl FormatNodeRule<ExprBoolOp> for FormatExprBoolOp {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(
|
write!(f, [op.format(), space(),])?;
|
||||||
f,
|
|
||||||
[
|
FormatValue { value }.fmt(f)?;
|
||||||
op.format(),
|
|
||||||
space(),
|
|
||||||
in_parentheses_only_group(&value.format())
|
|
||||||
]
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
in_parentheses_only_group(&inner).fmt(f)
|
if self.chained {
|
||||||
|
// Chained boolean operations should not be given a new group
|
||||||
|
inner.fmt(f)
|
||||||
|
} else {
|
||||||
|
in_parentheses_only_group(&inner).fmt(f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +87,33 @@ impl NeedsParentheses for ExprBoolOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FormatValue<'a> {
|
||||||
|
value: &'a Expr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Format<PyFormatContext<'_>> for FormatValue<'_> {
|
||||||
|
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
|
match self.value {
|
||||||
|
Expr::BoolOp(bool_op)
|
||||||
|
if !is_expression_parenthesized(
|
||||||
|
bool_op.as_any_node_ref(),
|
||||||
|
f.context().source(),
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
// Mark chained boolean operations e.g. `x and y or z` and avoid creating a new group
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[bool_op.format().with_options(BoolOpLayout {
|
||||||
|
parentheses: None,
|
||||||
|
chained: true,
|
||||||
|
})]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => write!(f, [in_parentheses_only_group(&self.value.format())]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct FormatBoolOp;
|
pub struct FormatBoolOp;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ use crate::expression::parentheses::{
|
||||||
};
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use self::expr_bool_op::BoolOpLayout;
|
||||||
|
|
||||||
pub(crate) mod expr_attribute;
|
pub(crate) mod expr_attribute;
|
||||||
pub(crate) mod expr_await;
|
pub(crate) mod expr_await;
|
||||||
pub(crate) mod expr_bin_op;
|
pub(crate) mod expr_bin_op;
|
||||||
|
@ -67,7 +69,13 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
let parentheses = self.parentheses;
|
let parentheses = self.parentheses;
|
||||||
|
|
||||||
let format_expr = format_with(|f| match expression {
|
let format_expr = format_with(|f| match expression {
|
||||||
Expr::BoolOp(expr) => expr.format().with_options(Some(parentheses)).fmt(f),
|
Expr::BoolOp(expr) => expr
|
||||||
|
.format()
|
||||||
|
.with_options(BoolOpLayout {
|
||||||
|
parentheses: Some(parentheses),
|
||||||
|
chained: false,
|
||||||
|
})
|
||||||
|
.fmt(f),
|
||||||
Expr::NamedExpr(expr) => expr.format().fmt(f),
|
Expr::NamedExpr(expr) => expr.format().fmt(f),
|
||||||
Expr::BinOp(expr) => expr.format().fmt(f),
|
Expr::BinOp(expr) => expr.format().fmt(f),
|
||||||
Expr::UnaryOp(expr) => expr.format().fmt(f),
|
Expr::UnaryOp(expr) => expr.format().fmt(f),
|
||||||
|
|
|
@ -180,7 +180,8 @@ def test():
|
||||||
", %s unmodified" % unmodified_count if collected["unmodified"] else ""
|
", %s unmodified" % unmodified_count if collected["unmodified"] else ""
|
||||||
),
|
),
|
||||||
"post_processed": (
|
"post_processed": (
|
||||||
collected["post_processed"] and ", %s post-processed" % post_processed_count
|
collected["post_processed"]
|
||||||
|
and ", %s post-processed" % post_processed_count
|
||||||
or ""
|
or ""
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,38 @@ if (
|
||||||
and [dddddddddddddd, eeeeeeeeee, fffffffffffffff]
|
and [dddddddddddddd, eeeeeeeeee, fffffffffffffff]
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/6068
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) or numpy and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) and numpy or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) or xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) and xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) or (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy) and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb) and (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy) or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
|
@ -136,6 +168,58 @@ if (
|
||||||
and [dddddddddddddd, eeeeeeeeee, fffffffffffffff]
|
and [dddddddddddddd, eeeeeeeeee, fffffffffffffff]
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/6068
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb)
|
||||||
|
or numpy
|
||||||
|
and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb)
|
||||||
|
and numpy
|
||||||
|
or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb)
|
||||||
|
or xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
+ yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||||
|
and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb)
|
||||||
|
and xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
+ yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||||
|
or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb)
|
||||||
|
or (
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
+ yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||||
|
)
|
||||||
|
and isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(aaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbb)
|
||||||
|
and (
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
+ yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||||
|
)
|
||||||
|
or isinstance(ccccccccccc, dddddd)
|
||||||
|
):
|
||||||
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue