Skip to content

Commit ee76d24

Browse files
committed
failed attempt at re-partitioning dangling comments
see #21410 (comment), but the short summary is that `if` (and likely other) statement formatting code that uses `maybe_parenthesize` checks if the condition has any leading or trailing comments, so if we try to smuggle the comments in as dangling comments, it thinks the expression won't break, so it doesn't add parentheses when formatting a case like this: ```py if ( not # comment a): pass ``` and we end up with a syntax error: ```py if not a: pass ``` There may be some other way around this, but this is why I'm giving up for now. It really feels like we want another CommentPlacement variant or some kind of dangling tag, like Micha mentioned when I met with him.
1 parent d63b4b0 commit ee76d24

File tree

2 files changed

+43
-11
lines changed

2 files changed

+43
-11
lines changed

crates/ruff_python_formatter/src/comments/placement.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1865,8 +1865,10 @@ fn handle_unary_op_comment<'a>(
18651865
.find(|token| token.kind == SimpleTokenKind::LParen)
18661866
.map_or(unary_op.operand.start(), |lparen| lparen.start());
18671867
if comment.end() < up_to {
1868-
CommentPlacement::leading(unary_op, comment)
1868+
eprintln!("leading: {}", &source[comment.range()]);
1869+
CommentPlacement::dangling(unary_op, comment)
18691870
} else {
1871+
eprintln!("default: {}", &source[comment.range()]);
18701872
CommentPlacement::Default(comment)
18711873
}
18721874
}

crates/ruff_python_formatter/src/expression/expr_unary_op.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
use ruff_python_ast::AnyNodeRef;
22
use ruff_python_ast::ExprUnaryOp;
33
use ruff_python_ast::UnaryOp;
4+
use ruff_python_trivia::SimpleTokenKind;
5+
use ruff_python_trivia::SimpleTokenizer;
6+
use ruff_text_size::Ranged;
7+
use ruff_text_size::TextRange;
48

9+
use crate::comments::leading_comments;
510
use crate::comments::trailing_comments;
611
use crate::expression::parentheses::{
712
NeedsParentheses, OptionalParentheses, Parentheses, is_expression_parenthesized,
@@ -20,24 +25,49 @@ impl FormatNodeRule<ExprUnaryOp> for FormatExprUnaryOp {
2025
operand,
2126
} = item;
2227

23-
let operator = match op {
24-
UnaryOp::Invert => "~",
25-
UnaryOp::Not => "not",
26-
UnaryOp::UAdd => "+",
27-
UnaryOp::USub => "-",
28-
};
29-
30-
token(operator).fmt(f)?;
31-
3228
let comments = f.context().comments().clone();
3329
let dangling = comments.dangling(item);
3430

31+
let mut tokenizer = SimpleTokenizer::new(
32+
f.context().source(),
33+
TextRange::new(item.start(), operand.start()),
34+
)
35+
.skip_trivia();
36+
let op_token = tokenizer.next();
37+
debug_assert!(op_token.is_some_and(|token| matches!(
38+
token.kind,
39+
SimpleTokenKind::Tilde
40+
| SimpleTokenKind::Not
41+
| SimpleTokenKind::Plus
42+
| SimpleTokenKind::Minus
43+
)));
44+
let up_to = tokenizer
45+
.find(|token| token.kind == SimpleTokenKind::LParen)
46+
.map_or(operand.start(), |lparen| lparen.start());
47+
48+
let pivot = dangling.partition_point(|comment| comment.end() < up_to);
49+
dbg!(pivot);
50+
let leading = &dangling[..pivot];
51+
3552
// Split off the comments that follow after the operator and format them as trailing comments.
3653
// ```python
3754
// (not # comment
3855
// a)
3956
// ```
40-
trailing_comments(dangling).fmt(f)?;
57+
if !leading.is_empty() {
58+
hard_line_break().fmt(f)?;
59+
leading_comments(&dangling[..pivot]).fmt(f)?;
60+
}
61+
trailing_comments(&dangling[pivot..]).fmt(f)?;
62+
63+
let operator = match op {
64+
UnaryOp::Invert => "~",
65+
UnaryOp::Not => "not",
66+
UnaryOp::UAdd => "+",
67+
UnaryOp::USub => "-",
68+
};
69+
70+
token(operator).fmt(f)?;
4171

4272
// Insert a line break if the operand has comments but itself is not parenthesized.
4373
// ```python

0 commit comments

Comments
 (0)