about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-06-06 08:37:03 +0200
committerGitHub <noreply@github.com>2022-06-06 08:37:03 +0200
commit554674b98cb5fdf2fd55620775d359b16423817c (patch)
treeee2bc35c4c1cf1c156d29b647729f80e1e74d402
parentc9c6c2e7de0b651617e0cc2b5c1ae368c516361e (diff)
parent4f85a73e516530844aecc44fb91d0db7604f7ac4 (diff)
downloadrust-554674b98cb5fdf2fd55620775d359b16423817c.tar.gz
rust-554674b98cb5fdf2fd55620775d359b16423817c.zip
Rollup merge of #97759 - WaffleLapkin:recover_label_expr, r=compiler-errors
Suggest adding `{}` for `'label: non_block_expr`

Adds suggestions like this:
```text
help: consider enclosing expression in a block
  |
3 |     'l {0};
  |        + +
```

inspired by https://github.com/rust-lang/rust/issues/48594#issuecomment-1146744400

r? ``@compiler-errors``
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs64
-rw-r--r--src/test/ui/parser/labeled-no-colon-expr.stderr6
-rw-r--r--src/test/ui/parser/recover-labeled-non-block-expr.fixed27
-rw-r--r--src/test/ui/parser/recover-labeled-non-block-expr.rs26
-rw-r--r--src/test/ui/parser/recover-labeled-non-block-expr.stderr80
5 files changed, 188 insertions, 15 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 63c7decbb2f..b786c52e688 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -13,10 +13,13 @@ use rustc_ast::tokenstream::Spacing;
 use rustc_ast::util::classify;
 use rustc_ast::util::literal::LitError;
 use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
+use rustc_ast::visit::Visitor;
+use rustc_ast::StmtKind;
 use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
 use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
 use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
 use rustc_ast_pretty::pprust;
+use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_session::lint::BuiltinLintDiagnostics;
@@ -1548,9 +1551,66 @@ impl<'a> Parser<'a> {
             Ok(self.mk_expr_err(lo))
         } else {
             let msg = "expected `while`, `for`, `loop` or `{` after a label";
-            self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
+
+            let mut err = self.struct_span_err(self.token.span, msg);
+            err.span_label(self.token.span, msg);
+
             // Continue as an expression in an effort to recover on `'label: non_block_expr`.
-            self.parse_expr()
+            let expr = self.parse_expr().map(|expr| {
+                let span = expr.span;
+
+                let found_labeled_breaks = {
+                    struct FindLabeledBreaksVisitor(bool);
+
+                    impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor {
+                        fn visit_expr_post(&mut self, ex: &'ast Expr) {
+                            if let ExprKind::Break(Some(_label), _) = ex.kind {
+                                self.0 = true;
+                            }
+                        }
+                    }
+
+                    let mut vis = FindLabeledBreaksVisitor(false);
+                    vis.visit_expr(&expr);
+                    vis.0
+                };
+
+                // Suggestion involves adding a (as of time of writing this, unstable) labeled block.
+                //
+                // If there are no breaks that may use this label, suggest removing the label and
+                // recover to the unmodified expression.
+                if !found_labeled_breaks {
+                    let msg = "consider removing the label";
+                    err.span_suggestion_verbose(
+                        lo.until(span),
+                        msg,
+                        "",
+                        Applicability::MachineApplicable,
+                    );
+
+                    return expr;
+                }
+
+                let sugg_msg = "consider enclosing expression in a block";
+                let suggestions = vec![
+                    (span.shrink_to_lo(), "{ ".to_owned()),
+                    (span.shrink_to_hi(), " }".to_owned()),
+                ];
+
+                err.multipart_suggestion_verbose(
+                    sugg_msg,
+                    suggestions,
+                    Applicability::MachineApplicable,
+                );
+
+                // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`.
+                let stmt = self.mk_stmt(span, StmtKind::Expr(expr));
+                let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span);
+                self.mk_expr(span, ExprKind::Block(blk, label), ThinVec::new())
+            });
+
+            err.emit();
+            expr
         }?;
 
         if !ate_colon && consume_colon {
diff --git a/src/test/ui/parser/labeled-no-colon-expr.stderr b/src/test/ui/parser/labeled-no-colon-expr.stderr
index 26884dc5d74..5c9597c440c 100644
--- a/src/test/ui/parser/labeled-no-colon-expr.stderr
+++ b/src/test/ui/parser/labeled-no-colon-expr.stderr
@@ -47,6 +47,12 @@ error: expected `while`, `for`, `loop` or `{` after a label
    |
 LL |     'l4 0;
    |         ^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider removing the label
+   |
+LL -     'l4 0;
+LL +     0;
+   | 
 
 error: labeled expression must be followed by `:`
   --> $DIR/labeled-no-colon-expr.rs:8:9
diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.fixed b/src/test/ui/parser/recover-labeled-non-block-expr.fixed
new file mode 100644
index 00000000000..fe546a71971
--- /dev/null
+++ b/src/test/ui/parser/recover-labeled-non-block-expr.fixed
@@ -0,0 +1,27 @@
+// run-rustfix
+#![feature(label_break_value)]
+fn main() {
+    let _ = 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+
+    match () { () => {}, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    'label: { match () { () => break 'label, } }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    #[allow(unused_labels)]
+    'label: { match () { () => 'lp: loop { break 'lp 0 }, } }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+
+    let x = 1;
+    let _i = 'label: { match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+        0 => 42,
+        1 if false => break 'label 17,
+        1 => {
+            if true {
+                break 'label 13
+            } else {
+                break 'label 0;
+            }
+        }
+        _ => 1,
+    } };
+
+    let other = 3;
+    let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+}
diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.rs b/src/test/ui/parser/recover-labeled-non-block-expr.rs
index be92170acf0..35862e2eef9 100644
--- a/src/test/ui/parser/recover-labeled-non-block-expr.rs
+++ b/src/test/ui/parser/recover-labeled-non-block-expr.rs
@@ -1,5 +1,27 @@
+// run-rustfix
+#![feature(label_break_value)]
 fn main() {
-    'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    let _ = 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
 
-    let _recovery_witness: () = 0; //~ ERROR mismatched types
+    'label: match () { () => {}, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    'label: match () { () => break 'label, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    #[allow(unused_labels)]
+    'label: match () { () => 'lp: loop { break 'lp 0 }, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+
+    let x = 1;
+    let _i = 'label: match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+        0 => 42,
+        1 if false => break 'label 17,
+        1 => {
+            if true {
+                break 'label 13
+            } else {
+                break 'label 0;
+            }
+        }
+        _ => 1,
+    };
+
+    let other = 3;
+    let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other }); //~ ERROR expected `while`, `for`, `loop` or `{` after a label
 }
diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr
index 771a915288c..767389c4808 100644
--- a/src/test/ui/parser/recover-labeled-non-block-expr.stderr
+++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr
@@ -1,17 +1,75 @@
 error: expected `while`, `for`, `loop` or `{` after a label
-  --> $DIR/recover-labeled-non-block-expr.rs:2:13
+  --> $DIR/recover-labeled-non-block-expr.rs:4:21
    |
-LL |     'label: 1 + 1;
-   |             ^ expected `while`, `for`, `loop` or `{` after a label
+LL |     let _ = 'label: 1 + 1;
+   |                     ^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider removing the label
+   |
+LL -     let _ = 'label: 1 + 1;
+LL +     let _ = 1 + 1;
+   | 
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:6:13
+   |
+LL |     'label: match () { () => {}, };
+   |             ^^^^^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider removing the label
+   |
+LL -     'label: match () { () => {}, };
+LL +     match () { () => {}, };
+   | 
 
-error[E0308]: mismatched types
-  --> $DIR/recover-labeled-non-block-expr.rs:4:33
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:7:13
+   |
+LL |     'label: match () { () => break 'label, };
+   |             ^^^^^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider enclosing expression in a block
+   |
+LL |     'label: { match () { () => break 'label, } };
+   |             +                                  +
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:9:13
+   |
+LL |     'label: match () { () => 'lp: loop { break 'lp 0 }, };
+   |             ^^^^^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider enclosing expression in a block
+   |
+LL |     'label: { match () { () => 'lp: loop { break 'lp 0 }, } };
+   |             +                                               +
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:12:22
+   |
+LL |     let _i = 'label: match x {
+   |                      ^^^^^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider enclosing expression in a block
+   |
+LL ~     let _i = 'label: { match x {
+LL |         0 => 42,
+LL |         1 if false => break 'label 17,
+LL |         1 => {
+LL |             if true {
+LL |                 break 'label 13
+ ...
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:26:24
+   |
+LL |     let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other });
+   |                        ^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider enclosing expression in a block
    |
-LL |     let _recovery_witness: () = 0;
-   |                            --   ^ expected `()`, found integer
-   |                            |
-   |                            expected due to this
+LL |     let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) };
+   |                        +                                                           +
 
-error: aborting due to 2 previous errors
+error: aborting due to 6 previous errors
 
-For more information about this error, try `rustc --explain E0308`.