Skip to content

Commit 57d822a

Browse files
committed
Recover labels written as identifiers
1 parent b060988 commit 57d822a

File tree

4 files changed

+74
-18
lines changed

4 files changed

+74
-18
lines changed

compiler/rustc_parse/src/parser/expr.rs

+52-5
Original file line numberDiff line numberDiff line change
@@ -1346,9 +1346,6 @@ impl<'a> Parser<'a> {
13461346
err.span_label(sp, "while parsing this `loop` expression");
13471347
err
13481348
})
1349-
} else if self.eat_keyword(kw::Continue) {
1350-
let kind = ExprKind::Continue(self.eat_label());
1351-
Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
13521349
} else if self.eat_keyword(kw::Match) {
13531350
let match_sp = self.prev_token.span;
13541351
self.parse_match_expr().map_err(|mut err| {
@@ -1372,6 +1369,8 @@ impl<'a> Parser<'a> {
13721369
self.parse_try_block(lo)
13731370
} else if self.eat_keyword(kw::Return) {
13741371
self.parse_return_expr()
1372+
} else if self.eat_keyword(kw::Continue) {
1373+
self.parse_continue_expr(lo)
13751374
} else if self.eat_keyword(kw::Break) {
13761375
self.parse_break_expr()
13771376
} else if self.eat_keyword(kw::Yield) {
@@ -1724,8 +1723,8 @@ impl<'a> Parser<'a> {
17241723
} else if self.token != token::OpenDelim(Delimiter::Brace)
17251724
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
17261725
{
1727-
let expr = self.parse_expr_opt()?;
1728-
if let Some(expr) = &expr {
1726+
let mut expr = self.parse_expr_opt()?;
1727+
if let Some(expr) = &mut expr {
17291728
if label.is_some()
17301729
&& matches!(
17311730
expr.kind,
@@ -1743,7 +1742,19 @@ impl<'a> Parser<'a> {
17431742
BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span),
17441743
);
17451744
}
1745+
1746+
// Recover `break label aaaaa`
1747+
if self.may_recover()
1748+
&& let ExprKind::Path(None, p) = &expr.kind
1749+
&& let [segment] = &*p.segments
1750+
&& let &ast::PathSegment { ident, args: None, .. } = segment
1751+
&& let Some(next) = self.parse_expr_opt()?
1752+
{
1753+
label = Some(self.recover_ident_into_label(ident));
1754+
*expr = next;
1755+
}
17461756
}
1757+
17471758
expr
17481759
} else {
17491760
None
@@ -1752,6 +1763,23 @@ impl<'a> Parser<'a> {
17521763
self.maybe_recover_from_bad_qpath(expr)
17531764
}
17541765

1766+
/// Parse `"continue" label?`.
1767+
fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
1768+
let mut label = self.eat_label();
1769+
1770+
// Recover `continue label` -> `continue 'label`
1771+
if self.may_recover()
1772+
&& label.is_none()
1773+
&& let Some((ident, _)) = self.token.ident()
1774+
{
1775+
self.bump();
1776+
label = Some(self.recover_ident_into_label(ident));
1777+
}
1778+
1779+
let kind = ExprKind::Continue(label);
1780+
Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
1781+
}
1782+
17551783
/// Parse `"yield" expr?`.
17561784
fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> {
17571785
let lo = self.prev_token.span;
@@ -3037,6 +3065,25 @@ impl<'a> Parser<'a> {
30373065
false
30383066
}
30393067

3068+
/// Converts an ident into 'label and emits an "expected a label, found an identifier" error.
3069+
fn recover_ident_into_label(&mut self, ident: Ident) -> Label {
3070+
// Convert `label` -> `'label`,
3071+
// so that nameres doesn't complain about non-existing label
3072+
let label = format!("'{}", ident.name);
3073+
let ident = Ident { name: Symbol::intern(&label), span: ident.span };
3074+
3075+
self.struct_span_err(ident.span, "expected a label, found an identifier")
3076+
.span_suggestion(
3077+
ident.span,
3078+
"labels start with a tick",
3079+
label,
3080+
Applicability::MachineApplicable,
3081+
)
3082+
.emit();
3083+
3084+
Label { ident }
3085+
}
3086+
30403087
/// Parses `ident (COLON expr)?`.
30413088
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
30423089
let attrs = self.parse_outer_attributes()?;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// run-rustfix
2+
3+
fn main() {
4+
'label: loop { break 'label }; //~ error: cannot find value `label` in this scope
5+
'label: loop { break 'label 0 }; //~ error: expected a label, found an identifier
6+
'label: loop { continue 'label }; //~ error: expected a label, found an identifier
7+
}
+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
// run-rustfix
2+
13
fn main() {
2-
'label: loop { break label } //~ error: cannot find value `label` in this scope
3-
'label: loop { break label 0 } //~ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `0`
4-
'label: loop { continue label } //~ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `label`
4+
'label: loop { break label }; //~ error: cannot find value `label` in this scope
5+
'label: loop { break label 0 }; //~ error: expected a label, found an identifier
6+
'label: loop { continue label }; //~ error: expected a label, found an identifier
57
}

tests/ui/parser/recover-unticked-labels.stderr

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `0`
2-
--> $DIR/recover-unticked-labels.rs:3:32
1+
error: expected a label, found an identifier
2+
--> $DIR/recover-unticked-labels.rs:5:26
33
|
4-
LL | 'label: loop { break label 0 }
5-
| ^ expected one of 8 possible tokens
4+
LL | 'label: loop { break label 0 };
5+
| ^^^^^ help: labels start with a tick: `'label`
66

7-
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `label`
8-
--> $DIR/recover-unticked-labels.rs:4:29
7+
error: expected a label, found an identifier
8+
--> $DIR/recover-unticked-labels.rs:6:29
99
|
10-
LL | 'label: loop { continue label }
11-
| ^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator
10+
LL | 'label: loop { continue label };
11+
| ^^^^^ help: labels start with a tick: `'label`
1212

1313
error[E0425]: cannot find value `label` in this scope
14-
--> $DIR/recover-unticked-labels.rs:2:26
14+
--> $DIR/recover-unticked-labels.rs:4:26
1515
|
16-
LL | 'label: loop { break label }
16+
LL | 'label: loop { break label };
1717
| ------ ^^^^^
1818
| | |
1919
| | not found in this scope

0 commit comments

Comments
 (0)