From 741c65344b4f2c860cb1237f431ebe0da418b0f1 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 12 Jan 2023 11:28:47 +0000 Subject: [PATCH 1/3] Remove an `unwrap()` from parser that can be written as if-let-chain --- compiler/rustc_parse/src/parser/expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f5093fb02a875..1f6e33971050d 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1709,10 +1709,10 @@ impl<'a> Parser<'a> { fn parse_break_expr(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; let mut label = self.eat_label(); - let kind = if label.is_some() && self.token == token::Colon { + let kind = if self.token == token::Colon && let Some(label) = label.take() { // The value expression can be a labeled loop, see issue #86948, e.g.: // `loop { break 'label: loop { break 'label 42; }; }` - let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?; + let lexpr = self.parse_labeled_expr(label, true)?; self.sess.emit_err(LabeledLoopInBreak { span: lexpr.span, sub: WrapExpressionInParentheses { From b0609889d7ca7f11dcb90880caf97fdf411a9374 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 12 Jan 2023 19:25:32 +0000 Subject: [PATCH 2/3] Add a test for recovery of unticked labels --- tests/ui/parser/recover-unticked-labels.rs | 5 ++++ .../ui/parser/recover-unticked-labels.stderr | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/ui/parser/recover-unticked-labels.rs create mode 100644 tests/ui/parser/recover-unticked-labels.stderr diff --git a/tests/ui/parser/recover-unticked-labels.rs b/tests/ui/parser/recover-unticked-labels.rs new file mode 100644 index 0000000000000..88bd15d0ca8b9 --- /dev/null +++ b/tests/ui/parser/recover-unticked-labels.rs @@ -0,0 +1,5 @@ +fn main() { + 'label: loop { break label } //~ error: cannot find value `label` in this scope + 'label: loop { break label 0 } //~ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `0` + 'label: loop { continue label } //~ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `label` +} diff --git a/tests/ui/parser/recover-unticked-labels.stderr b/tests/ui/parser/recover-unticked-labels.stderr new file mode 100644 index 0000000000000..3b48c1224b3ad --- /dev/null +++ b/tests/ui/parser/recover-unticked-labels.stderr @@ -0,0 +1,25 @@ +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `0` + --> $DIR/recover-unticked-labels.rs:3:32 + | +LL | 'label: loop { break label 0 } + | ^ expected one of 8 possible tokens + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `label` + --> $DIR/recover-unticked-labels.rs:4:29 + | +LL | 'label: loop { continue label } + | ^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator + +error[E0425]: cannot find value `label` in this scope + --> $DIR/recover-unticked-labels.rs:2:26 + | +LL | 'label: loop { break label } + | ------ ^^^^^ + | | | + | | not found in this scope + | | help: use the similarly named label: `'label` + | a label with a similar name exists + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0425`. From 57d822a904e440b6020d39274ac40a0ed68d55c9 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 12 Jan 2023 19:40:22 +0000 Subject: [PATCH 3/3] Recover labels written as identifiers --- compiler/rustc_parse/src/parser/expr.rs | 57 +++++++++++++++++-- tests/ui/parser/recover-unticked-labels.fixed | 7 +++ tests/ui/parser/recover-unticked-labels.rs | 8 ++- .../ui/parser/recover-unticked-labels.stderr | 20 +++---- 4 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 tests/ui/parser/recover-unticked-labels.fixed diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1f6e33971050d..84c049efc5014 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1346,9 +1346,6 @@ impl<'a> Parser<'a> { err.span_label(sp, "while parsing this `loop` expression"); err }) - } else if self.eat_keyword(kw::Continue) { - let kind = ExprKind::Continue(self.eat_label()); - Ok(self.mk_expr(lo.to(self.prev_token.span), kind)) } else if self.eat_keyword(kw::Match) { let match_sp = self.prev_token.span; self.parse_match_expr().map_err(|mut err| { @@ -1372,6 +1369,8 @@ impl<'a> Parser<'a> { self.parse_try_block(lo) } else if self.eat_keyword(kw::Return) { self.parse_return_expr() + } else if self.eat_keyword(kw::Continue) { + self.parse_continue_expr(lo) } else if self.eat_keyword(kw::Break) { self.parse_break_expr() } else if self.eat_keyword(kw::Yield) { @@ -1724,8 +1723,8 @@ impl<'a> Parser<'a> { } else if self.token != token::OpenDelim(Delimiter::Brace) || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) { - let expr = self.parse_expr_opt()?; - if let Some(expr) = &expr { + let mut expr = self.parse_expr_opt()?; + if let Some(expr) = &mut expr { if label.is_some() && matches!( expr.kind, @@ -1743,7 +1742,19 @@ impl<'a> Parser<'a> { BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span), ); } + + // Recover `break label aaaaa` + if self.may_recover() + && let ExprKind::Path(None, p) = &expr.kind + && let [segment] = &*p.segments + && let &ast::PathSegment { ident, args: None, .. } = segment + && let Some(next) = self.parse_expr_opt()? + { + label = Some(self.recover_ident_into_label(ident)); + *expr = next; + } } + expr } else { None @@ -1752,6 +1763,23 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } + /// Parse `"continue" label?`. + fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P> { + let mut label = self.eat_label(); + + // Recover `continue label` -> `continue 'label` + if self.may_recover() + && label.is_none() + && let Some((ident, _)) = self.token.ident() + { + self.bump(); + label = Some(self.recover_ident_into_label(ident)); + } + + let kind = ExprKind::Continue(label); + Ok(self.mk_expr(lo.to(self.prev_token.span), kind)) + } + /// Parse `"yield" expr?`. fn parse_yield_expr(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; @@ -3037,6 +3065,25 @@ impl<'a> Parser<'a> { false } + /// Converts an ident into 'label and emits an "expected a label, found an identifier" error. + fn recover_ident_into_label(&mut self, ident: Ident) -> Label { + // Convert `label` -> `'label`, + // so that nameres doesn't complain about non-existing label + let label = format!("'{}", ident.name); + let ident = Ident { name: Symbol::intern(&label), span: ident.span }; + + self.struct_span_err(ident.span, "expected a label, found an identifier") + .span_suggestion( + ident.span, + "labels start with a tick", + label, + Applicability::MachineApplicable, + ) + .emit(); + + Label { ident } + } + /// Parses `ident (COLON expr)?`. fn parse_expr_field(&mut self) -> PResult<'a, ExprField> { let attrs = self.parse_outer_attributes()?; diff --git a/tests/ui/parser/recover-unticked-labels.fixed b/tests/ui/parser/recover-unticked-labels.fixed new file mode 100644 index 0000000000000..159d995b8dad3 --- /dev/null +++ b/tests/ui/parser/recover-unticked-labels.fixed @@ -0,0 +1,7 @@ +// run-rustfix + +fn main() { + 'label: loop { break 'label }; //~ error: cannot find value `label` in this scope + 'label: loop { break 'label 0 }; //~ error: expected a label, found an identifier + 'label: loop { continue 'label }; //~ error: expected a label, found an identifier +} diff --git a/tests/ui/parser/recover-unticked-labels.rs b/tests/ui/parser/recover-unticked-labels.rs index 88bd15d0ca8b9..56034de68449f 100644 --- a/tests/ui/parser/recover-unticked-labels.rs +++ b/tests/ui/parser/recover-unticked-labels.rs @@ -1,5 +1,7 @@ +// run-rustfix + fn main() { - 'label: loop { break label } //~ error: cannot find value `label` in this scope - 'label: loop { break label 0 } //~ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `0` - 'label: loop { continue label } //~ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `label` + 'label: loop { break label }; //~ error: cannot find value `label` in this scope + 'label: loop { break label 0 }; //~ error: expected a label, found an identifier + 'label: loop { continue label }; //~ error: expected a label, found an identifier } diff --git a/tests/ui/parser/recover-unticked-labels.stderr b/tests/ui/parser/recover-unticked-labels.stderr index 3b48c1224b3ad..c115dffb10e9c 100644 --- a/tests/ui/parser/recover-unticked-labels.stderr +++ b/tests/ui/parser/recover-unticked-labels.stderr @@ -1,19 +1,19 @@ -error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `0` - --> $DIR/recover-unticked-labels.rs:3:32 +error: expected a label, found an identifier + --> $DIR/recover-unticked-labels.rs:5:26 | -LL | 'label: loop { break label 0 } - | ^ expected one of 8 possible tokens +LL | 'label: loop { break label 0 }; + | ^^^^^ help: labels start with a tick: `'label` -error: expected one of `.`, `;`, `?`, `}`, or an operator, found `label` - --> $DIR/recover-unticked-labels.rs:4:29 +error: expected a label, found an identifier + --> $DIR/recover-unticked-labels.rs:6:29 | -LL | 'label: loop { continue label } - | ^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator +LL | 'label: loop { continue label }; + | ^^^^^ help: labels start with a tick: `'label` error[E0425]: cannot find value `label` in this scope - --> $DIR/recover-unticked-labels.rs:2:26 + --> $DIR/recover-unticked-labels.rs:4:26 | -LL | 'label: loop { break label } +LL | 'label: loop { break label }; | ------ ^^^^^ | | | | | not found in this scope