diff --git a/crates/ruff_python_formatter/src/expression/expr_attribute.rs b/crates/ruff_python_formatter/src/expression/expr_attribute.rs index 2fd19d768f88e7..e5a8602caad737 100644 --- a/crates/ruff_python_formatter/src/expression/expr_attribute.rs +++ b/crates/ruff_python_formatter/src/expression/expr_attribute.rs @@ -1,11 +1,12 @@ -use crate::comments::{leading_comments, trailing_comments, Comments}; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use rustpython_parser::ast::{Constant, Expr, ExprAttribute, ExprConstant}; + +use ruff_formatter::write; +use ruff_python_ast::node::AnyNodeRef; + +use crate::comments::{leading_comments, trailing_comments}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses, Parentheses}; use crate::prelude::*; use crate::FormatNodeRule; -use ruff_formatter::write; -use rustpython_parser::ast::{Constant, Expr, ExprAttribute, ExprConstant}; #[derive(Default)] pub struct FormatExprAttribute; @@ -35,7 +36,7 @@ impl FormatNodeRule for FormatExprAttribute { dangling_comments.split_at(leading_attribute_comments_start); if needs_parentheses { - value.format().with_options(Parenthesize::Always).fmt(f)?; + value.format().with_options(Parentheses::Always).fmt(f)?; } else if let Expr::Attribute(expr_attribute) = value.as_ref() { // We're in a attribute chain (`a.b.c`). The outermost node adds parentheses if // required, the inner ones don't need them so we skip the `Expr` formatting that @@ -71,41 +72,23 @@ impl FormatNodeRule for FormatExprAttribute { } } -/// Checks if there are any own line comments in an attribute chain (a.b.c). This method is -/// recursive up to the innermost expression that the attribute chain starts behind. -fn has_breaking_comments_attribute_chain( - expr_attribute: &ExprAttribute, - comments: &Comments, -) -> bool { - if comments - .dangling_comments(expr_attribute) - .iter() - .any(|comment| comment.line_position().is_own_line()) - || comments.has_trailing_own_line_comments(expr_attribute) - { - return true; - } - - if let Expr::Attribute(inner) = expr_attribute.value.as_ref() { - return has_breaking_comments_attribute_chain(inner, comments); - } - - return comments.has_trailing_own_line_comments(expr_attribute.value.as_ref()); -} - impl NeedsParentheses for ExprAttribute { fn needs_parentheses( &self, - parenthesize: Parenthesize, + parent: AnyNodeRef, context: &PyFormatContext, - ) -> Parentheses { - if has_breaking_comments_attribute_chain(self, context.comments()) { - return Parentheses::Always; - } - - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, + ) -> OptionalParentheses { + // Checks if there are any own line comments in an attribute chain (a.b.c). + if context + .comments() + .dangling_comments(self) + .iter() + .any(|comment| comment.line_position().is_own_line()) + || context.comments().has_trailing_own_line_comments(self) + { + OptionalParentheses::Always + } else { + self.value.needs_parentheses(parent, context) } } } diff --git a/crates/ruff_python_formatter/src/expression/expr_await.rs b/crates/ruff_python_formatter/src/expression/expr_await.rs index 6e275fd9a12159..2f02c352edbae4 100644 --- a/crates/ruff_python_formatter/src/expression/expr_await.rs +++ b/crates/ruff_python_formatter/src/expression/expr_await.rs @@ -1,10 +1,9 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{AsFormat, FormatNodeRule, PyFormatter}; use ruff_formatter::prelude::{space, text}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprAwait; #[derive(Default)] @@ -20,9 +19,9 @@ impl FormatNodeRule for FormatExprAwait { impl NeedsParentheses for ExprAwait { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_bin_op.rs b/crates/ruff_python_formatter/src/expression/expr_bin_op.rs index f6f051066a80a6..20d708e819c887 100644 --- a/crates/ruff_python_formatter/src/expression/expr_bin_op.rs +++ b/crates/ruff_python_formatter/src/expression/expr_bin_op.rs @@ -1,13 +1,12 @@ use crate::comments::{trailing_comments, trailing_node_comments}; use crate::expression::parentheses::{ - default_expression_needs_parentheses, in_parentheses_only_group, is_expression_parenthesized, - NeedsParentheses, Parenthesize, + in_parentheses_only_group, is_expression_parenthesized, NeedsParentheses, OptionalParentheses, }; use crate::expression::Parentheses; use crate::prelude::*; use crate::FormatNodeRule; use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions}; -use ruff_python_ast::node::AstNode; +use ruff_python_ast::node::{AnyNodeRef, AstNode}; use rustpython_parser::ast::{ Constant, Expr, ExprAttribute, ExprBinOp, ExprConstant, ExprUnaryOp, Operator, UnaryOp, }; @@ -175,9 +174,9 @@ impl FormatRule> for FormatOperator { impl NeedsParentheses for ExprBinOp { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_bool_op.rs b/crates/ruff_python_formatter/src/expression/expr_bool_op.rs index 9ba2f9a2b223ed..f18abeb18c5a30 100644 --- a/crates/ruff_python_formatter/src/expression/expr_bool_op.rs +++ b/crates/ruff_python_formatter/src/expression/expr_bool_op.rs @@ -1,10 +1,10 @@ use crate::comments::leading_comments; use crate::expression::parentheses::{ - default_expression_needs_parentheses, in_parentheses_only_group, NeedsParentheses, Parentheses, - Parenthesize, + in_parentheses_only_group, NeedsParentheses, OptionalParentheses, Parentheses, }; use crate::prelude::*; use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::{BoolOp, ExprBoolOp}; #[derive(Default)] @@ -70,10 +70,10 @@ impl FormatNodeRule for FormatExprBoolOp { impl NeedsParentheses for ExprBoolOp { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_call.rs b/crates/ruff_python_formatter/src/expression/expr_call.rs index b1dd972df267bb..c46aa374f7c41f 100644 --- a/crates/ruff_python_formatter/src/expression/expr_call.rs +++ b/crates/ruff_python_formatter/src/expression/expr_call.rs @@ -1,16 +1,17 @@ -use crate::builders::PyFormatterExtensions; +use ruff_text_size::{TextRange, TextSize}; +use rustpython_parser::ast::{Expr, ExprCall, Ranged}; + +use ruff_formatter::write; +use ruff_python_ast::node::AnyNodeRef; + use crate::comments::dangling_comments; -use crate::context::PyFormatContext; + use crate::expression::parentheses::{ - default_expression_needs_parentheses, parenthesized, NeedsParentheses, Parentheses, - Parenthesize, + parenthesized, NeedsParentheses, OptionalParentheses, Parentheses, }; +use crate::prelude::*; use crate::trivia::{SimpleTokenizer, TokenKind}; -use crate::{AsFormat, FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::{format_with, group, text}; -use ruff_formatter::{write, Buffer, FormatResult}; -use ruff_text_size::{TextRange, TextSize}; -use rustpython_parser::ast::{Expr, ExprCall, Ranged}; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatExprCall; @@ -52,14 +53,21 @@ impl FormatNodeRule for FormatExprCall { [argument] if keywords.is_empty() => { let parentheses = if is_single_argument_parenthesized(argument, item.end(), source) { - Parenthesize::Always + Parentheses::Always } else { - Parenthesize::Never + Parentheses::Never }; joiner.entry(argument, &argument.format().with_options(parentheses)); } arguments => { - joiner.nodes(arguments).nodes(keywords.iter()); + joiner + .entries( + // We have the parentheses from the call so the arguments never need any + arguments + .iter() + .map(|arg| (arg, arg.format().with_options(Parentheses::Preserve))), + ) + .nodes(keywords.iter()); } } @@ -100,13 +108,10 @@ impl FormatNodeRule for FormatExprCall { impl NeedsParentheses for ExprCall { fn needs_parentheses( &self, - parenthesize: Parenthesize, + parent: AnyNodeRef, context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + ) -> OptionalParentheses { + self.func.needs_parentheses(parent, context) } } diff --git a/crates/ruff_python_formatter/src/expression/expr_compare.rs b/crates/ruff_python_formatter/src/expression/expr_compare.rs index d03094c7debaa4..770c7501624e34 100644 --- a/crates/ruff_python_formatter/src/expression/expr_compare.rs +++ b/crates/ruff_python_formatter/src/expression/expr_compare.rs @@ -1,11 +1,11 @@ use crate::comments::leading_comments; use crate::expression::parentheses::{ - default_expression_needs_parentheses, in_parentheses_only_group, NeedsParentheses, Parentheses, - Parenthesize, + in_parentheses_only_group, NeedsParentheses, OptionalParentheses, Parentheses, }; use crate::prelude::*; use crate::FormatNodeRule; use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::{CmpOp, ExprCompare}; #[derive(Default)] @@ -73,10 +73,10 @@ impl FormatNodeRule for FormatExprCompare { impl NeedsParentheses for ExprCompare { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_constant.rs b/crates/ruff_python_formatter/src/expression/expr_constant.rs index b657b16c286071..a1e3ef58e36797 100644 --- a/crates/ruff_python_formatter/src/expression/expr_constant.rs +++ b/crates/ruff_python_formatter/src/expression/expr_constant.rs @@ -1,25 +1,17 @@ -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; -use crate::expression::string::{FormatString, StringLayout}; +use ruff_text_size::{TextLen, TextRange}; +use rustpython_parser::ast::{Constant, ExprConstant, Ranged}; + +use ruff_formatter::write; +use ruff_python_ast::node::AnyNodeRef; +use ruff_python_ast::str::is_implicit_concatenation; + +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; +use crate::expression::string::{FormatString, StringPrefix, StringQuotes}; use crate::prelude::*; use crate::{not_yet_implemented_custom_text, verbatim_text, FormatNodeRule}; -use ruff_formatter::{write, FormatRuleWithOptions}; -use rustpython_parser::ast::{Constant, ExprConstant}; #[derive(Default)] -pub struct FormatExprConstant { - string_layout: StringLayout, -} - -impl FormatRuleWithOptions> for FormatExprConstant { - type Options = StringLayout; - - fn with_options(mut self, options: Self::Options) -> Self { - self.string_layout = options; - self - } -} +pub struct FormatExprConstant; impl FormatNodeRule for FormatExprConstant { fn fmt_fields(&self, item: &ExprConstant, f: &mut PyFormatter) -> FormatResult<()> { @@ -39,7 +31,7 @@ impl FormatNodeRule for FormatExprConstant { Constant::Int(_) | Constant::Float(_) | Constant::Complex { .. } => { write!(f, [verbatim_text(item)]) } - Constant::Str(_) => FormatString::new(item, self.string_layout).fmt(f), + Constant::Str(_) => FormatString::new(item).fmt(f), Constant::Bytes(_) => { not_yet_implemented_custom_text(r#"b"NOT_YET_IMPLEMENTED_BYTE_STRING""#).fmt(f) } @@ -61,20 +53,32 @@ impl FormatNodeRule for FormatExprConstant { impl NeedsParentheses for ExprConstant { fn needs_parentheses( &self, - parenthesize: Parenthesize, + _parent: AnyNodeRef, context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional if self.value.is_str() && parenthesize.is_if_breaks() => { - // Custom handling that only adds parentheses for implicit concatenated strings. - if parenthesize.is_if_breaks() { - Parentheses::Custom - } else { - Parentheses::Optional - } + ) -> OptionalParentheses { + if self.value.is_str() { + let contents = context.locator().slice(self.range()); + // Don't wrap triple quoted strings + if is_multiline_string(self, context.source()) || !is_implicit_concatenation(contents) { + OptionalParentheses::Never + } else { + OptionalParentheses::Multiline } - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, + } else { + OptionalParentheses::Never } } } + +pub(super) fn is_multiline_string(constant: &ExprConstant, source: &str) -> bool { + if constant.value.is_str() { + let contents = &source[constant.range()]; + let prefix = StringPrefix::parse(contents); + let quotes = + StringQuotes::parse(&contents[TextRange::new(prefix.text_len(), contents.text_len())]); + + quotes.map_or(false, StringQuotes::is_triple) && contents.contains(['\n', '\r']) + } else { + false + } +} diff --git a/crates/ruff_python_formatter/src/expression/expr_dict.rs b/crates/ruff_python_formatter/src/expression/expr_dict.rs index 5a003867791058..1e8b73e0223f3e 100644 --- a/crates/ruff_python_formatter/src/expression/expr_dict.rs +++ b/crates/ruff_python_formatter/src/expression/expr_dict.rs @@ -1,11 +1,9 @@ use crate::comments::{dangling_node_comments, leading_comments}; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, parenthesized, NeedsParentheses, Parentheses, - Parenthesize, -}; +use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses}; use crate::prelude::*; use crate::FormatNodeRule; use ruff_formatter::{format_args, write}; +use ruff_python_ast::node::AnyNodeRef; use ruff_text_size::TextRange; use rustpython_parser::ast::Ranged; use rustpython_parser::ast::{Expr, ExprDict}; @@ -99,12 +97,9 @@ impl FormatNodeRule for FormatExprDict { impl NeedsParentheses for ExprDict { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_dict_comp.rs b/crates/ruff_python_formatter/src/expression/expr_dict_comp.rs index 12af04579ac08b..322dfea898b8bd 100644 --- a/crates/ruff_python_formatter/src/expression/expr_dict_comp.rs +++ b/crates/ruff_python_formatter/src/expression/expr_dict_comp.rs @@ -1,9 +1,8 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprDictComp; #[derive(Default)] @@ -23,12 +22,9 @@ impl FormatNodeRule for FormatExprDictComp { impl NeedsParentheses for ExprDictComp { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_formatted_value.rs b/crates/ruff_python_formatter/src/expression/expr_formatted_value.rs index 7cc71b967dbee9..d2f224efd5cd9b 100644 --- a/crates/ruff_python_formatter/src/expression/expr_formatted_value.rs +++ b/crates/ruff_python_formatter/src/expression/expr_formatted_value.rs @@ -1,9 +1,8 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprFormattedValue; #[derive(Default)] @@ -18,9 +17,9 @@ impl FormatNodeRule for FormatExprFormattedValue { impl NeedsParentheses for ExprFormattedValue { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_generator_exp.rs b/crates/ruff_python_formatter/src/expression/expr_generator_exp.rs index 2f91b0c518205c..b9e8b2ed6502a5 100644 --- a/crates/ruff_python_formatter/src/expression/expr_generator_exp.rs +++ b/crates/ruff_python_formatter/src/expression/expr_generator_exp.rs @@ -1,9 +1,8 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprGeneratorExp; #[derive(Default)] @@ -23,12 +22,9 @@ impl FormatNodeRule for FormatExprGeneratorExp { impl NeedsParentheses for ExprGeneratorExp { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_if_exp.rs b/crates/ruff_python_formatter/src/expression/expr_if_exp.rs index 85bcff304c2cff..5eb09d3b50b6e2 100644 --- a/crates/ruff_python_formatter/src/expression/expr_if_exp.rs +++ b/crates/ruff_python_formatter/src/expression/expr_if_exp.rs @@ -1,11 +1,11 @@ use crate::comments::leading_comments; use crate::expression::parentheses::{ - default_expression_needs_parentheses, in_parentheses_only_group, NeedsParentheses, Parentheses, - Parenthesize, + in_parentheses_only_group, NeedsParentheses, OptionalParentheses, }; use crate::prelude::*; use crate::FormatNodeRule; use ruff_formatter::{format_args, write}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprIfExp; #[derive(Default)] @@ -46,9 +46,9 @@ impl FormatNodeRule for FormatExprIfExp { impl NeedsParentheses for ExprIfExp { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_joined_str.rs b/crates/ruff_python_formatter/src/expression/expr_joined_str.rs index bd4bcf6fc91c05..f6a9c4168cae74 100644 --- a/crates/ruff_python_formatter/src/expression/expr_joined_str.rs +++ b/crates/ruff_python_formatter/src/expression/expr_joined_str.rs @@ -1,9 +1,8 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprJoinedStr; #[derive(Default)] @@ -18,9 +17,9 @@ impl FormatNodeRule for FormatExprJoinedStr { impl NeedsParentheses for ExprJoinedStr { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_lambda.rs b/crates/ruff_python_formatter/src/expression/expr_lambda.rs index 8f2f81e5b33add..e631e80061373b 100644 --- a/crates/ruff_python_formatter/src/expression/expr_lambda.rs +++ b/crates/ruff_python_formatter/src/expression/expr_lambda.rs @@ -1,9 +1,8 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprLambda; #[derive(Default)] @@ -23,9 +22,9 @@ impl FormatNodeRule for FormatExprLambda { impl NeedsParentheses for ExprLambda { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_list.rs b/crates/ruff_python_formatter/src/expression/expr_list.rs index b35abdaa4339f8..b28085fc008b34 100644 --- a/crates/ruff_python_formatter/src/expression/expr_list.rs +++ b/crates/ruff_python_formatter/src/expression/expr_list.rs @@ -1,11 +1,9 @@ use crate::comments::{dangling_comments, CommentLinePosition}; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, parenthesized, NeedsParentheses, Parentheses, - Parenthesize, -}; +use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses}; use crate::prelude::*; use crate::FormatNodeRule; use ruff_formatter::{format_args, write}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::{ExprList, Ranged}; #[derive(Default)] @@ -71,12 +69,9 @@ impl FormatNodeRule for FormatExprList { impl NeedsParentheses for ExprList { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_list_comp.rs b/crates/ruff_python_formatter/src/expression/expr_list_comp.rs index a911ecf2668ddd..764ff399b1088b 100644 --- a/crates/ruff_python_formatter/src/expression/expr_list_comp.rs +++ b/crates/ruff_python_formatter/src/expression/expr_list_comp.rs @@ -1,12 +1,10 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, parenthesized, NeedsParentheses, Parentheses, - Parenthesize, -}; +use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses}; use crate::prelude::*; use crate::AsFormat; use crate::{FormatNodeRule, PyFormatter}; use ruff_formatter::{format_args, write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprListComp; #[derive(Default)] @@ -44,12 +42,9 @@ impl FormatNodeRule for FormatExprListComp { impl NeedsParentheses for ExprListComp { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_name.rs b/crates/ruff_python_formatter/src/expression/expr_name.rs index ca07981ea16a62..b3963adb42c9b2 100644 --- a/crates/ruff_python_formatter/src/expression/expr_name.rs +++ b/crates/ruff_python_formatter/src/expression/expr_name.rs @@ -1,9 +1,8 @@ -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::prelude::*; use crate::FormatNodeRule; use ruff_formatter::{write, FormatContext}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprName; #[derive(Default)] @@ -28,10 +27,10 @@ impl FormatNodeRule for FormatExprName { impl NeedsParentheses for ExprName { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_named_expr.rs b/crates/ruff_python_formatter/src/expression/expr_named_expr.rs index e1ba5c1789e9f9..4a009b87460740 100644 --- a/crates/ruff_python_formatter/src/expression/expr_named_expr.rs +++ b/crates/ruff_python_formatter/src/expression/expr_named_expr.rs @@ -1,10 +1,9 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{AsFormat, FormatNodeRule, PyFormatter}; use ruff_formatter::prelude::{space, text}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprNamedExpr; #[derive(Default)] @@ -33,14 +32,11 @@ impl FormatNodeRule for FormatExprNamedExpr { impl NeedsParentheses for ExprNamedExpr { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - // Unlike tuples, named expression parentheses are not part of the range even when - // mandatory. See [PEP 572](https://peps.python.org/pep-0572/) for details. - Parentheses::Optional => Parentheses::Always, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + // Unlike tuples, named expression parentheses are not part of the range even when + // mandatory. See [PEP 572](https://peps.python.org/pep-0572/) for details. + OptionalParentheses::Always } } diff --git a/crates/ruff_python_formatter/src/expression/expr_set.rs b/crates/ruff_python_formatter/src/expression/expr_set.rs index 3af73764611c0f..83ff228a83a9d7 100644 --- a/crates/ruff_python_formatter/src/expression/expr_set.rs +++ b/crates/ruff_python_formatter/src/expression/expr_set.rs @@ -1,10 +1,8 @@ -use crate::expression::parentheses::{ - default_expression_needs_parentheses, parenthesized, NeedsParentheses, Parentheses, - Parenthesize, -}; +use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses}; use crate::prelude::*; use crate::FormatNodeRule; use ruff_formatter::format_args; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprSet; #[derive(Default)] @@ -29,12 +27,9 @@ impl FormatNodeRule for FormatExprSet { impl NeedsParentheses for ExprSet { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_set_comp.rs b/crates/ruff_python_formatter/src/expression/expr_set_comp.rs index e661b133868b13..9588dee66f00c7 100644 --- a/crates/ruff_python_formatter/src/expression/expr_set_comp.rs +++ b/crates/ruff_python_formatter/src/expression/expr_set_comp.rs @@ -1,9 +1,8 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprSetComp; #[derive(Default)] @@ -23,12 +22,9 @@ impl FormatNodeRule for FormatExprSetComp { impl NeedsParentheses for ExprSetComp { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_slice.rs b/crates/ruff_python_formatter/src/expression/expr_slice.rs index 1dcf71099b9f42..0d9dd7445fd8f2 100644 --- a/crates/ruff_python_formatter/src/expression/expr_slice.rs +++ b/crates/ruff_python_formatter/src/expression/expr_slice.rs @@ -1,14 +1,12 @@ use crate::comments::{dangling_comments, SourceComment}; use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::trivia::Token; use crate::trivia::{first_non_trivia_token, TokenKind}; use crate::{AsFormat, FormatNodeRule, PyFormatter}; use ruff_formatter::prelude::{hard_line_break, line_suffix_boundary, space, text}; use ruff_formatter::{write, Buffer, Format, FormatError, FormatResult}; -use ruff_python_ast::node::AstNode; +use ruff_python_ast::node::{AnyNodeRef, AstNode}; use ruff_text_size::TextRange; use rustpython_parser::ast::ExprSlice; use rustpython_parser::ast::{Expr, Ranged}; @@ -262,9 +260,9 @@ fn leading_comments_spacing( impl NeedsParentheses for ExprSlice { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_starred.rs b/crates/ruff_python_formatter/src/expression/expr_starred.rs index e45e4456765b38..6027ef84308805 100644 --- a/crates/ruff_python_formatter/src/expression/expr_starred.rs +++ b/crates/ruff_python_formatter/src/expression/expr_starred.rs @@ -1,11 +1,10 @@ use rustpython_parser::ast::ExprStarred; use ruff_formatter::write; +use ruff_python_ast::node::AnyNodeRef; use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::prelude::*; use crate::FormatNodeRule; @@ -33,9 +32,9 @@ impl FormatNodeRule for FormatExprStarred { impl NeedsParentheses for ExprStarred { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_subscript.rs b/crates/ruff_python_formatter/src/expression/expr_subscript.rs index 2a5e90c444aa85..a03cc7ec899647 100644 --- a/crates/ruff_python_formatter/src/expression/expr_subscript.rs +++ b/crates/ruff_python_formatter/src/expression/expr_subscript.rs @@ -1,15 +1,14 @@ use rustpython_parser::ast::{Expr, ExprSubscript}; use ruff_formatter::{format_args, write}; -use ruff_python_ast::node::AstNode; +use ruff_python_ast::node::{AnyNodeRef, AstNode}; use crate::comments::trailing_comments; use crate::context::NodeLevel; use crate::context::PyFormatContext; use crate::expression::expr_tuple::TupleParentheses; use crate::expression::parentheses::{ - default_expression_needs_parentheses, in_parentheses_only_group, NeedsParentheses, Parentheses, - Parenthesize, + in_parentheses_only_group, NeedsParentheses, OptionalParentheses, }; use crate::prelude::*; use crate::FormatNodeRule; @@ -87,12 +86,11 @@ impl FormatNodeRule for FormatExprSubscript { impl NeedsParentheses for ExprSubscript { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + { + OptionalParentheses::Never } } } diff --git a/crates/ruff_python_formatter/src/expression/expr_tuple.rs b/crates/ruff_python_formatter/src/expression/expr_tuple.rs index 2fcad273871f0e..e0e5689a9c1551 100644 --- a/crates/ruff_python_formatter/src/expression/expr_tuple.rs +++ b/crates/ruff_python_formatter/src/expression/expr_tuple.rs @@ -1,14 +1,16 @@ +use ruff_text_size::TextRange; +use rustpython_parser::ast::ExprTuple; +use rustpython_parser::ast::{Expr, Ranged}; + +use ruff_formatter::{format_args, write, FormatRuleWithOptions}; +use ruff_python_ast::node::AnyNodeRef; + use crate::builders::parenthesize_if_expands; use crate::comments::{dangling_comments, CommentLinePosition}; use crate::expression::parentheses::{ - default_expression_needs_parentheses, parenthesized, NeedsParentheses, Parentheses, - Parenthesize, + parenthesized, NeedsParentheses, OptionalParentheses, Parentheses, }; use crate::prelude::*; -use ruff_formatter::{format_args, write, FormatRuleWithOptions}; -use ruff_text_size::TextRange; -use rustpython_parser::ast::ExprTuple; -use rustpython_parser::ast::{Expr, Ranged}; #[derive(Eq, PartialEq, Debug, Default)] pub enum TupleParentheses { @@ -148,13 +150,10 @@ impl Format> for ExprSequence<'_> { impl NeedsParentheses for ExprTuple { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => Parentheses::Never, - parentheses => parentheses, - } + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Never } } diff --git a/crates/ruff_python_formatter/src/expression/expr_unary_op.rs b/crates/ruff_python_formatter/src/expression/expr_unary_op.rs index 8e2655d40b10f1..97462c4d7f9b97 100644 --- a/crates/ruff_python_formatter/src/expression/expr_unary_op.rs +++ b/crates/ruff_python_formatter/src/expression/expr_unary_op.rs @@ -1,12 +1,11 @@ use crate::comments::trailing_comments; use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::trivia::{SimpleTokenizer, TokenKind}; use crate::{AsFormat, FormatNodeRule, PyFormatter}; use ruff_formatter::prelude::{hard_line_break, space, text}; use ruff_formatter::{Format, FormatContext, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use ruff_text_size::{TextLen, TextRange}; use rustpython_parser::ast::UnaryOp; use rustpython_parser::ast::{ExprUnaryOp, Ranged}; @@ -70,19 +69,14 @@ impl FormatNodeRule for FormatExprUnaryOp { impl NeedsParentheses for ExprUnaryOp { fn needs_parentheses( &self, - parenthesize: Parenthesize, + _parent: AnyNodeRef, context: &PyFormatContext, - ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, context) { - Parentheses::Optional => { - // We preserve the parentheses of the operand. It should not be necessary to break this expression. - if is_operand_parenthesized(self, context.source()) { - Parentheses::Never - } else { - Parentheses::Optional - } - } - parentheses => parentheses, + ) -> OptionalParentheses { + // We preserve the parentheses of the operand. It should not be necessary to break this expression. + if is_operand_parenthesized(self, context.source()) { + OptionalParentheses::Never + } else { + OptionalParentheses::Multiline } } } diff --git a/crates/ruff_python_formatter/src/expression/expr_yield.rs b/crates/ruff_python_formatter/src/expression/expr_yield.rs index a31c56bfbbc68d..20fabe7d05d458 100644 --- a/crates/ruff_python_formatter/src/expression/expr_yield.rs +++ b/crates/ruff_python_formatter/src/expression/expr_yield.rs @@ -1,9 +1,8 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprYield; #[derive(Default)] @@ -18,9 +17,9 @@ impl FormatNodeRule for FormatExprYield { impl NeedsParentheses for ExprYield { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/expr_yield_from.rs b/crates/ruff_python_formatter/src/expression/expr_yield_from.rs index ae122ded3adfe5..3f2f6b68422cbc 100644 --- a/crates/ruff_python_formatter/src/expression/expr_yield_from.rs +++ b/crates/ruff_python_formatter/src/expression/expr_yield_from.rs @@ -1,9 +1,8 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprYieldFrom; #[derive(Default)] @@ -18,9 +17,9 @@ impl FormatNodeRule for FormatExprYieldFrom { impl NeedsParentheses for ExprYieldFrom { fn needs_parentheses( &self, - parenthesize: Parenthesize, - context: &PyFormatContext, - ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, context) + _parent: AnyNodeRef, + _context: &PyFormatContext, + ) -> OptionalParentheses { + OptionalParentheses::Multiline } } diff --git a/crates/ruff_python_formatter/src/expression/mod.rs b/crates/ruff_python_formatter/src/expression/mod.rs index cfe701d35a9e83..121610b81c52c5 100644 --- a/crates/ruff_python_formatter/src/expression/mod.rs +++ b/crates/ruff_python_formatter/src/expression/mod.rs @@ -1,19 +1,19 @@ +use std::cmp::Ordering; + use rustpython_parser::ast; use rustpython_parser::ast::{Expr, Operator}; -use std::cmp::Ordering; -use crate::builders::parenthesize_if_expands; use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions}; use ruff_python_ast::node::AnyNodeRef; use ruff_python_ast::visitor::preorder::{walk_expr, PreorderVisitor}; +use crate::builders::parenthesize_if_expands; use crate::context::NodeLevel; use crate::expression::expr_tuple::TupleParentheses; use crate::expression::parentheses::{ is_expression_parenthesized, optional_parentheses, parenthesized, NeedsParentheses, - Parentheses, Parenthesize, + OptionalParentheses, Parentheses, Parenthesize, }; -use crate::expression::string::StringLayout; use crate::prelude::*; pub(crate) mod expr_attribute; @@ -46,25 +46,25 @@ pub(crate) mod expr_yield_from; pub(crate) mod parentheses; pub(crate) mod string; -#[derive(Default)] +#[derive(Copy, Clone, PartialEq, Eq, Default)] pub struct FormatExpr { - parenthesize: Parenthesize, + parentheses: Parentheses, } impl FormatRuleWithOptions> for FormatExpr { - type Options = Parenthesize; + type Options = Parentheses; fn with_options(mut self, options: Self::Options) -> Self { - self.parenthesize = options; + self.parentheses = options; self } } impl FormatRule> for FormatExpr { - fn fmt(&self, item: &Expr, f: &mut PyFormatter) -> FormatResult<()> { - let parentheses = item.needs_parentheses(self.parenthesize, f.context()); + fn fmt(&self, expression: &Expr, f: &mut PyFormatter) -> FormatResult<()> { + let parentheses = self.parentheses; - let format_expr = format_with(|f| match item { + let format_expr = format_with(|f| match expression { Expr::BoolOp(expr) => expr.format().with_options(Some(parentheses)).fmt(f), Expr::NamedExpr(expr) => expr.format().fmt(f), Expr::BinOp(expr) => expr.format().with_options(Some(parentheses)).fmt(f), @@ -84,10 +84,7 @@ impl FormatRule> for FormatExpr { Expr::Call(expr) => expr.format().fmt(f), Expr::FormattedValue(expr) => expr.format().fmt(f), Expr::JoinedStr(expr) => expr.format().fmt(f), - Expr::Constant(expr) => expr - .format() - .with_options(StringLayout::Default(Some(parentheses))) - .fmt(f), + Expr::Constant(expr) => expr.format().fmt(f), Expr::Attribute(expr) => expr.format().fmt(f), Expr::Subscript(expr) => expr.format().fmt(f), Expr::Starred(expr) => expr.format().fmt(f), @@ -100,74 +97,136 @@ impl FormatRule> for FormatExpr { Expr::Slice(expr) => expr.format().fmt(f), }); - let result = match parentheses { - Parentheses::Always => parenthesized("(", &format_expr, ")").fmt(f), - // Add optional parentheses. Ignore if the item renders parentheses itself. - Parentheses::Optional => { - if can_omit_optional_parentheses(item, f.context()) { - optional_parentheses(&format_expr).fmt(f) - } else { - parenthesize_if_expands(&format_expr).fmt(f) - } + let parenthesize = match parentheses { + Parentheses::Preserve => { + is_expression_parenthesized(AnyNodeRef::from(expression), f.context().source()) } - Parentheses::Custom | Parentheses::Never => { - let saved_level = f.context().node_level(); + Parentheses::Always => true, + Parentheses::Never => false, + }; - let new_level = match saved_level { - NodeLevel::TopLevel | NodeLevel::CompoundStatement => { - NodeLevel::Expression(None) - } - level @ (NodeLevel::Expression(_) | NodeLevel::ParenthesizedExpression) => { - level - } - }; + if parenthesize { + parenthesized("(", &format_expr, ")").fmt(f) + } else { + let saved_level = match f.context().node_level() { + saved_level @ (NodeLevel::TopLevel | NodeLevel::CompoundStatement) => { + f.context_mut().set_node_level(NodeLevel::Expression(None)); + Some(saved_level) + } + NodeLevel::Expression(_) | NodeLevel::ParenthesizedExpression => None, + }; - f.context_mut().set_node_level(new_level); + let result = Format::fmt(&format_expr, f); - let result = Format::fmt(&format_expr, f); + if let Some(saved_level) = saved_level { f.context_mut().set_node_level(saved_level); - result } + + result + } + } +} + +/// Wraps an expression in an optional parentheses except if its [`NeedsParentheses::needs_parentheses`] implementation +/// indicates that it is okay to omit the parentheses. For example, parentheses can always be omitted for lists, +/// because they already bring their own parentheses. +pub(crate) fn maybe_parenthesize_expression<'a, T>( + expression: &'a Expr, + parent: T, + parenthesize: Parenthesize, +) -> MaybeParenthesizeExpression<'a> +where + T: Into>, +{ + MaybeParenthesizeExpression { + expression, + parent: parent.into(), + parenthesize, + } +} + +pub(crate) struct MaybeParenthesizeExpression<'a> { + expression: &'a Expr, + parent: AnyNodeRef<'a>, + parenthesize: Parenthesize, +} + +impl Format> for MaybeParenthesizeExpression<'_> { + fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { + let MaybeParenthesizeExpression { + expression, + parent, + parenthesize, + } = self; + + let parenthesize = match parenthesize { + Parenthesize::Optional => { + is_expression_parenthesized(AnyNodeRef::from(*expression), f.context().source()) + } + Parenthesize::IfBreaks => false, }; - result + let parentheses = + if parenthesize || f.context().comments().has_leading_comments(*expression) { + OptionalParentheses::Always + } else { + expression.needs_parentheses(*parent, f.context()) + }; + + match parentheses { + OptionalParentheses::Multiline => { + if can_omit_optional_parentheses(expression, f.context()) { + optional_parentheses(&expression.format().with_options(Parentheses::Never)) + .fmt(f) + } else { + parenthesize_if_expands(&expression.format().with_options(Parentheses::Never)) + .fmt(f) + } + } + OptionalParentheses::Always => { + expression.format().with_options(Parentheses::Always).fmt(f) + } + OptionalParentheses::Never => { + expression.format().with_options(Parentheses::Never).fmt(f) + } + } } } impl NeedsParentheses for Expr { fn needs_parentheses( &self, - parenthesize: Parenthesize, + parent: AnyNodeRef, context: &PyFormatContext, - ) -> Parentheses { + ) -> OptionalParentheses { match self { - Expr::BoolOp(expr) => expr.needs_parentheses(parenthesize, context), - Expr::NamedExpr(expr) => expr.needs_parentheses(parenthesize, context), - Expr::BinOp(expr) => expr.needs_parentheses(parenthesize, context), - Expr::UnaryOp(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Lambda(expr) => expr.needs_parentheses(parenthesize, context), - Expr::IfExp(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Dict(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Set(expr) => expr.needs_parentheses(parenthesize, context), - Expr::ListComp(expr) => expr.needs_parentheses(parenthesize, context), - Expr::SetComp(expr) => expr.needs_parentheses(parenthesize, context), - Expr::DictComp(expr) => expr.needs_parentheses(parenthesize, context), - Expr::GeneratorExp(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Await(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Yield(expr) => expr.needs_parentheses(parenthesize, context), - Expr::YieldFrom(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Compare(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Call(expr) => expr.needs_parentheses(parenthesize, context), - Expr::FormattedValue(expr) => expr.needs_parentheses(parenthesize, context), - Expr::JoinedStr(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Constant(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Attribute(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Subscript(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Starred(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Name(expr) => expr.needs_parentheses(parenthesize, context), - Expr::List(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Tuple(expr) => expr.needs_parentheses(parenthesize, context), - Expr::Slice(expr) => expr.needs_parentheses(parenthesize, context), + Expr::BoolOp(expr) => expr.needs_parentheses(parent, context), + Expr::NamedExpr(expr) => expr.needs_parentheses(parent, context), + Expr::BinOp(expr) => expr.needs_parentheses(parent, context), + Expr::UnaryOp(expr) => expr.needs_parentheses(parent, context), + Expr::Lambda(expr) => expr.needs_parentheses(parent, context), + Expr::IfExp(expr) => expr.needs_parentheses(parent, context), + Expr::Dict(expr) => expr.needs_parentheses(parent, context), + Expr::Set(expr) => expr.needs_parentheses(parent, context), + Expr::ListComp(expr) => expr.needs_parentheses(parent, context), + Expr::SetComp(expr) => expr.needs_parentheses(parent, context), + Expr::DictComp(expr) => expr.needs_parentheses(parent, context), + Expr::GeneratorExp(expr) => expr.needs_parentheses(parent, context), + Expr::Await(expr) => expr.needs_parentheses(parent, context), + Expr::Yield(expr) => expr.needs_parentheses(parent, context), + Expr::YieldFrom(expr) => expr.needs_parentheses(parent, context), + Expr::Compare(expr) => expr.needs_parentheses(parent, context), + Expr::Call(expr) => expr.needs_parentheses(parent, context), + Expr::FormattedValue(expr) => expr.needs_parentheses(parent, context), + Expr::JoinedStr(expr) => expr.needs_parentheses(parent, context), + Expr::Constant(expr) => expr.needs_parentheses(parent, context), + Expr::Attribute(expr) => expr.needs_parentheses(parent, context), + Expr::Subscript(expr) => expr.needs_parentheses(parent, context), + Expr::Starred(expr) => expr.needs_parentheses(parent, context), + Expr::Name(expr) => expr.needs_parentheses(parent, context), + Expr::List(expr) => expr.needs_parentheses(parent, context), + Expr::Tuple(expr) => expr.needs_parentheses(parent, context), + Expr::Slice(expr) => expr.needs_parentheses(parent, context), } } } diff --git a/crates/ruff_python_formatter/src/expression/parentheses.rs b/crates/ruff_python_formatter/src/expression/parentheses.rs index 2d387d5f468d96..80405ebcf2c1f0 100644 --- a/crates/ruff_python_formatter/src/expression/parentheses.rs +++ b/crates/ruff_python_formatter/src/expression/parentheses.rs @@ -6,101 +6,50 @@ use ruff_formatter::{format_args, Argument, Arguments}; use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::Ranged; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(crate) enum OptionalParentheses { + /// Add parentheses if the expression expands over multiple lines + Multiline, + + /// Always set parentheses regardless if the expression breaks or if they were + /// present in the source. + Always, + + /// Never add parentheses + Never, +} + pub(crate) trait NeedsParentheses { + /// Determines if this object needs optional parentheses or if it is safe to omit the parentheses. fn needs_parentheses( &self, - parenthesize: Parenthesize, + parent: AnyNodeRef, context: &PyFormatContext, - ) -> Parentheses; -} - -pub(super) fn default_expression_needs_parentheses( - node: AnyNodeRef, - parenthesize: Parenthesize, - context: &PyFormatContext, -) -> Parentheses { - debug_assert!( - node.is_expression(), - "Should only be called for expressions" - ); - - #[allow(clippy::if_same_then_else)] - if parenthesize.is_always() { - Parentheses::Always - } else if parenthesize.is_never() { - Parentheses::Never - } - // `Optional` or `Preserve` and expression has parentheses in source code. - else if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, context.source()) { - Parentheses::Always - } - // `Optional` or `IfBreaks`: Add parentheses if the expression doesn't fit on a line but enforce - // parentheses if the expression has leading comments - else if !parenthesize.is_preserve() { - if context.comments().has_leading_comments(node) { - Parentheses::Always - } else { - Parentheses::Optional - } - } else { - //`Preserve` and expression has no parentheses in the source code - Parentheses::Never - } + ) -> OptionalParentheses; } /// Configures if the expression should be parenthesized. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub enum Parenthesize { - /// Parenthesize the expression if it has parenthesis in the source. - #[default] - Preserve, - +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) enum Parenthesize { /// Parenthesizes the expression if it doesn't fit on a line OR if the expression is parenthesized in the source code. Optional, /// Parenthesizes the expression only if it doesn't fit on a line. IfBreaks, - - /// Always adds parentheses - Always, - - /// Never adds parentheses. Parentheses are handled by the caller. - Never, -} - -impl Parenthesize { - pub(crate) const fn is_always(self) -> bool { - matches!(self, Parenthesize::Always) - } - - pub(crate) const fn is_never(self) -> bool { - matches!(self, Parenthesize::Never) - } - - pub(crate) const fn is_if_breaks(self) -> bool { - matches!(self, Parenthesize::IfBreaks) - } - - pub(crate) const fn is_preserve(self) -> bool { - matches!(self, Parenthesize::Preserve) - } } /// Whether it is necessary to add parentheses around an expression. /// This is different from [`Parenthesize`] in that it is the resolved representation: It takes into account /// whether there are parentheses in the source code or not. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] pub enum Parentheses { + #[default] + Preserve, + /// Always set parentheses regardless if the expression breaks or if they were /// present in the source. Always, - /// Only add parentheses when necessary because the expression breaks over multiple lines. - Optional, - - /// Custom handling by the node's formatter implementation - Custom, - /// Never add parentheses Never, } @@ -182,20 +131,20 @@ impl<'ast> Format> for FormatParenthesized<'_, 'ast> { /// a parentheses (`()`, `[]`, `{}`). pub(crate) fn optional_parentheses<'content, 'ast, Content>( content: &'content Content, -) -> OptionalParentheses<'content, 'ast> +) -> FormatOptionalParentheses<'content, 'ast> where Content: Format>, { - OptionalParentheses { + FormatOptionalParentheses { content: Argument::new(content), } } -pub(crate) struct OptionalParentheses<'content, 'ast> { +pub(crate) struct FormatOptionalParentheses<'content, 'ast> { content: Argument<'content, PyFormatContext<'ast>>, } -impl<'ast> Format> for OptionalParentheses<'_, 'ast> { +impl<'ast> Format> for FormatOptionalParentheses<'_, 'ast> { fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { let saved_level = f.context().node_level(); diff --git a/crates/ruff_python_formatter/src/expression/string.rs b/crates/ruff_python_formatter/src/expression/string.rs index 34ffebae7d3885..fab95539f26c57 100644 --- a/crates/ruff_python_formatter/src/expression/string.rs +++ b/crates/ruff_python_formatter/src/expression/string.rs @@ -1,41 +1,27 @@ -use crate::builders::parenthesize_if_expands; -use crate::comments::{leading_comments, trailing_comments}; -use crate::expression::parentheses::Parentheses; -use crate::prelude::*; -use crate::QuoteStyle; +use std::borrow::Cow; + use bitflags::bitflags; -use ruff_formatter::{format_args, write, FormatError}; -use ruff_python_ast::str::is_implicit_concatenation; use ruff_text_size::{TextLen, TextRange, TextSize}; use rustpython_parser::ast::{ExprConstant, Ranged}; use rustpython_parser::lexer::{lex_starts_at, LexicalError, LexicalErrorType}; use rustpython_parser::{Mode, Tok}; -use std::borrow::Cow; - -#[derive(Copy, Clone, Debug)] -pub enum StringLayout { - Default(Option), - /// Enforces that implicit continuation strings are printed on a single line even if they exceed - /// the configured line width. - Flat, -} +use ruff_formatter::{format_args, write, FormatError}; +use ruff_python_ast::str::is_implicit_concatenation; -impl Default for StringLayout { - fn default() -> Self { - Self::Default(None) - } -} +use crate::comments::{leading_comments, trailing_comments}; +use crate::expression::parentheses::in_parentheses_only_group; +use crate::prelude::*; +use crate::QuoteStyle; pub(super) struct FormatString<'a> { constant: &'a ExprConstant, - layout: StringLayout, } impl<'a> FormatString<'a> { - pub(super) fn new(constant: &'a ExprConstant, layout: StringLayout) -> Self { + pub(super) fn new(constant: &'a ExprConstant) -> Self { debug_assert!(constant.value.is_str()); - Self { constant, layout } + Self { constant } } } @@ -45,13 +31,7 @@ impl<'a> Format> for FormatString<'a> { let string_content = f.context().locator().slice(string_range); if is_implicit_concatenation(string_content) { - let format_continuation = FormatStringContinuation::new(self.constant, self.layout); - - if let StringLayout::Default(Some(Parentheses::Custom)) = self.layout { - parenthesize_if_expands(&format_continuation).fmt(f) - } else { - format_continuation.fmt(f) - } + in_parentheses_only_group(&FormatStringContinuation::new(self.constant)).fmt(f) } else { FormatStringPart::new(string_range).fmt(f) } @@ -60,13 +40,12 @@ impl<'a> Format> for FormatString<'a> { struct FormatStringContinuation<'a> { constant: &'a ExprConstant, - layout: StringLayout, } impl<'a> FormatStringContinuation<'a> { - fn new(constant: &'a ExprConstant, layout: StringLayout) -> Self { + fn new(constant: &'a ExprConstant) -> Self { debug_assert!(constant.value.is_str()); - Self { constant, layout } + Self { constant } } } @@ -85,12 +64,7 @@ impl Format> for FormatStringContinuation<'_> { // because this is a black preview style. let lexer = lex_starts_at(string_content, Mode::Expression, string_range.start()); - let separator = format_with(|f| match self.layout { - StringLayout::Default(_) => soft_line_break_or_space().fmt(f), - StringLayout::Flat => space().fmt(f), - }); - - let mut joiner = f.join_with(separator); + let mut joiner = f.join_with(soft_line_break_or_space()); for token in lexer { let (token, token_range) = match token { @@ -220,7 +194,7 @@ impl Format> for FormatStringPart { bitflags! { #[derive(Copy, Clone, Debug)] - struct StringPrefix: u8 { + pub(super) struct StringPrefix: u8 { const UNICODE = 0b0000_0001; /// `r"test"` const RAW = 0b0000_0010; @@ -232,7 +206,7 @@ bitflags! { } impl StringPrefix { - fn parse(input: &str) -> StringPrefix { + pub(super) fn parse(input: &str) -> StringPrefix { let chars = input.chars(); let mut prefix = StringPrefix::empty(); @@ -257,7 +231,7 @@ impl StringPrefix { prefix } - const fn text_len(self) -> TextSize { + pub(super) const fn text_len(self) -> TextSize { TextSize::new(self.bits().count_ones()) } } @@ -383,13 +357,13 @@ fn preferred_quotes( } #[derive(Copy, Clone, Debug)] -struct StringQuotes { +pub(super) struct StringQuotes { triple: bool, style: QuoteStyle, } impl StringQuotes { - fn parse(input: &str) -> Option { + pub(super) fn parse(input: &str) -> Option { let mut chars = input.chars(); let quote_char = chars.next()?; @@ -400,6 +374,10 @@ impl StringQuotes { Some(Self { triple, style }) } + pub(super) const fn is_triple(self) -> bool { + self.triple + } + const fn text_len(self) -> TextSize { if self.triple { TextSize::new(3) diff --git a/crates/ruff_python_formatter/src/other/decorator.rs b/crates/ruff_python_formatter/src/other/decorator.rs index 3ebdca4e703844..88b1a92d65be5f 100644 --- a/crates/ruff_python_formatter/src/other/decorator.rs +++ b/crates/ruff_python_formatter/src/other/decorator.rs @@ -1,3 +1,4 @@ +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::FormatNodeRule; @@ -18,7 +19,7 @@ impl FormatNodeRule for FormatDecorator { f, [ text("@"), - expression.format().with_options(Parenthesize::Optional) + maybe_parenthesize_expression(expression, item, Parenthesize::Optional) ] ) } diff --git a/crates/ruff_python_formatter/src/other/except_handler_except_handler.rs b/crates/ruff_python_formatter/src/other/except_handler_except_handler.rs index 66ab84be591974..a66fd39963a168 100644 --- a/crates/ruff_python_formatter/src/other/except_handler_except_handler.rs +++ b/crates/ruff_python_formatter/src/other/except_handler_except_handler.rs @@ -1,4 +1,5 @@ use crate::comments::trailing_comments; +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::{FormatNodeRule, PyFormatter}; @@ -60,7 +61,10 @@ impl FormatNodeRule for FormatExceptHandlerExceptHan if let Some(type_) = type_ { write!( f, - [space(), type_.format().with_options(Parenthesize::IfBreaks)] + [ + space(), + maybe_parenthesize_expression(type_, item, Parenthesize::IfBreaks) + ] )?; if let Some(name) = name { write!(f, [space(), text("as"), space(), name.format()])?; diff --git a/crates/ruff_python_formatter/src/other/with_item.rs b/crates/ruff_python_formatter/src/other/with_item.rs index 0c3f696e276978..1004639f15f289 100644 --- a/crates/ruff_python_formatter/src/other/with_item.rs +++ b/crates/ruff_python_formatter/src/other/with_item.rs @@ -1,3 +1,4 @@ +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::{FormatNodeRule, PyFormatter}; @@ -18,7 +19,11 @@ impl FormatNodeRule for FormatWithItem { let inner = format_with(|f| { write!( f, - [context_expr.format().with_options(Parenthesize::IfBreaks)] + [maybe_parenthesize_expression( + context_expr, + item, + Parenthesize::IfBreaks + )] )?; if let Some(optional_vars) = optional_vars { write!(f, [space(), text("as"), space(), optional_vars.format()])?; diff --git a/crates/ruff_python_formatter/src/statement/stmt_assign.rs b/crates/ruff_python_formatter/src/statement/stmt_assign.rs index e8f1175a3e1790..36a53891b5dc41 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_assign.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_assign.rs @@ -2,6 +2,7 @@ use rustpython_parser::ast::StmtAssign; use ruff_formatter::write; +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::FormatNodeRule; @@ -26,6 +27,13 @@ impl FormatNodeRule for FormatStmtAssign { write!(f, [target.format(), space(), text("="), space()])?; } - write!(f, [value.format().with_options(Parenthesize::IfBreaks)]) + write!( + f, + [maybe_parenthesize_expression( + value, + item, + Parenthesize::IfBreaks + )] + ) } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_async_function_def.rs b/crates/ruff_python_formatter/src/statement/stmt_async_function_def.rs index a0e28aa381d336..1a001d91b7da2a 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_async_function_def.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_async_function_def.rs @@ -1,7 +1,9 @@ +use rustpython_parser::ast::StmtAsyncFunctionDef; + +use ruff_python_ast::function::AnyFunctionDefinition; + use crate::prelude::*; use crate::FormatNodeRule; -use ruff_python_ast::function::AnyFunctionDefinition; -use rustpython_parser::ast::StmtAsyncFunctionDef; #[derive(Default)] pub struct FormatStmtAsyncFunctionDef; diff --git a/crates/ruff_python_formatter/src/statement/stmt_aug_assign.rs b/crates/ruff_python_formatter/src/statement/stmt_aug_assign.rs index c09c58ed62e984..6682ef547b562e 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_aug_assign.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_aug_assign.rs @@ -1,3 +1,4 @@ +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::{AsFormat, FormatNodeRule, PyFormatter}; use ruff_formatter::prelude::{space, text}; @@ -23,7 +24,7 @@ impl FormatNodeRule for FormatStmtAugAssign { op.format(), text("="), space(), - value.format().with_options(Parenthesize::IfBreaks) + maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks) ] ) } diff --git a/crates/ruff_python_formatter/src/statement/stmt_class_def.rs b/crates/ruff_python_formatter/src/statement/stmt_class_def.rs index ab6d4be00e4906..bd903d8b398d76 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_class_def.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_class_def.rs @@ -1,5 +1,6 @@ use crate::comments::trailing_comments; -use crate::expression::parentheses::Parenthesize; + +use crate::expression::parentheses::Parentheses; use crate::prelude::*; use crate::trivia::{SimpleTokenizer, TokenKind}; use ruff_formatter::{format_args, write}; @@ -100,13 +101,13 @@ impl Format> for FormatInheritanceClause<'_> { .count(); // Ignore the first parentheses count - let parenthesize = if left_paren_count > 1 { - Parenthesize::Always + let parentheses = if left_paren_count > 1 { + Parentheses::Always } else { - Parenthesize::Never + Parentheses::Never }; - joiner.entry(first, &first.format().with_options(parenthesize)); + joiner.entry(first, &first.format().with_options(parentheses)); joiner.nodes(rest.iter()); } diff --git a/crates/ruff_python_formatter/src/statement/stmt_delete.rs b/crates/ruff_python_formatter/src/statement/stmt_delete.rs index 3b5d3225fcc3ac..a61e86c028349e 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_delete.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_delete.rs @@ -1,7 +1,8 @@ use crate::builders::{parenthesize_if_expands, PyFormatterExtensions}; use crate::comments::dangling_node_comments; +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; -use crate::{AsFormat, FormatNodeRule, PyFormatter}; +use crate::{FormatNodeRule, PyFormatter}; use ruff_formatter::prelude::{block_indent, format_with, space, text}; use ruff_formatter::{write, Buffer, Format, FormatResult}; use rustpython_parser::ast::{Ranged, StmtDelete}; @@ -32,7 +33,14 @@ impl FormatNodeRule for FormatStmtDelete { ) } [single] => { - write!(f, [single.format().with_options(Parenthesize::IfBreaks)]) + write!( + f, + [maybe_parenthesize_expression( + single, + item, + Parenthesize::IfBreaks + )] + ) } targets => { let item = format_with(|f| { diff --git a/crates/ruff_python_formatter/src/statement/stmt_expr.rs b/crates/ruff_python_formatter/src/statement/stmt_expr.rs index 4e415f77cf8e8c..0eb13b24931936 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_expr.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_expr.rs @@ -1,8 +1,9 @@ -use crate::expression::parentheses::{is_expression_parenthesized, Parenthesize}; -use crate::expression::string::StringLayout; +use rustpython_parser::ast::StmtExpr; + +use crate::expression::maybe_parenthesize_expression; +use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::FormatNodeRule; -use rustpython_parser::ast::StmtExpr; #[derive(Default)] pub struct FormatStmtExpr; @@ -11,14 +12,6 @@ impl FormatNodeRule for FormatStmtExpr { fn fmt_fields(&self, item: &StmtExpr, f: &mut PyFormatter) -> FormatResult<()> { let StmtExpr { value, .. } = item; - if let Some(constant) = value.as_constant_expr() { - if constant.value.is_str() - && !is_expression_parenthesized(value.as_ref().into(), f.context().source()) - { - return constant.format().with_options(StringLayout::Flat).fmt(f); - } - } - - value.format().with_options(Parenthesize::Optional).fmt(f) + maybe_parenthesize_expression(value, item, Parenthesize::Optional).fmt(f) } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_for.rs b/crates/ruff_python_formatter/src/statement/stmt_for.rs index 528e06f1526392..b03283cabc257b 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_for.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_for.rs @@ -1,5 +1,6 @@ use crate::comments::{leading_alternate_branch_comments, trailing_comments}; use crate::expression::expr_tuple::TupleParentheses; +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::{FormatNodeRule, PyFormatter}; @@ -17,7 +18,7 @@ impl Format> for ExprTupleWithoutParentheses<'_> { .format() .with_options(TupleParentheses::StripInsideForLoop) .fmt(f), - other => other.format().with_options(Parenthesize::IfBreaks).fmt(f), + other => maybe_parenthesize_expression(other, self.0, Parenthesize::IfBreaks).fmt(f), } } } @@ -54,7 +55,7 @@ impl FormatNodeRule for FormatStmtFor { space(), text("in"), space(), - iter.format().with_options(Parenthesize::IfBreaks), + maybe_parenthesize_expression(iter, item, Parenthesize::IfBreaks), text(":"), trailing_comments(trailing_condition_comments), block_indent(&body.format()) diff --git a/crates/ruff_python_formatter/src/statement/stmt_function_def.rs b/crates/ruff_python_formatter/src/statement/stmt_function_def.rs index a0079b49755063..69f370e7c10e32 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_function_def.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_function_def.rs @@ -1,12 +1,14 @@ +use rustpython_parser::ast::{Ranged, StmtFunctionDef}; + +use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule}; +use ruff_python_ast::function::AnyFunctionDefinition; + use crate::comments::{leading_comments, trailing_comments}; use crate::context::NodeLevel; -use crate::expression::parentheses::{optional_parentheses, Parenthesize}; +use crate::expression::parentheses::{optional_parentheses, Parentheses}; use crate::prelude::*; use crate::trivia::{lines_after, skip_trailing_trivia}; use crate::FormatNodeRule; -use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule}; -use ruff_python_ast::function::AnyFunctionDefinition; -use rustpython_parser::ast::{Ranged, StmtFunctionDef}; #[derive(Default)] pub struct FormatStmtFunctionDef; @@ -98,7 +100,7 @@ impl FormatRule, PyFormatContext<'_>> for FormatAnyFun text("->"), space(), optional_parentheses( - &return_annotation.format().with_options(Parenthesize::Never) + &return_annotation.format().with_options(Parentheses::Never) ) ] )?; diff --git a/crates/ruff_python_formatter/src/statement/stmt_if.rs b/crates/ruff_python_formatter/src/statement/stmt_if.rs index 5611a66ac322b0..ab249f228e1ede 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_if.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_if.rs @@ -1,4 +1,5 @@ use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment}; +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::FormatNodeRule; @@ -48,7 +49,7 @@ impl FormatNodeRule for FormatStmtIf { [ text(current.keyword()), space(), - test.format().with_options(Parenthesize::IfBreaks), + maybe_parenthesize_expression(test, current_statement, Parenthesize::IfBreaks), text(":"), trailing_comments(if_trailing_comments), block_indent(&body.format()) diff --git a/crates/ruff_python_formatter/src/statement/stmt_raise.rs b/crates/ruff_python_formatter/src/statement/stmt_raise.rs index 5159d3ab3556ff..78c8591b5ae67f 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_raise.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_raise.rs @@ -1,8 +1,9 @@ use crate::expression::parentheses::Parenthesize; -use crate::{AsFormat, FormatNodeRule, PyFormatter}; +use crate::{FormatNodeRule, PyFormatter}; use ruff_formatter::prelude::{space, text}; use ruff_formatter::{write, Buffer, Format, FormatResult}; +use crate::expression::maybe_parenthesize_expression; use rustpython_parser::ast::StmtRaise; #[derive(Default)] @@ -21,7 +22,10 @@ impl FormatNodeRule for FormatStmtRaise { if let Some(value) = exc { write!( f, - [space(), value.format().with_options(Parenthesize::Optional)] + [ + space(), + maybe_parenthesize_expression(value, item, Parenthesize::Optional) + ] )?; } @@ -32,7 +36,7 @@ impl FormatNodeRule for FormatStmtRaise { space(), text("from"), space(), - value.format().with_options(Parenthesize::Optional) + maybe_parenthesize_expression(value, item, Parenthesize::Optional) ] )?; } diff --git a/crates/ruff_python_formatter/src/statement/stmt_return.rs b/crates/ruff_python_formatter/src/statement/stmt_return.rs index db9b28b0d4f808..c298379c475a33 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_return.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_return.rs @@ -1,5 +1,6 @@ +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; -use crate::{AsFormat, FormatNodeRule, PyFormatter}; +use crate::{FormatNodeRule, PyFormatter}; use ruff_formatter::prelude::{space, text}; use ruff_formatter::{write, Buffer, Format, FormatResult}; use rustpython_parser::ast::StmtReturn; @@ -16,7 +17,7 @@ impl FormatNodeRule for FormatStmtReturn { [ text("return"), space(), - value.format().with_options(Parenthesize::IfBreaks) + maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks) ] ) } else { diff --git a/crates/ruff_python_formatter/src/statement/stmt_while.rs b/crates/ruff_python_formatter/src/statement/stmt_while.rs index 758f1d840e5fe9..d89e02fb8537f3 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_while.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_while.rs @@ -1,4 +1,5 @@ use crate::comments::{leading_alternate_branch_comments, trailing_comments}; +use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::FormatNodeRule; @@ -33,7 +34,7 @@ impl FormatNodeRule for FormatStmtWhile { [ text("while"), space(), - test.format().with_options(Parenthesize::IfBreaks), + maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks), text(":"), trailing_comments(trailing_condition_comments), block_indent(&body.format()) diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap index 6355a154f54803..5aca69b3a68366 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap @@ -304,17 +304,6 @@ long_unmergable_string_with_pragma = ( ```diff --- Black +++ Ruff -@@ -70,8 +70,8 @@ - bad_split3 = ( - "What if we have inline comments on " # First Comment - "each line of a bad split? In that " # Second Comment -- "case, we should just leave it alone." # Third Comment --) -+ "case, we should just leave it alone." -+) # Third Comment - - bad_split_func1( - "But what should happen when code has already " @@ -143,9 +143,9 @@ ) ) @@ -458,8 +447,8 @@ bad_split2 = ( bad_split3 = ( "What if we have inline comments on " # First Comment "each line of a bad split? In that " # Second Comment - "case, we should just leave it alone." -) # Third Comment + "case, we should just leave it alone." # Third Comment +) bad_split_func1( "But what should happen when code has already " diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments4.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments4.py.snap index 7a1948330ffe6a..b7752423a8e470 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments4.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments4.py.snap @@ -122,11 +122,10 @@ def foo3(list_a, list_b): - .order_by(User.created_at.desc()) - .with_for_update(key_share=True) - .all() -- ) -+ filter -+ )(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))).order_by( -+ User.created_at.desc() -+ ).with_for_update(key_share=True).all() ++ filter(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))).order_by( ++ User.created_at.desc() ++ ).with_for_update(key_share=True).all() + ) return results @@ -224,10 +223,10 @@ def foo(list_a, list_b): db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) ).filter(User.xyz.is_(None)). # Another comment about the filtering on is_quux goes here. - filter - )(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))).order_by( - User.created_at.desc() - ).with_for_update(key_share=True).all() + filter(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))).order_by( + User.created_at.desc() + ).with_for_update(key_share=True).all() + ) return results diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap index 4f4798dc4292e2..ad164bd6c6c054 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap @@ -398,10 +398,11 @@ last_call() (*starred,) { "id": "1", -@@ -208,24 +208,14 @@ +@@ -207,25 +207,15 @@ + ) what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( vars_to_remove - ) +-) -result = ( - session.query(models.Customer.id) - .filter( @@ -409,7 +410,7 @@ last_call() - ) - .order_by(models.Customer.id.asc()) - .all() --) + ) -result = ( - session.query(models.Customer.id) - .filter( @@ -446,7 +447,7 @@ last_call() async def f(): -@@ -248,18 +238,22 @@ +@@ -248,18 +238,20 @@ print(*[] or [1]) @@ -471,13 +472,11 @@ last_call() for y in (): ... -for z in (i for i in (1, 2, 3)): -+for ( -+ z -+) in (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []): ++for z in (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []): ... for i in call(): ... -@@ -328,13 +322,18 @@ +@@ -328,13 +320,18 @@ ): return True if ( @@ -499,7 +498,7 @@ last_call() ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n ): return True -@@ -342,7 +341,8 @@ +@@ -342,7 +339,8 @@ ~aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e @@ -767,9 +766,7 @@ for (x,) in (1,), (2,), (3,): ... for y in (): ... -for ( - z -) in (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []): +for z in (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []): ... for i in call(): ... diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff5.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff5.py.snap index 9cd5e7ed31e6e3..99e98844ffad43 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff5.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff5.py.snap @@ -96,18 +96,21 @@ elif unformatted: ```diff --- Black +++ Ruff -@@ -5,8 +5,8 @@ +@@ -3,10 +3,9 @@ + entry_points={ + # fmt: off "console_scripts": [ - "foo-bar" - "=foo.bar.:main", +- "foo-bar" +- "=foo.bar.:main", - # fmt: on - ] # Includes an formatted indentation. ++ "foo-bar" "=foo.bar.:main", + # fmt: on + ] # Includes an formatted indentation. }, ) -@@ -27,7 +27,7 @@ +@@ -27,7 +26,7 @@ # Regression test for https://github.com/psf/black/issues/3026. def test_func(): # yapf: disable @@ -116,7 +119,7 @@ elif unformatted: return True # yapf: enable elif b: -@@ -39,10 +39,10 @@ +@@ -39,10 +38,10 @@ # Regression test for https://github.com/psf/black/issues/2567. if True: # fmt: off @@ -131,7 +134,7 @@ elif unformatted: else: print("This will be formatted") -@@ -52,14 +52,12 @@ +@@ -52,14 +51,12 @@ async def call(param): if param: # fmt: off @@ -149,7 +152,7 @@ elif unformatted: print("This will be formatted") -@@ -68,13 +66,13 @@ +@@ -68,13 +65,13 @@ class Named(t.Protocol): # fmt: off @property @@ -165,7 +168,7 @@ elif unformatted: # fmt: on -@@ -82,6 +80,6 @@ +@@ -82,6 +79,6 @@ if x: return x # fmt: off @@ -183,8 +186,7 @@ setup( entry_points={ # fmt: off "console_scripts": [ - "foo-bar" - "=foo.bar.:main", + "foo-bar" "=foo.bar.:main", # fmt: on ] # Includes an formatted indentation. }, diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap index 9dea01bb948631..890bfd41cb6130 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap @@ -476,9 +476,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) - {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} -): +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 diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap index 53afe8b2a34c0e..72128eca3aa84d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap @@ -203,7 +203,20 @@ String \"\"\" "Let's" "start" "with" "a" "simple" "example" -"Let's" "start" "with" "a" "simple" "example" "now repeat after me:" "I am confident" "I am confident" "I am confident" "I am confident" "I am confident" +( + "Let's" + "start" + "with" + "a" + "simple" + "example" + "now repeat after me:" + "I am confident" + "I am confident" + "I am confident" + "I am confident" + "I am confident" +) ( "Let's" @@ -351,7 +364,20 @@ String \"\"\" "Let's" 'start' 'with' 'a' 'simple' 'example' -"Let's" 'start' 'with' 'a' 'simple' 'example' 'now repeat after me:' 'I am confident' 'I am confident' 'I am confident' 'I am confident' 'I am confident' +( + "Let's" + 'start' + 'with' + 'a' + 'simple' + 'example' + 'now repeat after me:' + 'I am confident' + 'I am confident' + 'I am confident' + 'I am confident' + 'I am confident' +) ( "Let's" diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__for.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__for.py.snap index b350ab75f498bb..637372d81f9568 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__for.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__for.py.snap @@ -55,11 +55,7 @@ else: # trailing else comment # trailing else body comment -for ( - aVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn -) in ( - anotherVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn -): # trailing comment +for aVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn in anotherVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn: # trailing comment pass else: diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__raise.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__raise.py.snap index f9e18087589d42..a90849f43ba083 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__raise.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__raise.py.snap @@ -105,9 +105,7 @@ raise a from OsError( ) # some comment -raise a from ( - aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa -) # some comment +raise a from aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa # some comment # some comment raise OsError( diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__while.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__while.py.snap index 9014bcdd531a31..fdc99df9a65261 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__while.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__while.py.snap @@ -51,9 +51,7 @@ else: # trailing else comment # trailing else body comment -while ( - aVeryLongConditionThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn -): # trailing comment +while aVeryLongConditionThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn: # trailing comment pass else: diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap index 9f1f6eded7854c..2d133fe87be6be 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap @@ -82,18 +82,14 @@ with ( with ( - ( - a # a # comma - ), + a, # a # comma b, # c ): # colon ... with ( - ( - a # a # as - ) as b, # b # comma + a as b, # a # as # b # comma c, # c ): # colon ... # body