Skip to content

Commit df88f7e

Browse files
authored
Rollup merge of #106783 - WaffleLapkin:break-my-ident, r=wesleywiser
Recover labels written as identifiers This adds recovery for `break label expr` and `continue label`, as well as a test for `break label`.
2 parents 51d50ea + 57d822a commit df88f7e

File tree

4 files changed

+93
-7
lines changed

4 files changed

+93
-7
lines changed

compiler/rustc_parse/src/parser/expr.rs

+54-7
Original file line numberDiff line numberDiff line change
@@ -1353,9 +1353,6 @@ impl<'a> Parser<'a> {
13531353
err.span_label(sp, "while parsing this `loop` expression");
13541354
err
13551355
})
1356-
} else if self.eat_keyword(kw::Continue) {
1357-
let kind = ExprKind::Continue(self.eat_label());
1358-
Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
13591356
} else if self.eat_keyword(kw::Match) {
13601357
let match_sp = self.prev_token.span;
13611358
self.parse_match_expr().map_err(|mut err| {
@@ -1379,6 +1376,8 @@ impl<'a> Parser<'a> {
13791376
self.parse_try_block(lo)
13801377
} else if self.eat_keyword(kw::Return) {
13811378
self.parse_return_expr()
1379+
} else if self.eat_keyword(kw::Continue) {
1380+
self.parse_continue_expr(lo)
13821381
} else if self.eat_keyword(kw::Break) {
13831382
self.parse_break_expr()
13841383
} else if self.eat_keyword(kw::Yield) {
@@ -1715,10 +1714,10 @@ impl<'a> Parser<'a> {
17151714
fn parse_break_expr(&mut self) -> PResult<'a, P<Expr>> {
17161715
let lo = self.prev_token.span;
17171716
let mut label = self.eat_label();
1718-
let kind = if label.is_some() && self.token == token::Colon {
1717+
let kind = if self.token == token::Colon && let Some(label) = label.take() {
17191718
// The value expression can be a labeled loop, see issue #86948, e.g.:
17201719
// `loop { break 'label: loop { break 'label 42; }; }`
1721-
let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?;
1720+
let lexpr = self.parse_labeled_expr(label, true)?;
17221721
self.sess.emit_err(LabeledLoopInBreak {
17231722
span: lexpr.span,
17241723
sub: WrapExpressionInParentheses {
@@ -1730,8 +1729,8 @@ impl<'a> Parser<'a> {
17301729
} else if self.token != token::OpenDelim(Delimiter::Brace)
17311730
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
17321731
{
1733-
let expr = self.parse_expr_opt()?;
1734-
if let Some(expr) = &expr {
1732+
let mut expr = self.parse_expr_opt()?;
1733+
if let Some(expr) = &mut expr {
17351734
if label.is_some()
17361735
&& matches!(
17371736
expr.kind,
@@ -1749,7 +1748,19 @@ impl<'a> Parser<'a> {
17491748
BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span),
17501749
);
17511750
}
1751+
1752+
// Recover `break label aaaaa`
1753+
if self.may_recover()
1754+
&& let ExprKind::Path(None, p) = &expr.kind
1755+
&& let [segment] = &*p.segments
1756+
&& let &ast::PathSegment { ident, args: None, .. } = segment
1757+
&& let Some(next) = self.parse_expr_opt()?
1758+
{
1759+
label = Some(self.recover_ident_into_label(ident));
1760+
*expr = next;
1761+
}
17521762
}
1763+
17531764
expr
17541765
} else {
17551766
None
@@ -1758,6 +1769,23 @@ impl<'a> Parser<'a> {
17581769
self.maybe_recover_from_bad_qpath(expr)
17591770
}
17601771

1772+
/// Parse `"continue" label?`.
1773+
fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
1774+
let mut label = self.eat_label();
1775+
1776+
// Recover `continue label` -> `continue 'label`
1777+
if self.may_recover()
1778+
&& label.is_none()
1779+
&& let Some((ident, _)) = self.token.ident()
1780+
{
1781+
self.bump();
1782+
label = Some(self.recover_ident_into_label(ident));
1783+
}
1784+
1785+
let kind = ExprKind::Continue(label);
1786+
Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
1787+
}
1788+
17611789
/// Parse `"yield" expr?`.
17621790
fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> {
17631791
let lo = self.prev_token.span;
@@ -3046,6 +3074,25 @@ impl<'a> Parser<'a> {
30463074
false
30473075
}
30483076

3077+
/// Converts an ident into 'label and emits an "expected a label, found an identifier" error.
3078+
fn recover_ident_into_label(&mut self, ident: Ident) -> Label {
3079+
// Convert `label` -> `'label`,
3080+
// so that nameres doesn't complain about non-existing label
3081+
let label = format!("'{}", ident.name);
3082+
let ident = Ident { name: Symbol::intern(&label), span: ident.span };
3083+
3084+
self.struct_span_err(ident.span, "expected a label, found an identifier")
3085+
.span_suggestion(
3086+
ident.span,
3087+
"labels start with a tick",
3088+
label,
3089+
Applicability::MachineApplicable,
3090+
)
3091+
.emit();
3092+
3093+
Label { ident }
3094+
}
3095+
30493096
/// Parses `ident (COLON expr)?`.
30503097
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
30513098
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+
}
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: expected a label, found an identifier
2+
--> $DIR/recover-unticked-labels.rs:5:26
3+
|
4+
LL | 'label: loop { break label 0 };
5+
| ^^^^^ help: labels start with a tick: `'label`
6+
7+
error: expected a label, found an identifier
8+
--> $DIR/recover-unticked-labels.rs:6:29
9+
|
10+
LL | 'label: loop { continue label };
11+
| ^^^^^ help: labels start with a tick: `'label`
12+
13+
error[E0425]: cannot find value `label` in this scope
14+
--> $DIR/recover-unticked-labels.rs:4:26
15+
|
16+
LL | 'label: loop { break label };
17+
| ------ ^^^^^
18+
| | |
19+
| | not found in this scope
20+
| | help: use the similarly named label: `'label`
21+
| a label with a similar name exists
22+
23+
error: aborting due to 3 previous errors
24+
25+
For more information about this error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)