avoid extra parentheses for nested collections

This commit is contained in:
Brent Westbrook
2025-10-20 17:07:05 -04:00
parent 1d61766826
commit 545df16091

View File

@@ -1,5 +1,5 @@
use ruff_formatter::{Buffer, FormatResult, format_args, write};
use ruff_python_ast::{Comprehension, Expr};
use ruff_python_ast::{self as ast, Comprehension, Expr};
use ruff_python_trivia::{SimpleTokenKind, find_only_token_in_range};
use ruff_text_size::{Ranged, TextRange};
@@ -117,7 +117,11 @@ impl FormatNodeRule<Comprehension> for FormatComprehension {
maybe_parenthesize_expression(
iter,
item,
Parenthesize::IfBreaksParenthesizedNested
if needs_nested_parentheses(iter) {
Parenthesize::IfBreaksParenthesizedNested
} else {
Parenthesize::IfBreaks
}
)
]
)?;
@@ -188,3 +192,68 @@ impl Format<PyFormatContext<'_>> for ExprTupleWithoutParentheses<'_> {
}
}
}
/// Returns `true` if the expression needs additional parentheses when used as the `in` clause of a
/// comprehension.
///
/// For example, we need to parenthesize a long attribute expression:
///
/// ```python
/// [
/// a
/// for graph_path_expression in (
/// refined_constraint.condition_as_predicate.variables
/// )
/// ]
/// ```
///
/// but avoid adding additional parentheses to a set or other collection:
///
/// ```python
/// [
/// x
/// for x in { # _not_ `({`
/// x
/// for x in "long line long line long line long line long line long line long line"
/// }
/// ]
/// ```
fn needs_nested_parentheses(expr: &Expr) -> bool {
match expr {
Expr::Tuple(_)
| Expr::List(_)
| Expr::Set(_)
| Expr::Dict(_)
| Expr::ListComp(_)
| Expr::SetComp(_)
| Expr::DictComp(_) => false,
Expr::Starred(ast::ExprStarred { value, .. }) => needs_nested_parentheses(value),
Expr::BoolOp(_)
| Expr::Named(_)
| Expr::BinOp(_)
| Expr::UnaryOp(_)
| Expr::Lambda(_)
| Expr::If(_)
| Expr::Generator(_)
| Expr::Await(_)
| Expr::Yield(_)
| Expr::YieldFrom(_)
| Expr::Compare(_)
| Expr::Call(_)
| Expr::Attribute(_)
| Expr::Subscript(_)
| Expr::Name(_)
| Expr::Slice(_)
| Expr::IpyEscapeCommand(_)
| Expr::NumberLiteral(_)
| Expr::BooleanLiteral(_)
| Expr::NoneLiteral(_)
| Expr::StringLiteral(_)
| Expr::BytesLiteral(_)
| Expr::FString(_)
| Expr::TString(_)
| Expr::EllipsisLiteral(_) => true,
}
}