Skip to content

Commit c36f760

Browse files
authored
Turn closure bodies from exprs into blocks if there are comments (#4396)
* Added test cases * Kind of working, need to refactor. * Final code * Update closures.rs * Update comments. * Use argument span instead of calculating between_span. * Add test cases for #[attr]s. * Use first attr instead of body span, if it exists. * Use rewrite_missing_comment so comments are correctly folded into blocks. * Update closures.rs * Use ? not unwrap()
1 parent d7f0d76 commit c36f760

File tree

4 files changed

+130
-5
lines changed

4 files changed

+130
-5
lines changed

src/formatting/closures.rs

+30
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_span::Span;
44
use crate::config::{lists::*, IndentStyle, SeparatorTactic};
55
use crate::formatting::{
66
attr::get_attrs_from_stmt,
7+
comment::{contains_comment, rewrite_missing_comment},
78
expr::{
89
block_contains_comment, is_simple_block, is_unsafe_block, rewrite_block_with_visitor,
910
rewrite_cond,
@@ -34,6 +35,7 @@ pub(crate) fn rewrite_closure(
3435
fn_decl: &ast::FnDecl,
3536
body: &ast::Expr,
3637
span: Span,
38+
arg_span: Span,
3739
context: &RewriteContext<'_>,
3840
shape: Shape,
3941
) -> Option<String> {
@@ -65,6 +67,34 @@ pub(crate) fn rewrite_closure(
6567
rewrite_closure_block(block, &prefix, context, body_shape)
6668
})
6769
} else {
70+
// If there are comments between the fn decl and the body, the body (+ comments) need to be
71+
// wrapped in a block. Since there's no return type annotation on closures with expr
72+
// bodies, look for comments after the argument block.
73+
// Since attrs come before the body, check up to the first attr if there is one.
74+
let first_span = body
75+
.attrs
76+
.first()
77+
.map(|attr| attr.span)
78+
.unwrap_or(body.span);
79+
let between_span = Span::between(arg_span, first_span);
80+
if contains_comment(context.snippet(between_span)) {
81+
return rewrite_closure_with_block(body, &prefix, context, body_shape).and_then(|rw| {
82+
let mut parts = rw.splitn(2, "\n");
83+
let head = parts.next()?;
84+
let rest = parts.next()?;
85+
let block_shape = shape.block_indent(context.config.tab_spaces());
86+
let indent = block_shape.indent.to_string_with_newline(context.config);
87+
let missing_comment = rewrite_missing_comment(between_span, block_shape, context)?;
88+
Some(format!(
89+
"{}{}{}{}{}",
90+
head,
91+
indent,
92+
missing_comment,
93+
indent,
94+
rest.trim()
95+
))
96+
});
97+
}
6898
rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|| {
6999
// The closure originally had a non-block expression, but we can't fit on
70100
// one line, so we'll insert a block.

src/formatting/expr.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,16 @@ pub(crate) fn format_expr(
189189
Some("yield".to_string())
190190
}
191191
}
192-
ast::ExprKind::Closure(capture, ref is_async, movability, ref fn_decl, ref body, _) => {
193-
closures::rewrite_closure(
194-
capture, is_async, movability, fn_decl, body, expr.span, context, shape,
195-
)
196-
}
192+
ast::ExprKind::Closure(
193+
capture,
194+
ref is_async,
195+
movability,
196+
ref fn_decl,
197+
ref body,
198+
arg_span,
199+
) => closures::rewrite_closure(
200+
capture, is_async, movability, fn_decl, body, expr.span, arg_span, context, shape,
201+
),
197202
ast::ExprKind::Try(..) | ast::ExprKind::Field(..) | ast::ExprKind::MethodCall(..) => {
198203
rewrite_chain(expr, context, shape)
199204
}

tests/source/issue-4384.rs

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
fn main() {
2+
let single_comment = ||
3+
// this should be indented.
4+
1;
5+
6+
let block_comment = ||
7+
/* This is a long,
8+
* explanatory comment.
9+
* Put this all in a block.
10+
*/
11+
1;
12+
13+
let nested_closure = ||
14+
// indent + wrap in a block.
15+
|| 1;
16+
17+
let nested_comment_closure = ||
18+
// if you see this code in real life, run.
19+
||
20+
// nested closures don't need blocks,
21+
// but comments do.
22+
|| 1;
23+
24+
let attrb = ||
25+
// This block has an attr.
26+
#[foo]
27+
1;
28+
29+
let after_attr = ||
30+
// There's a comment before...
31+
#[attr]
32+
// and one after!
33+
1;
34+
35+
let one_line = || /* one line */ 1;
36+
37+
let one_comment_line = || // put this comment in body
38+
1;
39+
}

tests/target/issue-4384.rs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
fn main() {
2+
let single_comment = || {
3+
// this should be indented.
4+
1
5+
};
6+
7+
let block_comment = || {
8+
/* This is a long,
9+
* explanatory comment.
10+
* Put this all in a block.
11+
*/
12+
1
13+
};
14+
15+
let nested_closure = || {
16+
// indent + wrap in a block.
17+
|| 1
18+
};
19+
20+
let nested_comment_closure = || {
21+
// if you see this code in real life, run.
22+
|| {
23+
// nested closures don't need blocks,
24+
// but comments do.
25+
|| 1
26+
}
27+
};
28+
29+
let attrb = || {
30+
// This block has an attr.
31+
#[foo]
32+
1
33+
};
34+
35+
let after_attr = || {
36+
// There's a comment before...
37+
#[attr]
38+
// and one after!
39+
1
40+
};
41+
42+
let one_line = || {
43+
/* one line */
44+
1
45+
};
46+
47+
let one_comment_line = || {
48+
// put this comment in body
49+
1
50+
};
51+
}

0 commit comments

Comments
 (0)