Skip to content

Commit 8c5dafd

Browse files
committed
Parse loop labels missing a leading '
When encountering the following typo: ```rust a: loop { break 'a; } ``` provide an appropriate suggestion.
1 parent 74ddaf0 commit 8c5dafd

File tree

3 files changed

+70
-23
lines changed

3 files changed

+70
-23
lines changed

compiler/rustc_parse/src/parser/expr.rs

+48-6
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ impl<'a> Parser<'a> {
585585
lhs_span: Span,
586586
expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind,
587587
) -> PResult<'a, P<Expr>> {
588-
let mk_expr = |this: &mut Self, rhs: P<Ty>| {
588+
let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| {
589589
this.mk_expr(
590590
this.mk_expr_sp(&lhs, lhs_span, rhs.span),
591591
expr_kind(lhs, rhs),
@@ -597,13 +597,49 @@ impl<'a> Parser<'a> {
597597
// LessThan comparison after this cast.
598598
let parser_snapshot_before_type = self.clone();
599599
let cast_expr = match self.parse_ty_no_plus() {
600-
Ok(rhs) => mk_expr(self, rhs),
600+
Ok(rhs) => mk_expr(self, lhs, rhs),
601601
Err(mut type_err) => {
602602
// Rewind to before attempting to parse the type with generics, to recover
603603
// from situations like `x as usize < y` in which we first tried to parse
604604
// `usize < y` as a type with generic arguments.
605605
let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type);
606606

607+
// Check for typo of `'a: loop { break 'a }` with a missing `'`.
608+
match (&lhs.kind, &self.token.kind) {
609+
(
610+
// `foo: `
611+
ExprKind::Path(None, ast::Path { segments, .. }),
612+
TokenKind::Ident(kw::For | kw::Loop | kw::While, false),
613+
) if segments.len() == 1 => {
614+
let snapshot = self.clone();
615+
let label = Label {
616+
ident: Ident::from_str_and_span(
617+
&format!("'{}", segments[0].ident),
618+
segments[0].ident.span,
619+
),
620+
};
621+
match self.parse_labeled_expr(label, AttrVec::new(), false) {
622+
Ok(expr) => {
623+
type_err.cancel();
624+
self.struct_span_err(label.ident.span, "malformed loop label")
625+
.span_suggestion(
626+
label.ident.span,
627+
"use the correct loop label format",
628+
label.ident.to_string(),
629+
Applicability::MachineApplicable,
630+
)
631+
.emit();
632+
return Ok(expr);
633+
}
634+
Err(mut err) => {
635+
err.cancel();
636+
*self = snapshot;
637+
}
638+
}
639+
}
640+
_ => {}
641+
}
642+
607643
match self.parse_path(PathStyle::Expr) {
608644
Ok(path) => {
609645
let (op_noun, op_verb) = match self.token.kind {
@@ -630,7 +666,8 @@ impl<'a> Parser<'a> {
630666
op_noun,
631667
);
632668
let span_after_type = parser_snapshot_after_type.token.span;
633-
let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path)));
669+
let expr =
670+
mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path)));
634671

635672
let expr_str = self
636673
.span_to_snippet(expr.span)
@@ -1067,7 +1104,7 @@ impl<'a> Parser<'a> {
10671104
} else if self.eat_keyword(kw::While) {
10681105
self.parse_while_expr(None, self.prev_token.span, attrs)
10691106
} else if let Some(label) = self.eat_label() {
1070-
self.parse_labeled_expr(label, attrs)
1107+
self.parse_labeled_expr(label, attrs, true)
10711108
} else if self.eat_keyword(kw::Loop) {
10721109
self.parse_loop_expr(None, self.prev_token.span, attrs)
10731110
} else if self.eat_keyword(kw::Continue) {
@@ -1228,7 +1265,12 @@ impl<'a> Parser<'a> {
12281265
}
12291266

12301267
/// Parse `'label: $expr`. The label is already parsed.
1231-
fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P<Expr>> {
1268+
fn parse_labeled_expr(
1269+
&mut self,
1270+
label: Label,
1271+
attrs: AttrVec,
1272+
consume_colon: bool,
1273+
) -> PResult<'a, P<Expr>> {
12321274
let lo = label.ident.span;
12331275
let label = Some(label);
12341276
let ate_colon = self.eat(&token::Colon);
@@ -1247,7 +1289,7 @@ impl<'a> Parser<'a> {
12471289
self.parse_expr()
12481290
}?;
12491291

1250-
if !ate_colon {
1292+
if !ate_colon && consume_colon {
12511293
self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span);
12521294
}
12531295

src/test/ui/label/label_misspelled_2.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ fn main() {
77
'b: for _ in 0..1 {
88
break b; //~ ERROR cannot find value `b` in this scope
99
}
10-
c: for _ in 0..1 { //~ ERROR expected identifier, found keyword `for`
11-
//~^ ERROR expected `<`, found reserved identifier `_`
10+
c: for _ in 0..1 { //~ ERROR malformed loop label
1211
break 'c;
1312
}
14-
d: for _ in 0..1 {
15-
break ;
13+
d: for _ in 0..1 { //~ ERROR malformed loop label
14+
break d; //~ ERROR cannot find value `d` in this scope
1615
}
1716
}
+19-13
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1-
error: expected identifier, found keyword `for`
2-
--> $DIR/label_misspelled_2.rs:10:8
1+
error: malformed loop label
2+
--> $DIR/label_misspelled_2.rs:10:5
33
|
44
LL | c: for _ in 0..1 {
5-
| ^^^ expected identifier, found keyword
5+
| ^ help: use the correct loop label format: `'c`
66

7-
error: expected `<`, found reserved identifier `_`
8-
--> $DIR/label_misspelled_2.rs:10:12
7+
error: malformed loop label
8+
--> $DIR/label_misspelled_2.rs:13:5
99
|
10-
LL | c: for _ in 0..1 {
11-
| - ^ expected `<`
12-
| |
13-
| tried to parse a type due to this type ascription
14-
|
15-
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
16-
= note: see issue #23416 <https://github.com/rust-lang/rust/issues/23416> for more information
10+
LL | d: for _ in 0..1 {
11+
| ^ help: use the correct loop label format: `'d`
1712

1813
error[E0425]: cannot find value `b` in this scope
1914
--> $DIR/label_misspelled_2.rs:8:15
@@ -26,6 +21,17 @@ LL | break b;
2621
| not found in this scope
2722
| help: use the similarly named label: `'b`
2823

29-
error: aborting due to 3 previous errors
24+
error[E0425]: cannot find value `d` in this scope
25+
--> $DIR/label_misspelled_2.rs:14:15
26+
|
27+
LL | d: for _ in 0..1 {
28+
| - a label with a similar name exists
29+
LL | break d;
30+
| ^
31+
| |
32+
| not found in this scope
33+
| help: use the similarly named label: `'d`
34+
35+
error: aborting due to 4 previous errors
3036

3137
For more information about this error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)