mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:56 +00:00
Add dangling comment handling for lambda
expressions (#7493)
## Summary This PR adds dangling comment handling for `lambda` expressions. In short, comments around the `lambda` and the `:` are all considered dangling. Comments that come between the `lambda` and the `:` may be moved after the colon for simplicity (this is an odd position for a comment anyway), unless they also precede the lambda parameters, in which case they're formatted before the parameters. Closes https://github.com/astral-sh/ruff/issues/7470. ## Test Plan `cargo test` No change in similarity. Before: | project | similarity index | total files | changed files | |--------------|------------------:|------------------:|------------------:| | cpython | 0.76083 | 1789 | 1632 | | django | 0.99982 | 2760 | 37 | | transformers | 0.99957 | 2587 | 398 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99983 | 3496 | 18 | | warehouse | 0.99929 | 648 | 16 | | zulip | 0.99962 | 1437 | 22 | After: | project | similarity index | total files | changed files | |--------------|------------------:|------------------:|------------------:| | cpython | 0.76083 | 1789 | 1632 | | django | 0.99982 | 2760 | 37 | | transformers | 0.99957 | 2587 | 398 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99983 | 3496 | 18 | | warehouse | 0.99929 | 648 | 16 | | zulip | 0.99962 | 1437 | 22 |
This commit is contained in:
parent
e07670ad97
commit
4c4eceee36
5 changed files with 310 additions and 33 deletions
|
@ -152,4 +152,54 @@ lambda *x\
|
|||
x: x
|
||||
)
|
||||
|
||||
lambda: ( # comment
|
||||
x)
|
||||
|
||||
(
|
||||
lambda: # comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda:
|
||||
# comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment
|
||||
:
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
# comment
|
||||
:
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda: # comment
|
||||
( # comment
|
||||
x
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda # 1
|
||||
# 2
|
||||
x # 3
|
||||
# 4
|
||||
: # 5
|
||||
# 6
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
x,
|
||||
# comment
|
||||
y:
|
||||
z
|
||||
)
|
||||
|
|
|
@ -229,6 +229,7 @@ fn handle_enclosed_comment<'a>(
|
|||
}
|
||||
AnyNodeRef::ExprUnaryOp(unary_op) => handle_unary_op_comment(comment, unary_op, locator),
|
||||
AnyNodeRef::ExprNamedExpr(_) => handle_named_expr_comment(comment, locator),
|
||||
AnyNodeRef::ExprLambda(lambda) => handle_lambda_comment(comment, lambda, locator),
|
||||
AnyNodeRef::ExprDict(_) => handle_dict_unpacking_comment(comment, locator)
|
||||
.or_else(|comment| handle_bracketed_end_of_line_comment(comment, locator))
|
||||
.or_else(|comment| handle_key_value_comment(comment, locator)),
|
||||
|
@ -1687,6 +1688,119 @@ fn handle_named_expr_comment<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Handles comments around the `:` token in a lambda expression.
|
||||
///
|
||||
/// For parameterized lambdas, both the comments between the `lambda` and the parameters, and the
|
||||
/// comments between the parameters and the body, are considered dangling, as is the case for all
|
||||
/// of the following:
|
||||
///
|
||||
/// ```python
|
||||
/// (
|
||||
/// lambda # 1
|
||||
/// # 2
|
||||
/// x
|
||||
/// : # 3
|
||||
/// # 4
|
||||
/// y
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// For non-parameterized lambdas, all comments before the body are considered dangling, as is the
|
||||
/// case for all of the following:
|
||||
///
|
||||
/// ```python
|
||||
/// (
|
||||
/// lambda # 1
|
||||
/// # 2
|
||||
/// : # 3
|
||||
/// # 4
|
||||
/// y
|
||||
/// )
|
||||
/// ```
|
||||
fn handle_lambda_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
lambda: &'a ast::ExprLambda,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
if let Some(parameters) = lambda.parameters.as_deref() {
|
||||
// Comments between the `lambda` and the parameters are dangling on the lambda:
|
||||
// ```python
|
||||
// (
|
||||
// lambda # comment
|
||||
// x:
|
||||
// y
|
||||
// )
|
||||
// ```
|
||||
if comment.start() < parameters.start() {
|
||||
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||
}
|
||||
|
||||
// Comments between the parameters and the body are dangling on the lambda:
|
||||
// ```python
|
||||
// (
|
||||
// lambda x: # comment
|
||||
// y
|
||||
// )
|
||||
// ```
|
||||
if parameters.end() < comment.start() && comment.start() < lambda.body.start() {
|
||||
// If the value is parenthesized, and the comment is within the parentheses, it should
|
||||
// be a leading comment on the value, not a dangling comment in the lambda, as in:
|
||||
// ```python
|
||||
// (
|
||||
// lambda x: ( # comment
|
||||
// y
|
||||
// )
|
||||
// )
|
||||
// ```
|
||||
let tokenizer = SimpleTokenizer::new(
|
||||
locator.contents(),
|
||||
TextRange::new(parameters.end(), comment.start()),
|
||||
);
|
||||
if tokenizer
|
||||
.skip_trivia()
|
||||
.any(|token| token.kind == SimpleTokenKind::LParen)
|
||||
{
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||
}
|
||||
} else {
|
||||
// Comments between the lambda and the body are dangling on the lambda:
|
||||
// ```python
|
||||
// (
|
||||
// lambda: # comment
|
||||
// y
|
||||
// )
|
||||
// ```
|
||||
if comment.start() < lambda.body.start() {
|
||||
// If the value is parenthesized, and the comment is within the parentheses, it should
|
||||
// be a leading comment on the value, not a dangling comment in the lambda, as in:
|
||||
// ```python
|
||||
// (
|
||||
// lambda: ( # comment
|
||||
// y
|
||||
// )
|
||||
// )
|
||||
// ```
|
||||
let tokenizer = SimpleTokenizer::new(
|
||||
locator.contents(),
|
||||
TextRange::new(lambda.start(), comment.start()),
|
||||
);
|
||||
if tokenizer
|
||||
.skip_trivia()
|
||||
.any(|token| token.kind == SimpleTokenKind::LParen)
|
||||
{
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||
}
|
||||
}
|
||||
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
|
||||
/// Attach trailing end-of-line comments on the operator as dangling comments on the enclosing
|
||||
/// node.
|
||||
///
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::ExprLambda;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
|
||||
|
@ -24,28 +25,40 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
|||
write!(f, [token("lambda")])?;
|
||||
|
||||
if let Some(parameters) = parameters {
|
||||
// In this context, a dangling comment can either be a comment between the `lambda` the
|
||||
// parameters, or a comment between the parameters and the body.
|
||||
let (dangling_before_parameters, dangling_after_parameters) = dangling
|
||||
.split_at(dangling.partition_point(|comment| comment.end() < parameters.start()));
|
||||
|
||||
if dangling_before_parameters.is_empty() {
|
||||
write!(f, [space()])?;
|
||||
} else {
|
||||
write!(f, [dangling_comments(dangling_before_parameters)])?;
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
space(),
|
||||
parameters
|
||||
.format()
|
||||
.with_options(ParametersParentheses::Never),
|
||||
]
|
||||
[parameters
|
||||
.format()
|
||||
.with_options(ParametersParentheses::Never)]
|
||||
)?;
|
||||
}
|
||||
|
||||
write!(f, [token(":")])?;
|
||||
write!(f, [token(":")])?;
|
||||
|
||||
if dangling.is_empty() {
|
||||
write!(f, [space()])?;
|
||||
if dangling_after_parameters.is_empty() {
|
||||
write!(f, [space()])?;
|
||||
} else {
|
||||
write!(f, [dangling_comments(dangling_after_parameters)])?;
|
||||
}
|
||||
} else {
|
||||
write!(f, [dangling_comments(dangling)])?;
|
||||
}
|
||||
write!(f, [token(":")])?;
|
||||
|
||||
// Insert hard line break if body has leading comment to ensure consistent formatting
|
||||
if comments.has_leading(body.as_ref()) {
|
||||
write!(f, [hard_line_break()])?;
|
||||
// In this context, a dangling comment is a comment between the `lambda` and the body.
|
||||
if dangling.is_empty() {
|
||||
write!(f, [space()])?;
|
||||
} else {
|
||||
write!(f, [dangling_comments(dangling)])?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, [body.format()])
|
||||
|
|
|
@ -17,7 +17,7 @@ impl FormatNodeRule<ExprNamedExpr> for FormatExprNamedExpr {
|
|||
range: _,
|
||||
} = item;
|
||||
|
||||
// This context, a dangling comment is an end-of-line comment on the same line as the `:=`.
|
||||
// This context, a dangling comment is a comment between the `:=` and the value.
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling = comments.dangling(item);
|
||||
|
||||
|
|
|
@ -158,7 +158,57 @@ lambda *x\
|
|||
x: x
|
||||
)
|
||||
|
||||
lambda: ( # comment
|
||||
x)
|
||||
|
||||
(
|
||||
lambda: # comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda:
|
||||
# comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment
|
||||
:
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
# comment
|
||||
:
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda: # comment
|
||||
( # comment
|
||||
x
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda # 1
|
||||
# 2
|
||||
x # 3
|
||||
# 4
|
||||
: # 5
|
||||
# 6
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
x,
|
||||
# comment
|
||||
y:
|
||||
z
|
||||
)
|
||||
```
|
||||
|
||||
## Output
|
||||
|
@ -220,8 +270,7 @@ lambda x: lambda y: lambda z: (
|
|||
# Trailing
|
||||
|
||||
a = (
|
||||
lambda:
|
||||
# Dangling
|
||||
lambda: # Dangling
|
||||
1
|
||||
)
|
||||
|
||||
|
@ -268,20 +317,18 @@ lambda a, /, c: a
|
|||
|
||||
# Dangling comments without parameters.
|
||||
(
|
||||
lambda:
|
||||
lambda: # 3
|
||||
None
|
||||
)
|
||||
|
||||
(
|
||||
lambda:
|
||||
# 3
|
||||
None
|
||||
)
|
||||
|
||||
(
|
||||
lambda:
|
||||
# 3
|
||||
None
|
||||
)
|
||||
|
||||
(
|
||||
lambda:
|
||||
# 1
|
||||
lambda: # 1
|
||||
# 2
|
||||
# 3
|
||||
# 4
|
||||
|
@ -289,30 +336,83 @@ lambda a, /, c: a
|
|||
)
|
||||
|
||||
(
|
||||
lambda # comment
|
||||
lambda
|
||||
# comment
|
||||
*x: x
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment 1
|
||||
lambda
|
||||
# comment 1
|
||||
# comment 2
|
||||
*x:
|
||||
*x:
|
||||
# comment 3
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment 1
|
||||
lambda # comment 1
|
||||
# comment 2
|
||||
*x: x # comment 3
|
||||
*x: # comment 3
|
||||
x
|
||||
)
|
||||
|
||||
lambda *x: x
|
||||
|
||||
(
|
||||
lambda # comment
|
||||
lambda
|
||||
# comment
|
||||
*x: x
|
||||
)
|
||||
|
||||
lambda: ( # comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda: # comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda:
|
||||
# comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda: # comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda:
|
||||
# comment
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda: # comment
|
||||
( # comment
|
||||
x
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda # 1
|
||||
# 2
|
||||
x: # 3
|
||||
# 4
|
||||
# 5
|
||||
# 6
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda x,
|
||||
# comment
|
||||
y: z
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue