Skip to content

Commit 8acbfe2

Browse files
authored
Rollup merge of #108388 - ohno418:better-suggestion-on-malformed-closure, r=davidtwco
parser: provide better suggestions and errors on closures with braces missing We currently provide wrong suggestions and unhelpful errors on closure bodies with braces missing. For example, given the following code: ```rust fn main() { let _x = Box::new(|x|x+1;); } ``` the current output is: ``` error: expected expression, found `)` --> ./main.rs:2:30 | 2 | let _x = Box::new(|x|x+1;); | ^ expected expression error: closure bodies that contain statements must be surrounded by braces --> ./main.rs:2:25 | 2 | let _x = Box::new(|x|x+1;); | ^ 3 | } | ^ | note: statement found outside of a block --> ./main.rs:2:29 | 2 | let _x = Box::new(|x|x+1;); | ---^ this `;` turns the preceding closure into a statement | | | this expression is a statement because of the trailing semicolon note: the closure body may be incorrectly delimited --> ./main.rs:2:23 | 2 | let _x = Box::new(|x|x+1;); | ^^^^^^ this is the parsed closure... 3 | } | - ...but likely you meant the closure to end here help: try adding braces | 2 ~ let _x = Box::new(|x| {x+1;); 3 ~ }} | error: expected `;`, found `}` --> ./main.rs:2:32 | 2 | let _x = Box::new(|x|x+1;); | ^ help: add `;` here 3 | } | - unexpected token error: aborting due to 3 previous errors ``` We got 3 errors, but all but the second are unnecessary or just wrong. This commit allows outputting correct suggestions and errors. The above code would output like this: ``` error: closure bodies that contain statements must be surrounded by braces --> ./main.rs:2:25 | 2 | let _x = Box::new(|x|x+1;); | ^ ^ | note: statement found outside of a block --> ./main.rs:2:29 | 2 | let _x = Box::new(|x|x+1;); | ---^ this `;` turns the preceding closure into a statement | | | this expression is a statement because of the trailing semicolon note: the closure body may be incorrectly delimited --> ./main.rs:2:23 | 2 | let _x = Box::new(|x|x+1;); | ^^^^^^ - ...but likely you meant the closure to end here | | | this is the parsed closure... help: try adding braces | 2 | let _x = Box::new(|x| {x+1;}); | + + error: aborting due to previous error ``` Fixes #107959. r? diagnostics
2 parents 4aff2c5 + 0e42298 commit 8acbfe2

File tree

4 files changed

+50
-7
lines changed

4 files changed

+50
-7
lines changed

compiler/rustc_parse/src/parser/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,11 @@ impl<'a> Parser<'a> {
982982
let initial_semicolon = self.token.span;
983983

984984
while self.eat(&TokenKind::Semi) {
985-
let _ = self.parse_stmt(ForceCollect::Yes)?;
985+
let _ =
986+
self.parse_stmt_without_recovery(false, ForceCollect::Yes).unwrap_or_else(|e| {
987+
e.cancel();
988+
None
989+
});
986990
}
987991

988992
expect_err.set_primary_message(

tests/ui/expr/malformed_closure/missing_braces_around_block.fixed

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@
44
// If this recovery happens, then plenty of errors are emitted. Here, we expect
55
// only one error.
66
//
7-
// This is part of issue #88065:
7+
// This is part of the following issues:
88
// https://github.com/rust-lang/rust/issues/88065
9+
// https://github.com/rust-lang/rust/issues/107959
910

1011
// run-rustfix
1112

1213
fn main() {
14+
// Closure with multiple expressions delimited by semicolon.
1315
let num = 5;
1416
(1..num).reduce(|a, b| {
1517
//~^ ERROR: closure bodies that contain statements must be surrounded by braces
1618
println!("{}", a);
1719
a * b
1820
}).unwrap();
21+
22+
// Closure with a single expression ended by a semicolon.
23+
let mut v = vec![1, 2, 3];
24+
v.iter_mut().for_each(|x| {*x = *x+1;});
25+
//~^ ERROR: closure bodies that contain statements must be surrounded by braces
1926
}

tests/ui/expr/malformed_closure/missing_braces_around_block.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@
44
// If this recovery happens, then plenty of errors are emitted. Here, we expect
55
// only one error.
66
//
7-
// This is part of issue #88065:
7+
// This is part of the following issues:
88
// https://github.com/rust-lang/rust/issues/88065
9+
// https://github.com/rust-lang/rust/issues/107959
910

1011
// run-rustfix
1112

1213
fn main() {
14+
// Closure with multiple expressions delimited by semicolon.
1315
let num = 5;
1416
(1..num).reduce(|a, b|
1517
//~^ ERROR: closure bodies that contain statements must be surrounded by braces
1618
println!("{}", a);
1719
a * b
1820
).unwrap();
21+
22+
// Closure with a single expression ended by a semicolon.
23+
let mut v = vec![1, 2, 3];
24+
v.iter_mut().for_each(|x|*x = *x+1;);
25+
//~^ ERROR: closure bodies that contain statements must be surrounded by braces
1926
}

tests/ui/expr/malformed_closure/missing_braces_around_block.stderr

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: closure bodies that contain statements must be surrounded by braces
2-
--> $DIR/missing_braces_around_block.rs:14:26
2+
--> $DIR/missing_braces_around_block.rs:16:26
33
|
44
LL | (1..num).reduce(|a, b|
55
| ^
@@ -8,14 +8,14 @@ LL | ).unwrap();
88
| ^
99
|
1010
note: statement found outside of a block
11-
--> $DIR/missing_braces_around_block.rs:16:26
11+
--> $DIR/missing_braces_around_block.rs:18:26
1212
|
1313
LL | println!("{}", a);
1414
| -----------------^ this `;` turns the preceding closure into a statement
1515
| |
1616
| this expression is a statement because of the trailing semicolon
1717
note: the closure body may be incorrectly delimited
18-
--> $DIR/missing_braces_around_block.rs:14:21
18+
--> $DIR/missing_braces_around_block.rs:16:21
1919
|
2020
LL | (1..num).reduce(|a, b|
2121
| _____________________^
@@ -34,5 +34,30 @@ LL | a * b
3434
LL ~ }).unwrap();
3535
|
3636

37-
error: aborting due to previous error
37+
error: closure bodies that contain statements must be surrounded by braces
38+
--> $DIR/missing_braces_around_block.rs:24:29
39+
|
40+
LL | v.iter_mut().for_each(|x|*x = *x+1;);
41+
| ^ ^
42+
|
43+
note: statement found outside of a block
44+
--> $DIR/missing_braces_around_block.rs:24:39
45+
|
46+
LL | v.iter_mut().for_each(|x|*x = *x+1;);
47+
| ---------^ this `;` turns the preceding closure into a statement
48+
| |
49+
| this expression is a statement because of the trailing semicolon
50+
note: the closure body may be incorrectly delimited
51+
--> $DIR/missing_braces_around_block.rs:24:27
52+
|
53+
LL | v.iter_mut().for_each(|x|*x = *x+1;);
54+
| ^^^^^^^^^^^^ - ...but likely you meant the closure to end here
55+
| |
56+
| this is the parsed closure...
57+
help: try adding braces
58+
|
59+
LL | v.iter_mut().for_each(|x| {*x = *x+1;});
60+
| + +
61+
62+
error: aborting due to 2 previous errors
3863

0 commit comments

Comments
 (0)