mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-24 13:33:50 +00:00
Fix unstable formatting of trailing end-of-line comments of parenthesized attribute values (#16187)
This commit is contained in:
parent
82eae511ca
commit
31180a84e4
4 changed files with 93 additions and 13 deletions
|
@ -152,3 +152,20 @@ result = (
|
||||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||||
+ 1
|
+ 1
|
||||||
).bit_length()
|
).bit_length()
|
||||||
|
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/16151
|
||||||
|
result = (
|
||||||
|
(await query_the_thing(mypy_doesnt_understand)) # type: ignore[x]
|
||||||
|
.foo()
|
||||||
|
.bar()
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(
|
||||||
|
a # trailing end-of-line
|
||||||
|
# trailing own-line
|
||||||
|
) # trailing closing parentheses
|
||||||
|
# dangling before dot
|
||||||
|
.b # trailing end-of-line
|
||||||
|
)
|
||||||
|
|
|
@ -1391,10 +1391,8 @@ fn handle_attribute_comment<'a>(
|
||||||
.take_while(|token| token.kind == SimpleTokenKind::RParen)
|
.take_while(|token| token.kind == SimpleTokenKind::RParen)
|
||||||
.last()
|
.last()
|
||||||
{
|
{
|
||||||
return if comment.start() < right_paren.start() {
|
if comment.start() < right_paren.start() {
|
||||||
CommentPlacement::trailing(attribute.value.as_ref(), comment)
|
return CommentPlacement::trailing(attribute.value.as_ref(), comment);
|
||||||
} else {
|
|
||||||
CommentPlacement::dangling(comment.enclosing_node(), comment)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_formatter::{write, FormatRuleWithOptions};
|
use ruff_formatter::{write, FormatRuleWithOptions};
|
||||||
use ruff_python_ast::AnyNodeRef;
|
use ruff_python_ast::AnyNodeRef;
|
||||||
use ruff_python_ast::{Expr, ExprAttribute, ExprNumberLiteral, Number};
|
use ruff_python_ast::{Expr, ExprAttribute, ExprNumberLiteral, Number};
|
||||||
use ruff_python_trivia::{find_only_token_in_range, SimpleTokenKind};
|
use ruff_python_trivia::{find_only_token_in_range, SimpleTokenKind, SimpleTokenizer};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::comments::dangling_comments;
|
use crate::comments::dangling_comments;
|
||||||
|
@ -50,9 +50,6 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
||||||
if parenthesize_value {
|
if parenthesize_value {
|
||||||
// Don't propagate the call chain layout.
|
// Don't propagate the call chain layout.
|
||||||
value.format().with_options(Parentheses::Always).fmt(f)?;
|
value.format().with_options(Parentheses::Always).fmt(f)?;
|
||||||
|
|
||||||
// Format the dot on its own line.
|
|
||||||
soft_line_break().fmt(f)?;
|
|
||||||
} else {
|
} else {
|
||||||
match value.as_ref() {
|
match value.as_ref() {
|
||||||
Expr::Attribute(expr) => {
|
Expr::Attribute(expr) => {
|
||||||
|
@ -60,11 +57,9 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
||||||
}
|
}
|
||||||
Expr::Call(expr) => {
|
Expr::Call(expr) => {
|
||||||
expr.format().with_options(call_chain_layout).fmt(f)?;
|
expr.format().with_options(call_chain_layout).fmt(f)?;
|
||||||
soft_line_break().fmt(f)?;
|
|
||||||
}
|
}
|
||||||
Expr::Subscript(expr) => {
|
Expr::Subscript(expr) => {
|
||||||
expr.format().with_options(call_chain_layout).fmt(f)?;
|
expr.format().with_options(call_chain_layout).fmt(f)?;
|
||||||
soft_line_break().fmt(f)?;
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
value.format().with_options(Parentheses::Never).fmt(f)?;
|
value.format().with_options(Parentheses::Never).fmt(f)?;
|
||||||
|
@ -77,19 +72,56 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
||||||
value.format().with_options(Parentheses::Never).fmt(f)?;
|
value.format().with_options(Parentheses::Never).fmt(f)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let comments = f.context().comments().clone();
|
||||||
|
|
||||||
|
// Always add a line break if the value is parenthesized and there's an
|
||||||
|
// end of line comment on the same line as the closing parenthesis.
|
||||||
|
// ```python
|
||||||
|
// (
|
||||||
|
// (
|
||||||
|
// a
|
||||||
|
// ) # `end_of_line_comment`
|
||||||
|
// .
|
||||||
|
// b
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
let has_trailing_end_of_line_comment =
|
||||||
|
SimpleTokenizer::starts_at(value.end(), f.context().source())
|
||||||
|
.skip_trivia()
|
||||||
|
.take_while(|token| token.kind == SimpleTokenKind::RParen)
|
||||||
|
.last()
|
||||||
|
.is_some_and(|right_paren| {
|
||||||
|
let trailing_value_comments = comments.trailing(&**value);
|
||||||
|
trailing_value_comments.iter().any(|comment| {
|
||||||
|
comment.line_position().is_end_of_line()
|
||||||
|
&& comment.start() > right_paren.end()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if has_trailing_end_of_line_comment {
|
||||||
|
hard_line_break().fmt(f)?;
|
||||||
|
}
|
||||||
|
// Allow the `.` on its own line if this is a fluent call chain
|
||||||
|
// and the value either requires parenthesizing or is a call or subscript expression
|
||||||
|
// (it's a fluent chain but not the first element).
|
||||||
|
else if call_chain_layout == CallChainLayout::Fluent {
|
||||||
|
if parenthesize_value || value.is_call_expr() || value.is_subscript_expr() {
|
||||||
|
soft_line_break().fmt(f)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Identify dangling comments before and after the dot:
|
// Identify dangling comments before and after the dot:
|
||||||
// ```python
|
// ```python
|
||||||
// (
|
// (
|
||||||
// (
|
// (
|
||||||
// a
|
// a
|
||||||
// ) # `before_dot`
|
// )
|
||||||
// # `before_dot`
|
// # `before_dot`
|
||||||
// . # `after_dot`
|
// . # `after_dot`
|
||||||
// # `after_dot`
|
// # `after_dot`
|
||||||
// b
|
// b
|
||||||
// )
|
// )
|
||||||
// ```
|
// ```
|
||||||
let comments = f.context().comments().clone();
|
|
||||||
let dangling = comments.dangling(item);
|
let dangling = comments.dangling(item);
|
||||||
let (before_dot, after_dot) = if dangling.is_empty() {
|
let (before_dot, after_dot) = if dangling.is_empty() {
|
||||||
(dangling, dangling)
|
(dangling, dangling)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/attribute.py
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/attribute.py
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
## Input
|
## Input
|
||||||
```python
|
```python
|
||||||
|
@ -159,6 +158,23 @@ result = (
|
||||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||||
+ 1
|
+ 1
|
||||||
).bit_length()
|
).bit_length()
|
||||||
|
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/16151
|
||||||
|
result = (
|
||||||
|
(await query_the_thing(mypy_doesnt_understand)) # type: ignore[x]
|
||||||
|
.foo()
|
||||||
|
.bar()
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(
|
||||||
|
a # trailing end-of-line
|
||||||
|
# trailing own-line
|
||||||
|
) # trailing closing parentheses
|
||||||
|
# dangling before dot
|
||||||
|
.b # trailing end-of-line
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
|
@ -295,4 +311,21 @@ result = (
|
||||||
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
|
||||||
+ 1
|
+ 1
|
||||||
).bit_length()
|
).bit_length()
|
||||||
|
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/16151
|
||||||
|
result = (
|
||||||
|
(await query_the_thing(mypy_doesnt_understand)) # type: ignore[x]
|
||||||
|
.foo()
|
||||||
|
.bar()
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(
|
||||||
|
a # trailing end-of-line
|
||||||
|
# trailing own-line
|
||||||
|
) # trailing closing parentheses
|
||||||
|
# dangling before dot
|
||||||
|
.b # trailing end-of-line
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue