1
Fork 0

Recover labels written as identifiers

This commit is contained in:
Maybe Waffle 2023-01-12 19:40:22 +00:00
parent b0609889d7
commit 57d822a904
4 changed files with 74 additions and 18 deletions

View file

@ -1346,9 +1346,6 @@ impl<'a> Parser<'a> {
err.span_label(sp, "while parsing this `loop` expression"); err.span_label(sp, "while parsing this `loop` expression");
err 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) { } else if self.eat_keyword(kw::Match) {
let match_sp = self.prev_token.span; let match_sp = self.prev_token.span;
self.parse_match_expr().map_err(|mut err| { self.parse_match_expr().map_err(|mut err| {
@ -1372,6 +1369,8 @@ impl<'a> Parser<'a> {
self.parse_try_block(lo) self.parse_try_block(lo)
} else if self.eat_keyword(kw::Return) { } else if self.eat_keyword(kw::Return) {
self.parse_return_expr() self.parse_return_expr()
} else if self.eat_keyword(kw::Continue) {
self.parse_continue_expr(lo)
} else if self.eat_keyword(kw::Break) { } else if self.eat_keyword(kw::Break) {
self.parse_break_expr() self.parse_break_expr()
} else if self.eat_keyword(kw::Yield) { } else if self.eat_keyword(kw::Yield) {
@ -1724,8 +1723,8 @@ impl<'a> Parser<'a> {
} else if self.token != token::OpenDelim(Delimiter::Brace) } else if self.token != token::OpenDelim(Delimiter::Brace)
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
{ {
let expr = self.parse_expr_opt()?; let mut expr = self.parse_expr_opt()?;
if let Some(expr) = &expr { if let Some(expr) = &mut expr {
if label.is_some() if label.is_some()
&& matches!( && matches!(
expr.kind, expr.kind,
@ -1743,7 +1742,19 @@ impl<'a> Parser<'a> {
BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span), 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 expr
} else { } else {
None None
@ -1752,6 +1763,23 @@ impl<'a> Parser<'a> {
self.maybe_recover_from_bad_qpath(expr) self.maybe_recover_from_bad_qpath(expr)
} }
/// Parse `"continue" label?`.
fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
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?`. /// Parse `"yield" expr?`.
fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> { fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span; let lo = self.prev_token.span;
@ -3037,6 +3065,25 @@ impl<'a> Parser<'a> {
false 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)?`. /// Parses `ident (COLON expr)?`.
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> { fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
let attrs = self.parse_outer_attributes()?; let attrs = self.parse_outer_attributes()?;

View file

@ -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
}

View file

@ -1,5 +1,7 @@
// run-rustfix
fn main() { fn main() {
'label: loop { break label } //~ error: cannot find value `label` in this scope '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 { break label 0 }; //~ error: expected a label, found an identifier
'label: loop { continue label } //~ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `label` 'label: loop { continue label }; //~ error: expected a label, found an identifier
} }

View file

@ -1,19 +1,19 @@
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `0` error: expected a label, found an identifier
--> $DIR/recover-unticked-labels.rs:3:32 --> $DIR/recover-unticked-labels.rs:5:26
| |
LL | 'label: loop { break label 0 } LL | 'label: loop { break label 0 };
| ^ expected one of 8 possible tokens | ^^^^^ help: labels start with a tick: `'label`
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `label` error: expected a label, found an identifier
--> $DIR/recover-unticked-labels.rs:4:29 --> $DIR/recover-unticked-labels.rs:6:29
| |
LL | 'label: loop { continue label } LL | 'label: loop { continue label };
| ^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator | ^^^^^ help: labels start with a tick: `'label`
error[E0425]: cannot find value `label` in this scope 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 | | not found in this scope