From 545df16091fe9bb4a4e422aa9592999b28acbe49 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Mon, 20 Oct 2025 17:07:05 -0400 Subject: [PATCH] avoid extra parentheses for nested collections --- .../src/other/comprehension.rs | 73 ++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/crates/ruff_python_formatter/src/other/comprehension.rs b/crates/ruff_python_formatter/src/other/comprehension.rs index 8caba13818..a6ae751f98 100644 --- a/crates/ruff_python_formatter/src/other/comprehension.rs +++ b/crates/ruff_python_formatter/src/other/comprehension.rs @@ -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 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> 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, + } +}