diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 63c7decbb2f..761d521a07e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -13,10 +13,12 @@ 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::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 +1550,33 @@ 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 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..4d0d9c4a00b 100644 --- a/src/test/ui/parser/labeled-no-colon-expr.stderr +++ b/src/test/ui/parser/labeled-no-colon-expr.stderr @@ -47,6 +47,11 @@ error: expected `while`, `for`, `loop` or `{` after a label | LL | 'l4 0; | ^ expected `while`, `for`, `loop` or `{` after a label + | +help: consider enclosing expression in a block + | +LL | 'l4 {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..dbe2f2e0bdd --- /dev/null +++ b/src/test/ui/parser/recover-labeled-non-block-expr.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![feature(label_break_value)] +fn main() { + #[allow(unused_labels)] + 'label: {1 + 1}; //~ ERROR expected `while`, `for`, `loop` or `{` after a label + + 'label: {match () { () => break 'label, }}; //~ 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..9c22c073712 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,25 @@ +// run-rustfix +#![feature(label_break_value)] fn main() { + #[allow(unused_labels)] 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label - let _recovery_witness: () = 0; //~ ERROR mismatched types + 'label: match () { () => break 'label, }; //~ 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..2e4e45d6171 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,51 @@ 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:5:13 | LL | 'label: 1 + 1; | ^ expected `while`, `for`, `loop` or `{` after a label - -error[E0308]: mismatched types - --> $DIR/recover-labeled-non-block-expr.rs:4:33 | -LL | let _recovery_witness: () = 0; - | -- ^ expected `()`, found integer - | | - | expected due to this +help: consider enclosing expression in a block + | +LL | 'label: {1 + 1}; + | + + -error: aborting due to 2 previous errors +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:10: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:24: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 _val = 'label: {(1, if other == 3 { break 'label (2, 3) } else { other })}; + | + + + +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`.