Fix panic when formatting comments in unary expressions (#21501)
## Summary This is another attempt at https://github.com/astral-sh/ruff/pull/21410 that fixes https://github.com/astral-sh/ruff/issues/19226. @MichaReiser helped me get something working in a very helpful pairing session. I pushed one additional commit moving the comments back from leading comments to trailing comments, which I think retains more of the input formatting. I was inspired by Dylan's PR (#21185) to make one of these tables: <table> <thead> <tr> <th scope="col">Input</th> <th scope="col">Main</th> <th scope="col">PR</th> </tr> </thead> <tbody> <tr> <td><pre lang="python"> if ( not # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> <td><pre lang="python"> if ( # comment not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> <td><pre lang="python"> if ( not # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> </tr> <tr> <td><pre lang="python"> if ( # unary comment not # operand comment ( # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ) ): pass </pre></td> <td><pre lang="python"> if ( # unary comment # operand comment not ( # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ) ): pass </pre></td> <td><pre lang="python"> if ( # unary comment not # operand comment ( # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ) ): pass </pre></td> </tr> <tr> <td><pre lang="python"> if ( not # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> <td><pre lang="python"> if ( # comment not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> <td><pre lang="python"> if ( not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # comment + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> </tr> </tbody> </table> hopefully it helps even though the snippets are much wider here. The two main differences are (1) that we now retain own-line comments between the unary operator and its operand instead of moving these to leading comments on the operator itself, and (2) that we move end-of-line comments between the operator and operand to dangling end-of-line comments on the operand (the last example in the table). ## Test Plan Existing tests, plus new ones based on the issue. As I noted below, I also ran the output from main on the unary.py file back through this branch to check that we don't reformat code from main. This made me feel a bit better about not preview-gating the changes in this PR. ```shell > git show main:crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/unary.py | ruff format - | ./target/debug/ruff format --diff - > echo $? 0 ``` --------- Co-authored-by: Micha Reiser <micha@reiser.io> Co-authored-by: Takayuki Maeda <takoyaki0316@gmail.com>
This commit is contained in:
@@ -1890,9 +1890,11 @@ fn handle_lambda_comment<'a>(
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
|
||||
/// Move comment between a unary op and its operand before the unary op by marking them as trailing.
|
||||
/// Move an end-of-line comment between a unary op and its operand after the operand by marking
|
||||
/// it as dangling.
|
||||
///
|
||||
/// For example, given:
|
||||
///
|
||||
/// ```python
|
||||
/// (
|
||||
/// not # comment
|
||||
@@ -1900,8 +1902,13 @@ fn handle_lambda_comment<'a>(
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// The `# comment` will be attached as a dangling comment on the enclosing node, to ensure that
|
||||
/// it remains on the same line as the operator.
|
||||
/// the `# comment` will be attached as a dangling comment on the unary op and formatted as:
|
||||
///
|
||||
/// ```python
|
||||
/// (
|
||||
/// not True # comment
|
||||
/// )
|
||||
/// ```
|
||||
fn handle_unary_op_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
unary_op: &'a ast::ExprUnaryOp,
|
||||
@@ -1923,8 +1930,8 @@ fn handle_unary_op_comment<'a>(
|
||||
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)
|
||||
if comment.end() < up_to && comment.line_position().is_end_of_line() {
|
||||
CommentPlacement::dangling(unary_op, comment)
|
||||
} else {
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user