mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:35 +00:00
Comments outside expression parentheses (#7873)
<!-- Thank you for contributing to Ruff! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary Fixes https://github.com/astral-sh/ruff/issues/7448 Fixes https://github.com/astral-sh/ruff/issues/7892 I've removed automatic dangling comment formatting, we're doing manual dangling comment formatting everywhere anyway (the assert-all-comments-formatted ensures this) and dangling comments would break the formatting there. ## Test Plan New test file. --------- Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
67b043482a
commit
8f9753f58e
13 changed files with 652 additions and 124 deletions
|
@ -575,6 +575,10 @@ where
|
||||||
context: PhantomData,
|
context: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rule(&self) -> &R {
|
||||||
|
&self.rule
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, R, O, C> FormatRefWithRule<'_, T, R, C>
|
impl<T, R, O, C> FormatRefWithRule<'_, T, R, C>
|
||||||
|
|
|
@ -4,14 +4,20 @@ use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||||
use crate::AnyNodeRef;
|
use crate::AnyNodeRef;
|
||||||
use crate::ExpressionRef;
|
use crate::ExpressionRef;
|
||||||
|
|
||||||
/// Returns the [`TextRange`] of a given expression including parentheses, if the expression is
|
/// Returns an iterator over the ranges of the optional parentheses surrounding an expression.
|
||||||
/// parenthesized; or `None`, if the expression is not parenthesized.
|
///
|
||||||
pub fn parenthesized_range(
|
/// E.g. for `((f()))` with `f()` as expression, the iterator returns the ranges (1, 6) and (0, 7).
|
||||||
expr: ExpressionRef,
|
///
|
||||||
parent: AnyNodeRef,
|
/// Note that without a parent the range can be inaccurate, e.g. `f(a)` we falsely return a set of
|
||||||
comment_ranges: &CommentRanges,
|
/// parentheses around `a` even if the parentheses actually belong to `f`. That is why you should
|
||||||
source: &str,
|
/// generally prefer [`parenthesized_range`].
|
||||||
) -> Option<TextRange> {
|
pub fn parentheses_iterator<'a>(
|
||||||
|
expr: ExpressionRef<'a>,
|
||||||
|
parent: Option<AnyNodeRef>,
|
||||||
|
comment_ranges: &'a CommentRanges,
|
||||||
|
source: &'a str,
|
||||||
|
) -> impl Iterator<Item = TextRange> + 'a {
|
||||||
|
let right_tokenizer = if let Some(parent) = parent {
|
||||||
// If the parent is a node that brings its own parentheses, exclude the closing parenthesis
|
// If the parent is a node that brings its own parentheses, exclude the closing parenthesis
|
||||||
// from our search range. Otherwise, we risk matching on calls, like `func(x)`, for which
|
// from our search range. Otherwise, we risk matching on calls, like `func(x)`, for which
|
||||||
// the open and close parentheses are part of the `Arguments` node.
|
// the open and close parentheses are part of the `Arguments` node.
|
||||||
|
@ -28,9 +34,12 @@ pub fn parenthesized_range(
|
||||||
} else {
|
} else {
|
||||||
parent.end()
|
parent.end()
|
||||||
};
|
};
|
||||||
|
|
||||||
let right_tokenizer =
|
|
||||||
SimpleTokenizer::new(source, TextRange::new(expr.end(), exclusive_parent_end))
|
SimpleTokenizer::new(source, TextRange::new(expr.end(), exclusive_parent_end))
|
||||||
|
} else {
|
||||||
|
SimpleTokenizer::starts_at(expr.end(), source)
|
||||||
|
};
|
||||||
|
|
||||||
|
let right_tokenizer = right_tokenizer
|
||||||
.skip_trivia()
|
.skip_trivia()
|
||||||
.take_while(|token| token.kind == SimpleTokenKind::RParen);
|
.take_while(|token| token.kind == SimpleTokenKind::RParen);
|
||||||
|
|
||||||
|
@ -43,6 +52,16 @@ pub fn parenthesized_range(
|
||||||
// the `right_tokenizer` is exhausted.
|
// the `right_tokenizer` is exhausted.
|
||||||
right_tokenizer
|
right_tokenizer
|
||||||
.zip(left_tokenizer)
|
.zip(left_tokenizer)
|
||||||
.last()
|
|
||||||
.map(|(right, left)| TextRange::new(left.start(), right.end()))
|
.map(|(right, left)| TextRange::new(left.start(), right.end()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`TextRange`] of a given expression including parentheses, if the expression is
|
||||||
|
/// parenthesized; or `None`, if the expression is not parenthesized.
|
||||||
|
pub fn parenthesized_range(
|
||||||
|
expr: ExpressionRef,
|
||||||
|
parent: AnyNodeRef,
|
||||||
|
comment_ranges: &CommentRanges,
|
||||||
|
source: &str,
|
||||||
|
) -> Option<TextRange> {
|
||||||
|
parentheses_iterator(expr, Some(parent), comment_ranges, source).last()
|
||||||
|
}
|
||||||
|
|
|
@ -161,3 +161,14 @@ if True:
|
||||||
+ "WARNING: Removing listed files. Do you really want to continue. yes/n)? "
|
+ "WARNING: Removing listed files. Do you really want to continue. yes/n)? "
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# https://github.com/astral-sh/ruff/issues/7448
|
||||||
|
x = (
|
||||||
|
# a
|
||||||
|
not # b
|
||||||
|
# c
|
||||||
|
( # d
|
||||||
|
# e
|
||||||
|
True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
list_with_parenthesized_elements1 = [
|
||||||
|
# comment leading outer
|
||||||
|
(
|
||||||
|
# comment leading inner
|
||||||
|
1 + 2 # comment trailing inner
|
||||||
|
) # comment trailing outer
|
||||||
|
]
|
||||||
|
|
||||||
|
list_with_parenthesized_elements2 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2)
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements3 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2) # trailing outer
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements4 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2), # trailing outer
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements5 = [
|
||||||
|
(1), # trailing outer
|
||||||
|
(2), # trailing outer
|
||||||
|
]
|
||||||
|
|
||||||
|
nested_parentheses1 = (
|
||||||
|
(
|
||||||
|
(
|
||||||
|
1
|
||||||
|
) # i
|
||||||
|
) # j
|
||||||
|
) # k
|
||||||
|
nested_parentheses2 = [
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
1
|
||||||
|
) # i
|
||||||
|
# i2
|
||||||
|
) # j
|
||||||
|
# j2
|
||||||
|
) # k
|
||||||
|
# k2
|
||||||
|
]
|
||||||
|
nested_parentheses3 = (
|
||||||
|
( # a
|
||||||
|
( # b
|
||||||
|
1
|
||||||
|
) # i
|
||||||
|
) # j
|
||||||
|
) # k
|
||||||
|
nested_parentheses4 = [
|
||||||
|
# a
|
||||||
|
( # b
|
||||||
|
# c
|
||||||
|
( # d
|
||||||
|
# e
|
||||||
|
( #f
|
||||||
|
1
|
||||||
|
) # i
|
||||||
|
# i2
|
||||||
|
) # j
|
||||||
|
# j2
|
||||||
|
) # k
|
||||||
|
# k2
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
x = (
|
||||||
|
# unary comment
|
||||||
|
not
|
||||||
|
# in-between comment
|
||||||
|
(
|
||||||
|
# leading inner
|
||||||
|
"a"
|
||||||
|
),
|
||||||
|
not # in-between comment
|
||||||
|
(
|
||||||
|
# leading inner
|
||||||
|
"b"
|
||||||
|
),
|
||||||
|
not
|
||||||
|
( # in-between comment
|
||||||
|
# leading inner
|
||||||
|
"c"
|
||||||
|
),
|
||||||
|
# 1
|
||||||
|
not # 2
|
||||||
|
( # 3
|
||||||
|
# 4
|
||||||
|
"d"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
# unary comment
|
||||||
|
not
|
||||||
|
# in-between comment
|
||||||
|
(
|
||||||
|
# leading inner
|
||||||
|
1
|
||||||
|
)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Make sure we keep a inside the parentheses
|
||||||
|
# https://github.com/astral-sh/ruff/issues/7892
|
||||||
|
x = (
|
||||||
|
# a
|
||||||
|
( # b
|
||||||
|
1
|
||||||
|
)
|
||||||
|
)
|
|
@ -86,10 +86,6 @@ with (
|
||||||
)
|
)
|
||||||
): pass
|
): pass
|
||||||
|
|
||||||
with (a # trailing same line comment
|
|
||||||
# trailing own line comment
|
|
||||||
) as b: pass
|
|
||||||
|
|
||||||
with (
|
with (
|
||||||
a # trailing same line comment
|
a # trailing same line comment
|
||||||
# trailing own line comment
|
# trailing own line comment
|
||||||
|
|
|
@ -1878,8 +1878,7 @@ fn handle_lambda_comment<'a>(
|
||||||
CommentPlacement::Default(comment)
|
CommentPlacement::Default(comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attach trailing end-of-line comments on the operator as dangling comments on the enclosing
|
/// Move comment between a unary op and its operand before the unary op by marking them as trailing.
|
||||||
/// node.
|
|
||||||
///
|
///
|
||||||
/// For example, given:
|
/// For example, given:
|
||||||
/// ```python
|
/// ```python
|
||||||
|
@ -1896,26 +1895,27 @@ fn handle_unary_op_comment<'a>(
|
||||||
unary_op: &'a ast::ExprUnaryOp,
|
unary_op: &'a ast::ExprUnaryOp,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
) -> CommentPlacement<'a> {
|
) -> CommentPlacement<'a> {
|
||||||
if comment.line_position().is_own_line() {
|
let mut tokenizer = SimpleTokenizer::new(
|
||||||
return CommentPlacement::Default(comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
if comment.start() > unary_op.operand.start() {
|
|
||||||
return CommentPlacement::Default(comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokenizer = SimpleTokenizer::new(
|
|
||||||
locator.contents(),
|
locator.contents(),
|
||||||
TextRange::new(comment.start(), unary_op.operand.start()),
|
TextRange::new(unary_op.start(), unary_op.operand.start()),
|
||||||
);
|
)
|
||||||
if tokenizer
|
.skip_trivia();
|
||||||
.skip_trivia()
|
let op_token = tokenizer.next();
|
||||||
.any(|token| token.kind == SimpleTokenKind::LParen)
|
debug_assert!(op_token.is_some_and(|token| matches!(
|
||||||
{
|
token.kind,
|
||||||
return CommentPlacement::Default(comment);
|
SimpleTokenKind::Tilde
|
||||||
|
| SimpleTokenKind::Not
|
||||||
|
| SimpleTokenKind::Plus
|
||||||
|
| SimpleTokenKind::Minus
|
||||||
|
)));
|
||||||
|
let up_to = tokenizer
|
||||||
|
.find(|token| token.kind == SimpleTokenKind::LParen)
|
||||||
|
.map_or(unary_op.operand.start(), |lparen| lparen.start());
|
||||||
|
if comment.end() < up_to {
|
||||||
|
CommentPlacement::leading(unary_op, comment)
|
||||||
|
} else {
|
||||||
|
CommentPlacement::Default(comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
CommentPlacement::dangling(comment.enclosing_node(), comment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attach an end-of-line comment immediately following an open bracket as a dangling comment on
|
/// Attach an end-of-line comment immediately following an open bracket as a dangling comment on
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::slice;
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use ruff_formatter::{
|
use ruff_formatter::{
|
||||||
write, FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions,
|
write, FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions,
|
||||||
};
|
};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
|
use ruff_python_ast::parenthesize::parentheses_iterator;
|
||||||
use ruff_python_ast::visitor::preorder::{walk_expr, PreorderVisitor};
|
use ruff_python_ast::visitor::preorder::{walk_expr, PreorderVisitor};
|
||||||
use ruff_python_ast::AnyNodeRef;
|
use ruff_python_ast::{AnyNodeRef, Constant, Expr, ExpressionRef, Operator};
|
||||||
use ruff_python_ast::{Constant, Expr, ExpressionRef, Operator};
|
|
||||||
use ruff_python_trivia::CommentRanges;
|
use ruff_python_trivia::CommentRanges;
|
||||||
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::builders::parenthesize_if_expands;
|
use crate::builders::parenthesize_if_expands;
|
||||||
use crate::comments::leading_comments;
|
use crate::comments::{leading_comments, trailing_comments, LeadingDanglingTrailingComments};
|
||||||
use crate::context::{NodeLevel, WithNodeLevel};
|
use crate::context::{NodeLevel, WithNodeLevel};
|
||||||
use crate::expression::parentheses::{
|
use crate::expression::parentheses::{
|
||||||
is_expression_parenthesized, optional_parentheses, parenthesized, NeedsParentheses,
|
is_expression_parenthesized, optional_parentheses, parenthesized, NeedsParentheses,
|
||||||
|
@ -102,7 +102,6 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
Expr::Slice(expr) => expr.format().fmt(f),
|
Expr::Slice(expr) => expr.format().fmt(f),
|
||||||
Expr::IpyEscapeCommand(expr) => expr.format().fmt(f),
|
Expr::IpyEscapeCommand(expr) => expr.format().fmt(f),
|
||||||
});
|
});
|
||||||
|
|
||||||
let parenthesize = match parentheses {
|
let parenthesize = match parentheses {
|
||||||
Parentheses::Preserve => is_expression_parenthesized(
|
Parentheses::Preserve => is_expression_parenthesized(
|
||||||
expression.into(),
|
expression.into(),
|
||||||
|
@ -113,32 +112,13 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
// Fluent style means we already have parentheses
|
// Fluent style means we already have parentheses
|
||||||
Parentheses::Never => false,
|
Parentheses::Never => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if parenthesize {
|
if parenthesize {
|
||||||
// Any comments on the open parenthesis of a `node`.
|
let comment = f.context().comments().clone();
|
||||||
//
|
let node_comments = comment.leading_dangling_trailing(expression);
|
||||||
// For example, `# comment` in:
|
if !node_comments.has_leading() && !node_comments.has_trailing() {
|
||||||
// ```python
|
|
||||||
// ( # comment
|
|
||||||
// foo.bar
|
|
||||||
// )
|
|
||||||
// ```
|
|
||||||
let comments = f.context().comments().clone();
|
|
||||||
let leading = comments.leading(expression);
|
|
||||||
if let Some((index, open_parenthesis_comment)) = leading
|
|
||||||
.iter()
|
|
||||||
.find_position(|comment| comment.line_position().is_end_of_line())
|
|
||||||
{
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
[
|
|
||||||
leading_comments(&leading[..index]),
|
|
||||||
parenthesized("(", &format_expr, ")")
|
|
||||||
.with_dangling_comments(std::slice::from_ref(open_parenthesis_comment))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
parenthesized("(", &format_expr, ")").fmt(f)
|
parenthesized("(", &format_expr, ")").fmt(f)
|
||||||
|
} else {
|
||||||
|
format_with_parentheses_comments(expression, &node_comments, f)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let level = match f.context().node_level() {
|
let level = match f.context().node_level() {
|
||||||
|
@ -155,6 +135,185 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The comments below are trailing on the addition, but it's also outside the
|
||||||
|
/// parentheses
|
||||||
|
/// ```python
|
||||||
|
/// x = [
|
||||||
|
/// # comment leading
|
||||||
|
/// (1 + 2) # comment trailing
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
/// as opposed to
|
||||||
|
/// ```python
|
||||||
|
/// x = [(
|
||||||
|
/// # comment leading
|
||||||
|
/// 1 + 2 # comment trailing
|
||||||
|
/// )]
|
||||||
|
/// ```
|
||||||
|
/// , where the comments are inside the parentheses. That is also affects list
|
||||||
|
/// formatting, where we want to avoid moving the comments after the comma inside
|
||||||
|
/// the parentheses:
|
||||||
|
/// ```python
|
||||||
|
/// data = [
|
||||||
|
/// (
|
||||||
|
/// b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
/// b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
/// ), # Point (0 0)
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
/// We could mark those comments as trailing in list but it's easier to handle
|
||||||
|
/// them here too.
|
||||||
|
///
|
||||||
|
/// So given
|
||||||
|
/// ```python
|
||||||
|
/// x = [
|
||||||
|
/// # comment leading outer
|
||||||
|
/// (
|
||||||
|
/// # comment leading inner
|
||||||
|
/// 1 + 2 # comment trailing inner
|
||||||
|
/// ) # comment trailing outer
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
/// we want to keep the inner an outer comments outside the parentheses and the inner ones inside.
|
||||||
|
/// This is independent of whether they are own line or end-of-line comments, though end-of-line
|
||||||
|
/// comments can become own line comments when we discard nested parentheses.
|
||||||
|
///
|
||||||
|
/// Style decision: When there are multiple nested parentheses around an expression, we consider the
|
||||||
|
/// outermost parentheses the relevant ones and discard the others.
|
||||||
|
fn format_with_parentheses_comments(
|
||||||
|
expression: &Expr,
|
||||||
|
node_comments: &LeadingDanglingTrailingComments,
|
||||||
|
f: &mut PyFormatter,
|
||||||
|
) -> FormatResult<()> {
|
||||||
|
// First part: Split the comments
|
||||||
|
|
||||||
|
// TODO(konstin): We don't have the parent, which is a problem:
|
||||||
|
// ```python
|
||||||
|
// f(
|
||||||
|
// # a
|
||||||
|
// (a)
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
// gets formatted as
|
||||||
|
// ```python
|
||||||
|
// f(
|
||||||
|
// (
|
||||||
|
// # a
|
||||||
|
// a
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
let range_with_parens = parentheses_iterator(
|
||||||
|
expression.into(),
|
||||||
|
None,
|
||||||
|
f.context().comments().ranges(),
|
||||||
|
f.context().source(),
|
||||||
|
)
|
||||||
|
.last();
|
||||||
|
|
||||||
|
let (leading_split, trailing_split) = if let Some(range_with_parens) = range_with_parens {
|
||||||
|
let leading_split = node_comments
|
||||||
|
.leading
|
||||||
|
.partition_point(|comment| comment.start() < range_with_parens.start());
|
||||||
|
let trailing_split = node_comments
|
||||||
|
.trailing
|
||||||
|
.partition_point(|comment| comment.start() < range_with_parens.end());
|
||||||
|
(leading_split, trailing_split)
|
||||||
|
} else {
|
||||||
|
(0, node_comments.trailing.len())
|
||||||
|
};
|
||||||
|
|
||||||
|
let (leading_outer, leading_inner) = node_comments.leading.split_at(leading_split);
|
||||||
|
let (trailing_inner, trailing_outer) = node_comments.trailing.split_at(trailing_split);
|
||||||
|
|
||||||
|
// Preserve an opening parentheses comment
|
||||||
|
// ```python
|
||||||
|
// a = ( # opening parentheses comment
|
||||||
|
// # leading inner
|
||||||
|
// 1
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
let (parentheses_comment, leading_inner) = match leading_inner.split_first() {
|
||||||
|
Some((first, rest)) if first.line_position().is_end_of_line() => {
|
||||||
|
(slice::from_ref(first), rest)
|
||||||
|
}
|
||||||
|
_ => (Default::default(), node_comments.leading),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Second Part: Format
|
||||||
|
|
||||||
|
// The code order is a bit strange here, we format:
|
||||||
|
// * outer leading comment
|
||||||
|
// * opening parenthesis
|
||||||
|
// * opening parenthesis comment
|
||||||
|
// * inner leading comments
|
||||||
|
// * the expression itself
|
||||||
|
// * inner trailing comments
|
||||||
|
// * the closing parenthesis
|
||||||
|
// * outer trailing comments
|
||||||
|
|
||||||
|
let fmt_fields = format_with(|f| match expression {
|
||||||
|
Expr::BoolOp(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::NamedExpr(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::BinOp(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::UnaryOp(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Lambda(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::IfExp(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Dict(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Set(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::ListComp(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::SetComp(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::DictComp(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::GeneratorExp(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Await(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Yield(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::YieldFrom(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Compare(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Call(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::FormattedValue(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::FString(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Constant(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Attribute(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Subscript(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Starred(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Name(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::List(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Tuple(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::Slice(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
Expr::IpyEscapeCommand(expr) => FormatNodeRule::fmt_fields(expr.format().rule(), expr, f),
|
||||||
|
});
|
||||||
|
|
||||||
|
leading_comments(leading_outer).fmt(f)?;
|
||||||
|
|
||||||
|
// Custom FormatNodeRule::fmt variant that only formats the inner comments
|
||||||
|
let format_node_rule_fmt = format_with(|f| {
|
||||||
|
// No need to handle suppression comments, those are statement only
|
||||||
|
leading_comments(leading_inner).fmt(f)?;
|
||||||
|
|
||||||
|
let is_source_map_enabled = f.options().source_map_generation().is_enabled();
|
||||||
|
|
||||||
|
if is_source_map_enabled {
|
||||||
|
source_position(expression.start()).fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt_fields.fmt(f)?;
|
||||||
|
|
||||||
|
if is_source_map_enabled {
|
||||||
|
source_position(expression.end()).fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
trailing_comments(trailing_inner).fmt(f)
|
||||||
|
});
|
||||||
|
|
||||||
|
// The actual parenthesized formatting
|
||||||
|
parenthesized("(", &format_node_rule_fmt, ")")
|
||||||
|
.with_dangling_comments(parentheses_comment)
|
||||||
|
.fmt(f)?;
|
||||||
|
trailing_comments(trailing_outer).fmt(f)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Wraps an expression in an optional parentheses except if its [`NeedsParentheses::needs_parentheses`] implementation
|
/// 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,
|
/// 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.
|
/// because they already bring their own parentheses.
|
||||||
|
|
|
@ -60,7 +60,6 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fmt_fields(node, f)?;
|
self.fmt_fields(node, f)?;
|
||||||
self.fmt_dangling_comments(node_comments.dangling, f)?;
|
|
||||||
|
|
||||||
if is_source_map_enabled {
|
if is_source_map_enabled {
|
||||||
source_position(node.end()).fmt(f)?;
|
source_position(node.end()).fmt(f)?;
|
||||||
|
|
|
@ -320,17 +320,6 @@ long_unmergable_string_with_pragma = (
|
||||||
"formatting"
|
"formatting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -221,8 +217,8 @@
|
|
||||||
func_with_bad_comma(
|
|
||||||
(
|
|
||||||
"This is a really long string argument to a function that has a trailing comma"
|
|
||||||
- " which should NOT be there."
|
|
||||||
- ), # comment after comma
|
|
||||||
+ " which should NOT be there." # comment after comma
|
|
||||||
+ ),
|
|
||||||
)
|
|
||||||
|
|
||||||
func_with_bad_parens_that_wont_fit_in_one_line(
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ruff Output
|
## Ruff Output
|
||||||
|
@ -555,8 +544,8 @@ func_with_bad_comma(
|
||||||
func_with_bad_comma(
|
func_with_bad_comma(
|
||||||
(
|
(
|
||||||
"This is a really long string argument to a function that has a trailing comma"
|
"This is a really long string argument to a function that has a trailing comma"
|
||||||
" which should NOT be there." # comment after comma
|
" which should NOT be there."
|
||||||
),
|
), # comment after comma
|
||||||
)
|
)
|
||||||
|
|
||||||
func_with_bad_parens_that_wont_fit_in_one_line(
|
func_with_bad_parens_that_wont_fit_in_one_line(
|
||||||
|
|
|
@ -458,8 +458,9 @@ func(
|
||||||
)
|
)
|
||||||
|
|
||||||
func(
|
func(
|
||||||
|
(
|
||||||
# outer comment
|
# outer comment
|
||||||
( # inner comment
|
# inner comment
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -167,6 +167,17 @@ if True:
|
||||||
+ "WARNING: Removing listed files. Do you really want to continue. yes/n)? "
|
+ "WARNING: Removing listed files. Do you really want to continue. yes/n)? "
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# https://github.com/astral-sh/ruff/issues/7448
|
||||||
|
x = (
|
||||||
|
# a
|
||||||
|
not # b
|
||||||
|
# c
|
||||||
|
( # d
|
||||||
|
# e
|
||||||
|
True
|
||||||
|
)
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
|
@ -217,35 +228,31 @@ if +(
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not
|
|
||||||
# comment
|
# comment
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
~
|
|
||||||
# comment
|
# comment
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if (
|
if (
|
||||||
-
|
|
||||||
# comment
|
# comment
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
+
|
|
||||||
# comment
|
# comment
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
@ -254,8 +261,8 @@ if (
|
||||||
|
|
||||||
if (
|
if (
|
||||||
# unary comment
|
# unary comment
|
||||||
not (
|
|
||||||
# operand comment
|
# operand comment
|
||||||
|
not (
|
||||||
# comment
|
# comment
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
|
@ -286,28 +293,31 @@ if not (
|
||||||
|
|
||||||
## Trailing operator comments
|
## Trailing operator comments
|
||||||
|
|
||||||
if (
|
if ( # comment
|
||||||
not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # comment
|
not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # comment
|
# comment
|
||||||
|
~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if (
|
if (
|
||||||
-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # comment
|
# comment
|
||||||
|
-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # comment
|
# comment
|
||||||
|
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
@ -327,13 +337,14 @@ if (
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not
|
|
||||||
# comment
|
# comment
|
||||||
a
|
not a
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if not a: # comment
|
if ( # comment
|
||||||
|
not a
|
||||||
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7423
|
# Regression test for: https://github.com/astral-sh/ruff/issues/7423
|
||||||
|
@ -345,6 +356,17 @@ if True:
|
||||||
+ "WARNING: Removing listed files. Do you really want to continue. yes/n)? "
|
+ "WARNING: Removing listed files. Do you really want to continue. yes/n)? "
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# https://github.com/astral-sh/ruff/issues/7448
|
||||||
|
x = (
|
||||||
|
# a
|
||||||
|
# b
|
||||||
|
# c
|
||||||
|
not ( # d
|
||||||
|
# e
|
||||||
|
True
|
||||||
|
)
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/parentheses/expression_parentheses_comments.py
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```py
|
||||||
|
list_with_parenthesized_elements1 = [
|
||||||
|
# comment leading outer
|
||||||
|
(
|
||||||
|
# comment leading inner
|
||||||
|
1 + 2 # comment trailing inner
|
||||||
|
) # comment trailing outer
|
||||||
|
]
|
||||||
|
|
||||||
|
list_with_parenthesized_elements2 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2)
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements3 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2) # trailing outer
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements4 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2), # trailing outer
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements5 = [
|
||||||
|
(1), # trailing outer
|
||||||
|
(2), # trailing outer
|
||||||
|
]
|
||||||
|
|
||||||
|
nested_parentheses1 = (
|
||||||
|
(
|
||||||
|
(
|
||||||
|
1
|
||||||
|
) # i
|
||||||
|
) # j
|
||||||
|
) # k
|
||||||
|
nested_parentheses2 = [
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
1
|
||||||
|
) # i
|
||||||
|
# i2
|
||||||
|
) # j
|
||||||
|
# j2
|
||||||
|
) # k
|
||||||
|
# k2
|
||||||
|
]
|
||||||
|
nested_parentheses3 = (
|
||||||
|
( # a
|
||||||
|
( # b
|
||||||
|
1
|
||||||
|
) # i
|
||||||
|
) # j
|
||||||
|
) # k
|
||||||
|
nested_parentheses4 = [
|
||||||
|
# a
|
||||||
|
( # b
|
||||||
|
# c
|
||||||
|
( # d
|
||||||
|
# e
|
||||||
|
( #f
|
||||||
|
1
|
||||||
|
) # i
|
||||||
|
# i2
|
||||||
|
) # j
|
||||||
|
# j2
|
||||||
|
) # k
|
||||||
|
# k2
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
x = (
|
||||||
|
# unary comment
|
||||||
|
not
|
||||||
|
# in-between comment
|
||||||
|
(
|
||||||
|
# leading inner
|
||||||
|
"a"
|
||||||
|
),
|
||||||
|
not # in-between comment
|
||||||
|
(
|
||||||
|
# leading inner
|
||||||
|
"b"
|
||||||
|
),
|
||||||
|
not
|
||||||
|
( # in-between comment
|
||||||
|
# leading inner
|
||||||
|
"c"
|
||||||
|
),
|
||||||
|
# 1
|
||||||
|
not # 2
|
||||||
|
( # 3
|
||||||
|
# 4
|
||||||
|
"d"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
# unary comment
|
||||||
|
not
|
||||||
|
# in-between comment
|
||||||
|
(
|
||||||
|
# leading inner
|
||||||
|
1
|
||||||
|
)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Make sure we keep a inside the parentheses
|
||||||
|
# https://github.com/astral-sh/ruff/issues/7892
|
||||||
|
x = (
|
||||||
|
# a
|
||||||
|
( # b
|
||||||
|
1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
```py
|
||||||
|
list_with_parenthesized_elements1 = [
|
||||||
|
# comment leading outer
|
||||||
|
(
|
||||||
|
# comment leading inner
|
||||||
|
1 + 2 # comment trailing inner
|
||||||
|
) # comment trailing outer
|
||||||
|
]
|
||||||
|
|
||||||
|
list_with_parenthesized_elements2 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2)
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements3 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2) # trailing outer
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements4 = [
|
||||||
|
# leading outer
|
||||||
|
(1 + 2), # trailing outer
|
||||||
|
]
|
||||||
|
list_with_parenthesized_elements5 = [
|
||||||
|
(1), # trailing outer
|
||||||
|
(2), # trailing outer
|
||||||
|
]
|
||||||
|
|
||||||
|
nested_parentheses1 = (
|
||||||
|
1 # i # j
|
||||||
|
) # k
|
||||||
|
nested_parentheses2 = [
|
||||||
|
(
|
||||||
|
1 # i
|
||||||
|
# i2
|
||||||
|
# j
|
||||||
|
# j2
|
||||||
|
) # k
|
||||||
|
# k2
|
||||||
|
]
|
||||||
|
nested_parentheses3 = ( # a
|
||||||
|
# b
|
||||||
|
1 # i # j
|
||||||
|
) # k
|
||||||
|
nested_parentheses4 = [
|
||||||
|
# a
|
||||||
|
( # b
|
||||||
|
# c
|
||||||
|
# d
|
||||||
|
# e
|
||||||
|
# f
|
||||||
|
1 # i
|
||||||
|
# i2
|
||||||
|
# j
|
||||||
|
# j2
|
||||||
|
) # k
|
||||||
|
# k2
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
x = (
|
||||||
|
# unary comment
|
||||||
|
# in-between comment
|
||||||
|
not (
|
||||||
|
# leading inner
|
||||||
|
"a"
|
||||||
|
),
|
||||||
|
# in-between comment
|
||||||
|
not (
|
||||||
|
# leading inner
|
||||||
|
"b"
|
||||||
|
),
|
||||||
|
not ( # in-between comment
|
||||||
|
# leading inner
|
||||||
|
"c"
|
||||||
|
),
|
||||||
|
# 1
|
||||||
|
# 2
|
||||||
|
not ( # 3
|
||||||
|
# 4
|
||||||
|
"d"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
# unary comment
|
||||||
|
# in-between comment
|
||||||
|
not (
|
||||||
|
# leading inner
|
||||||
|
1
|
||||||
|
)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Make sure we keep a inside the parentheses
|
||||||
|
# https://github.com/astral-sh/ruff/issues/7892
|
||||||
|
x = (
|
||||||
|
# a
|
||||||
|
# b
|
||||||
|
1
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -92,10 +92,6 @@ with (
|
||||||
)
|
)
|
||||||
): pass
|
): pass
|
||||||
|
|
||||||
with (a # trailing same line comment
|
|
||||||
# trailing own line comment
|
|
||||||
) as b: pass
|
|
||||||
|
|
||||||
with (
|
with (
|
||||||
a # trailing same line comment
|
a # trailing same line comment
|
||||||
# trailing own line comment
|
# trailing own line comment
|
||||||
|
@ -420,12 +416,6 @@ with (
|
||||||
) as b:
|
) as b:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with (
|
|
||||||
a # trailing same line comment
|
|
||||||
# trailing own line comment
|
|
||||||
) as b:
|
|
||||||
pass
|
|
||||||
|
|
||||||
with (
|
with (
|
||||||
(
|
(
|
||||||
a
|
a
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue