Skip to content

Commit

Permalink
Rollup merge of rust-lang#108388 - ohno418:better-suggestion-on-malfo…
Browse files Browse the repository at this point in the history
…rmed-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 rust-lang#107959.

r? diagnostics
  • Loading branch information
Dylan-DPC committed Feb 24, 2023
2 parents 4aff2c5 + 0e42298 commit 8acbfe2
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 7 deletions.
6 changes: 5 additions & 1 deletion compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,11 @@ impl<'a> Parser<'a> {
let initial_semicolon = self.token.span;

while self.eat(&TokenKind::Semi) {
let _ = self.parse_stmt(ForceCollect::Yes)?;
let _ =
self.parse_stmt_without_recovery(false, ForceCollect::Yes).unwrap_or_else(|e| {
e.cancel();
None
});
}

expect_err.set_primary_message(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@
// If this recovery happens, then plenty of errors are emitted. Here, we expect
// only one error.
//
// This is part of issue #88065:
// This is part of the following issues:
// https://github.com/rust-lang/rust/issues/88065
// https://github.com/rust-lang/rust/issues/107959

// run-rustfix

fn main() {
// Closure with multiple expressions delimited by semicolon.
let num = 5;
(1..num).reduce(|a, b| {
//~^ ERROR: closure bodies that contain statements must be surrounded by braces
println!("{}", a);
a * b
}).unwrap();

// Closure with a single expression ended by a semicolon.
let mut v = vec![1, 2, 3];
v.iter_mut().for_each(|x| {*x = *x+1;});
//~^ ERROR: closure bodies that contain statements must be surrounded by braces
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@
// If this recovery happens, then plenty of errors are emitted. Here, we expect
// only one error.
//
// This is part of issue #88065:
// This is part of the following issues:
// https://github.com/rust-lang/rust/issues/88065
// https://github.com/rust-lang/rust/issues/107959

// run-rustfix

fn main() {
// Closure with multiple expressions delimited by semicolon.
let num = 5;
(1..num).reduce(|a, b|
//~^ ERROR: closure bodies that contain statements must be surrounded by braces
println!("{}", a);
a * b
).unwrap();

// Closure with a single expression ended by a semicolon.
let mut v = vec![1, 2, 3];
v.iter_mut().for_each(|x|*x = *x+1;);
//~^ ERROR: closure bodies that contain statements must be surrounded by braces
}
33 changes: 29 additions & 4 deletions tests/ui/expr/malformed_closure/missing_braces_around_block.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: closure bodies that contain statements must be surrounded by braces
--> $DIR/missing_braces_around_block.rs:14:26
--> $DIR/missing_braces_around_block.rs:16:26
|
LL | (1..num).reduce(|a, b|
| ^
Expand All @@ -8,14 +8,14 @@ LL | ).unwrap();
| ^
|
note: statement found outside of a block
--> $DIR/missing_braces_around_block.rs:16:26
--> $DIR/missing_braces_around_block.rs:18:26
|
LL | println!("{}", a);
| -----------------^ 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
--> $DIR/missing_braces_around_block.rs:14:21
--> $DIR/missing_braces_around_block.rs:16:21
|
LL | (1..num).reduce(|a, b|
| _____________________^
Expand All @@ -34,5 +34,30 @@ LL | a * b
LL ~ }).unwrap();
|

error: aborting due to previous error
error: closure bodies that contain statements must be surrounded by braces
--> $DIR/missing_braces_around_block.rs:24:29
|
LL | v.iter_mut().for_each(|x|*x = *x+1;);
| ^ ^
|
note: statement found outside of a block
--> $DIR/missing_braces_around_block.rs:24:39
|
LL | v.iter_mut().for_each(|x|*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
--> $DIR/missing_braces_around_block.rs:24:27
|
LL | v.iter_mut().for_each(|x|*x = *x+1;);
| ^^^^^^^^^^^^ - ...but likely you meant the closure to end here
| |
| this is the parsed closure...
help: try adding braces
|
LL | v.iter_mut().for_each(|x| {*x = *x+1;});
| + +

error: aborting due to 2 previous errors

0 comments on commit 8acbfe2

Please sign in to comment.