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 https://github.com/rust-lang/rust/issues/107959.

r? diagnostics
This commit is contained in:
Dylan DPC 2023-02-24 12:02:44 +05:30 committed by GitHub
commit 8acbfe27d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 7 deletions

View File

@ -982,7 +982,11 @@ fn recover_missing_braces_around_closure_body(
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(

View File

@ -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
}

View File

@ -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
}

View File

@ -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|
| ^
@ -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|
| _____________________^
@ -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