diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 716ababb00802..c1cca1186af49 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2874,7 +2874,12 @@ impl<'a> Parser<'a> { first_pat } - pub(crate) fn maybe_recover_unexpected_block_label(&mut self) -> bool { + /// If `loop_header` is `Some` and an unexpected block label is encountered, + /// it is suggested to be moved just before `loop_header`, else it is suggested to be removed. + pub(crate) fn maybe_recover_unexpected_block_label( + &mut self, + loop_header: Option, + ) -> bool { // Check for `'a : {` if !(self.check_lifetime() && self.look_ahead(1, |t| *t == token::Colon) @@ -2885,16 +2890,28 @@ impl<'a> Parser<'a> { let label = self.eat_label().expect("just checked if a label exists"); self.bump(); // eat `:` let span = label.ident.span.to(self.prev_token.span); - self.dcx() + let mut diag = self + .dcx() .struct_span_err(span, "block label not supported here") - .with_span_label(span, "not supported here") - .with_tool_only_span_suggestion( + .with_span_label(span, "not supported here"); + if let Some(loop_header) = loop_header { + diag.multipart_suggestion( + "if you meant to label the loop, move this label before the loop", + vec![ + (label.ident.span.until(self.token.span), String::from("")), + (loop_header.shrink_to_lo(), format!("{}: ", label.ident)), + ], + Applicability::MachineApplicable, + ); + } else { + diag.tool_only_span_suggestion( label.ident.span.until(self.token.span), "remove this block label", "", Applicability::MachineApplicable, - ) - .emit(); + ); + } + diag.emit(); true } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0774324eae742..cd931888fbaa1 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2286,7 +2286,7 @@ impl<'a> Parser<'a> { }); } - let (attrs, blk) = self.parse_block_common(lo, blk_mode, true)?; + let (attrs, blk) = self.parse_block_common(lo, blk_mode, true, None)?; Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs)) } @@ -2851,7 +2851,11 @@ impl<'a> Parser<'a> { )); } - let (attrs, loop_block) = self.parse_inner_attrs_and_block()?; + let (attrs, loop_block) = self.parse_inner_attrs_and_block( + // Only suggest moving erroneous block label to the loop header + // if there is not already a label there + opt_label.is_none().then_some(lo), + )?; let kind = ExprKind::ForLoop { pat, iter: expr, body: loop_block, label: opt_label, kind }; @@ -2894,11 +2898,17 @@ impl<'a> Parser<'a> { err.span_label(lo, "while parsing the condition of this `while` expression"); err })?; - let (attrs, body) = self.parse_inner_attrs_and_block().map_err(|mut err| { - err.span_label(lo, "while parsing the body of this `while` expression"); - err.span_label(cond.span, "this `while` condition successfully parsed"); - err - })?; + let (attrs, body) = self + .parse_inner_attrs_and_block( + // Only suggest moving erroneous block label to the loop header + // if there is not already a label there + opt_label.is_none().then_some(lo), + ) + .map_err(|mut err| { + err.span_label(lo, "while parsing the body of this `while` expression"); + err.span_label(cond.span, "this `while` condition successfully parsed"); + err + })?; self.recover_loop_else("while", lo)?; @@ -2912,7 +2922,11 @@ impl<'a> Parser<'a> { /// Parses `loop { ... }` (`loop` token already eaten). fn parse_expr_loop(&mut self, opt_label: Option