From 8c5dafdcb8c7a569f27bc1e6dbf32736885ece64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 20 Jan 2021 20:03:29 -0800 Subject: [PATCH] Parse loop labels missing a leading `'` When encountering the following typo: ```rust a: loop { break 'a; } ``` provide an appropriate suggestion. --- compiler/rustc_parse/src/parser/expr.rs | 54 ++++++++++++++++++--- src/test/ui/label/label_misspelled_2.rs | 7 ++- src/test/ui/label/label_misspelled_2.stderr | 32 +++++++----- 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 47869f775fe..cfd7ad48222 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -585,7 +585,7 @@ fn parse_assoc_op_cast( lhs_span: Span, expr_kind: fn(P, P) -> ExprKind, ) -> PResult<'a, P> { - let mk_expr = |this: &mut Self, rhs: P| { + let mk_expr = |this: &mut Self, lhs: P, rhs: P| { this.mk_expr( this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs), @@ -597,13 +597,49 @@ fn parse_assoc_op_cast( // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); let cast_expr = match self.parse_ty_no_plus() { - Ok(rhs) => mk_expr(self, rhs), + Ok(rhs) => mk_expr(self, lhs, rhs), Err(mut type_err) => { // Rewind to before attempting to parse the type with generics, to recover // from situations like `x as usize < y` in which we first tried to parse // `usize < y` as a type with generic arguments. let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); + // Check for typo of `'a: loop { break 'a }` with a missing `'`. + match (&lhs.kind, &self.token.kind) { + ( + // `foo: ` + ExprKind::Path(None, ast::Path { segments, .. }), + TokenKind::Ident(kw::For | kw::Loop | kw::While, false), + ) if segments.len() == 1 => { + let snapshot = self.clone(); + let label = Label { + ident: Ident::from_str_and_span( + &format!("'{}", segments[0].ident), + segments[0].ident.span, + ), + }; + match self.parse_labeled_expr(label, AttrVec::new(), false) { + Ok(expr) => { + type_err.cancel(); + self.struct_span_err(label.ident.span, "malformed loop label") + .span_suggestion( + label.ident.span, + "use the correct loop label format", + label.ident.to_string(), + Applicability::MachineApplicable, + ) + .emit(); + return Ok(expr); + } + Err(mut err) => { + err.cancel(); + *self = snapshot; + } + } + } + _ => {} + } + match self.parse_path(PathStyle::Expr) { Ok(path) => { let (op_noun, op_verb) = match self.token.kind { @@ -630,7 +666,8 @@ fn parse_assoc_op_cast( op_noun, ); let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path))); + let expr = + mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path))); let expr_str = self .span_to_snippet(expr.span) @@ -1067,7 +1104,7 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P> { } else if self.eat_keyword(kw::While) { self.parse_while_expr(None, self.prev_token.span, attrs) } else if let Some(label) = self.eat_label() { - self.parse_labeled_expr(label, attrs) + self.parse_labeled_expr(label, attrs, true) } else if self.eat_keyword(kw::Loop) { self.parse_loop_expr(None, self.prev_token.span, attrs) } else if self.eat_keyword(kw::Continue) { @@ -1228,7 +1265,12 @@ fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { } /// Parse `'label: $expr`. The label is already parsed. - fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P> { + fn parse_labeled_expr( + &mut self, + label: Label, + attrs: AttrVec, + consume_colon: bool, + ) -> PResult<'a, P> { let lo = label.ident.span; let label = Some(label); let ate_colon = self.eat(&token::Colon); @@ -1247,7 +1289,7 @@ fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P< self.parse_expr() }?; - if !ate_colon { + if !ate_colon && consume_colon { self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span); } diff --git a/src/test/ui/label/label_misspelled_2.rs b/src/test/ui/label/label_misspelled_2.rs index 28bfe6f26cf..55bbe6b30a5 100644 --- a/src/test/ui/label/label_misspelled_2.rs +++ b/src/test/ui/label/label_misspelled_2.rs @@ -7,11 +7,10 @@ fn main() { 'b: for _ in 0..1 { break b; //~ ERROR cannot find value `b` in this scope } - c: for _ in 0..1 { //~ ERROR expected identifier, found keyword `for` - //~^ ERROR expected `<`, found reserved identifier `_` + c: for _ in 0..1 { //~ ERROR malformed loop label break 'c; } - d: for _ in 0..1 { - break ; + d: for _ in 0..1 { //~ ERROR malformed loop label + break d; //~ ERROR cannot find value `d` in this scope } } diff --git a/src/test/ui/label/label_misspelled_2.stderr b/src/test/ui/label/label_misspelled_2.stderr index c1921b95274..960646d9894 100644 --- a/src/test/ui/label/label_misspelled_2.stderr +++ b/src/test/ui/label/label_misspelled_2.stderr @@ -1,19 +1,14 @@ -error: expected identifier, found keyword `for` - --> $DIR/label_misspelled_2.rs:10:8 +error: malformed loop label + --> $DIR/label_misspelled_2.rs:10:5 | LL | c: for _ in 0..1 { - | ^^^ expected identifier, found keyword + | ^ help: use the correct loop label format: `'c` -error: expected `<`, found reserved identifier `_` - --> $DIR/label_misspelled_2.rs:10:12 +error: malformed loop label + --> $DIR/label_misspelled_2.rs:13:5 | -LL | c: for _ in 0..1 { - | - ^ expected `<` - | | - | tried to parse a type due to this type ascription - | - = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` - = note: see issue #23416 for more information +LL | d: for _ in 0..1 { + | ^ help: use the correct loop label format: `'d` error[E0425]: cannot find value `b` in this scope --> $DIR/label_misspelled_2.rs:8:15 @@ -26,6 +21,17 @@ LL | break b; | not found in this scope | help: use the similarly named label: `'b` -error: aborting due to 3 previous errors +error[E0425]: cannot find value `d` in this scope + --> $DIR/label_misspelled_2.rs:14:15 + | +LL | d: for _ in 0..1 { + | - a label with a similar name exists +LL | break d; + | ^ + | | + | not found in this scope + | help: use the similarly named label: `'d` + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0425`.