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`.
This commit is contained in:
commit
df88f7e02c
4 changed files with 93 additions and 7 deletions
|
@ -1353,9 +1353,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| {
|
||||||
|
@ -1379,6 +1376,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) {
|
||||||
|
@ -1715,10 +1714,10 @@ impl<'a> Parser<'a> {
|
||||||
fn parse_break_expr(&mut self) -> PResult<'a, P<Expr>> {
|
fn parse_break_expr(&mut self) -> PResult<'a, P<Expr>> {
|
||||||
let lo = self.prev_token.span;
|
let lo = self.prev_token.span;
|
||||||
let mut label = self.eat_label();
|
let mut label = self.eat_label();
|
||||||
let kind = if label.is_some() && self.token == token::Colon {
|
let kind = if self.token == token::Colon && let Some(label) = label.take() {
|
||||||
// The value expression can be a labeled loop, see issue #86948, e.g.:
|
// The value expression can be a labeled loop, see issue #86948, e.g.:
|
||||||
// `loop { break 'label: loop { break 'label 42; }; }`
|
// `loop { break 'label: loop { break 'label 42; }; }`
|
||||||
let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?;
|
let lexpr = self.parse_labeled_expr(label, true)?;
|
||||||
self.sess.emit_err(LabeledLoopInBreak {
|
self.sess.emit_err(LabeledLoopInBreak {
|
||||||
span: lexpr.span,
|
span: lexpr.span,
|
||||||
sub: WrapExpressionInParentheses {
|
sub: WrapExpressionInParentheses {
|
||||||
|
@ -1730,8 +1729,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,
|
||||||
|
@ -1749,7 +1748,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
|
||||||
|
@ -1758,6 +1769,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;
|
||||||
|
@ -3046,6 +3074,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()?;
|
||||||
|
|
7
tests/ui/parser/recover-unticked-labels.fixed
Normal file
7
tests/ui/parser/recover-unticked-labels.fixed
Normal 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
|
||||||
|
}
|
7
tests/ui/parser/recover-unticked-labels.rs
Normal file
7
tests/ui/parser/recover-unticked-labels.rs
Normal 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
|
||||||
|
}
|
25
tests/ui/parser/recover-unticked-labels.stderr
Normal file
25
tests/ui/parser/recover-unticked-labels.stderr
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
error: expected a label, found an identifier
|
||||||
|
--> $DIR/recover-unticked-labels.rs:5:26
|
||||||
|
|
|
||||||
|
LL | 'label: loop { break label 0 };
|
||||||
|
| ^^^^^ help: labels start with a tick: `'label`
|
||||||
|
|
||||||
|
error: expected a label, found an identifier
|
||||||
|
--> $DIR/recover-unticked-labels.rs:6:29
|
||||||
|
|
|
||||||
|
LL | 'label: loop { continue label };
|
||||||
|
| ^^^^^ help: labels start with a tick: `'label`
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `label` in this scope
|
||||||
|
--> $DIR/recover-unticked-labels.rs:4:26
|
||||||
|
|
|
||||||
|
LL | 'label: loop { break label };
|
||||||
|
| ------ ^^^^^
|
||||||
|
| | |
|
||||||
|
| | not found in this scope
|
||||||
|
| | help: use the similarly named label: `'label`
|
||||||
|
| a label with a similar name exists
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0425`.
|
Loading…
Add table
Add a link
Reference in a new issue