Format Attribute Expression (#5259)

This commit is contained in:
Micha Reiser 2023-06-21 23:33:53 +02:00 committed by GitHub
parent 341b12d918
commit ccf34aae8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 394 additions and 329 deletions

View file

@ -1,9 +1,9 @@
use crate::comments::Comments;
use crate::comments::{leading_comments, trailing_comments, Comments};
use crate::expression::parentheses::{
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
};
use crate::prelude::*;
use crate::{not_yet_implemented_custom_text, FormatNodeRule};
use crate::FormatNodeRule;
use ruff_formatter::write;
use rustpython_parser::ast::{Constant, Expr, ExprAttribute, ExprConstant};
@ -15,11 +15,11 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
let ExprAttribute {
value,
range: _,
attr: _,
attr,
ctx: _,
} = item;
let requires_space = matches!(
let needs_parentheses = matches!(
value.as_ref(),
Expr::Constant(ExprConstant {
value: Constant::Int(_) | Constant::Float(_),
@ -27,16 +27,45 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
})
);
if needs_parentheses {
value.format().with_options(Parenthesize::Always).fmt(f)?;
} else {
value.format().fmt(f)?;
}
let comments = f.context().comments().clone();
if comments.has_trailing_own_line_comments(value.as_ref()) {
hard_line_break().fmt(f)?;
}
let dangling_comments = comments.dangling_comments(item);
let leading_attribute_comments_start =
dangling_comments.partition_point(|comment| comment.line_position().is_end_of_line());
let (trailing_dot_comments, leading_attribute_comments) =
dangling_comments.split_at(leading_attribute_comments_start);
write!(
f,
[
item.value.format(),
requires_space.then_some(space()),
text("."),
not_yet_implemented_custom_text("NOT_IMPLEMENTED_attr")
trailing_comments(trailing_dot_comments),
(!leading_attribute_comments.is_empty()).then_some(hard_line_break()),
leading_comments(leading_attribute_comments),
attr.format()
]
)
}
fn fmt_dangling_comments(
&self,
_node: &ExprAttribute,
_f: &mut PyFormatter,
) -> FormatResult<()> {
// handle in `fmt_fields`
Ok(())
}
}
impl NeedsParentheses for ExprAttribute {
@ -46,6 +75,9 @@ impl NeedsParentheses for ExprAttribute {
source: &str,
comments: &Comments,
) -> Parentheses {
default_expression_needs_parentheses(self.into(), parenthesize, source, comments)
match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) {
Parentheses::Optional => Parentheses::Never,
parentheses => parentheses,
}
}
}

View file

@ -23,8 +23,12 @@ pub(super) fn default_expression_needs_parentheses(
"Should only be called for expressions"
);
#[allow(clippy::if_same_then_else)]
if parenthesize.is_always() {
Parentheses::Always
}
// `Optional` or `Preserve` and expression has parentheses in source code.
if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, source) {
else if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, source) {
Parentheses::Always
}
// `Optional` or `IfBreaks`: Add parentheses if the expression doesn't fit on a line but enforce
@ -53,9 +57,15 @@ pub enum Parenthesize {
/// Parenthesizes the expression only if it doesn't fit on a line.
IfBreaks,
Always,
}
impl Parenthesize {
pub(crate) const fn is_always(self) -> bool {
matches!(self, Parenthesize::Always)
}
pub(crate) const fn is_if_breaks(self) -> bool {
matches!(self, Parenthesize::IfBreaks)
}
@ -70,7 +80,8 @@ impl Parenthesize {
/// whether there are parentheses in the source code or not.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Parentheses {
/// Always create parentheses
/// 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.