about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYutaro Ohno <yutaro.ono.418@gmail.com>2023-02-23 16:42:52 +0900
committerYutaro Ohno <yutaro.ono.418@gmail.com>2023-02-23 19:05:13 +0900
commit0e42298674757bbb3563e0deda477044cce8271d (patch)
treefebe57fec7980d762731fd493fdfe58eab26a371
parent8b1dbf728add722d4db894b9b986ec24e1cdb0a1 (diff)
downloadrust-0e42298674757bbb3563e0deda477044cce8271d.tar.gz
rust-0e42298674757bbb3563e0deda477044cce8271d.zip
parser: provide better 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:

```
fn main() {
    let _x = Box::new(|x|x+1;);
}
```

the current output is like this:

```
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 | }
  | ^
  |

...

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
```

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
```
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs6
-rw-r--r--tests/ui/expr/malformed_closure/missing_braces_around_block.fixed9
-rw-r--r--tests/ui/expr/malformed_closure/missing_braces_around_block.rs9
-rw-r--r--tests/ui/expr/malformed_closure/missing_braces_around_block.stderr33
4 files changed, 50 insertions, 7 deletions
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 623c82b37e0..fda9151478f 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -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(
diff --git a/tests/ui/expr/malformed_closure/missing_braces_around_block.fixed b/tests/ui/expr/malformed_closure/missing_braces_around_block.fixed
index c50b9a12b6d..a7a9db7d977 100644
--- a/tests/ui/expr/malformed_closure/missing_braces_around_block.fixed
+++ b/tests/ui/expr/malformed_closure/missing_braces_around_block.fixed
@@ -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
 }
diff --git a/tests/ui/expr/malformed_closure/missing_braces_around_block.rs b/tests/ui/expr/malformed_closure/missing_braces_around_block.rs
index 58c81f3a6e2..b5690b2eca7 100644
--- a/tests/ui/expr/malformed_closure/missing_braces_around_block.rs
+++ b/tests/ui/expr/malformed_closure/missing_braces_around_block.rs
@@ -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
 }
diff --git a/tests/ui/expr/malformed_closure/missing_braces_around_block.stderr b/tests/ui/expr/malformed_closure/missing_braces_around_block.stderr
index dac9a8cfc69..039eef909fc 100644
--- a/tests/ui/expr/malformed_closure/missing_braces_around_block.stderr
+++ b/tests/ui/expr/malformed_closure/missing_braces_around_block.stderr
@@ -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