Skip to content

Commit 554674b

Browse files
authored
Rollup merge of rust-lang#97759 - WaffleLapkin:recover_label_expr, r=compiler-errors
Suggest adding `{}` for `'label: non_block_expr` Adds suggestions like this: ```text help: consider enclosing expression in a block | 3 | 'l {0}; | + + ``` inspired by rust-lang#48594 (comment) r? ``@compiler-errors``
2 parents c9c6c2e + 4f85a73 commit 554674b

File tree

5 files changed

+188
-15
lines changed

5 files changed

+188
-15
lines changed

compiler/rustc_parse/src/parser/expr.rs

+62-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ use rustc_ast::tokenstream::Spacing;
1313
use rustc_ast::util::classify;
1414
use rustc_ast::util::literal::LitError;
1515
use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
16+
use rustc_ast::visit::Visitor;
17+
use rustc_ast::StmtKind;
1618
use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
1719
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
1820
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
1921
use rustc_ast_pretty::pprust;
22+
use rustc_data_structures::thin_vec::ThinVec;
2023
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
2124
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
2225
use rustc_session::lint::BuiltinLintDiagnostics;
@@ -1548,9 +1551,66 @@ impl<'a> Parser<'a> {
15481551
Ok(self.mk_expr_err(lo))
15491552
} else {
15501553
let msg = "expected `while`, `for`, `loop` or `{` after a label";
1551-
self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
1554+
1555+
let mut err = self.struct_span_err(self.token.span, msg);
1556+
err.span_label(self.token.span, msg);
1557+
15521558
// Continue as an expression in an effort to recover on `'label: non_block_expr`.
1553-
self.parse_expr()
1559+
let expr = self.parse_expr().map(|expr| {
1560+
let span = expr.span;
1561+
1562+
let found_labeled_breaks = {
1563+
struct FindLabeledBreaksVisitor(bool);
1564+
1565+
impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor {
1566+
fn visit_expr_post(&mut self, ex: &'ast Expr) {
1567+
if let ExprKind::Break(Some(_label), _) = ex.kind {
1568+
self.0 = true;
1569+
}
1570+
}
1571+
}
1572+
1573+
let mut vis = FindLabeledBreaksVisitor(false);
1574+
vis.visit_expr(&expr);
1575+
vis.0
1576+
};
1577+
1578+
// Suggestion involves adding a (as of time of writing this, unstable) labeled block.
1579+
//
1580+
// If there are no breaks that may use this label, suggest removing the label and
1581+
// recover to the unmodified expression.
1582+
if !found_labeled_breaks {
1583+
let msg = "consider removing the label";
1584+
err.span_suggestion_verbose(
1585+
lo.until(span),
1586+
msg,
1587+
"",
1588+
Applicability::MachineApplicable,
1589+
);
1590+
1591+
return expr;
1592+
}
1593+
1594+
let sugg_msg = "consider enclosing expression in a block";
1595+
let suggestions = vec![
1596+
(span.shrink_to_lo(), "{ ".to_owned()),
1597+
(span.shrink_to_hi(), " }".to_owned()),
1598+
];
1599+
1600+
err.multipart_suggestion_verbose(
1601+
sugg_msg,
1602+
suggestions,
1603+
Applicability::MachineApplicable,
1604+
);
1605+
1606+
// Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`.
1607+
let stmt = self.mk_stmt(span, StmtKind::Expr(expr));
1608+
let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span);
1609+
self.mk_expr(span, ExprKind::Block(blk, label), ThinVec::new())
1610+
});
1611+
1612+
err.emit();
1613+
expr
15541614
}?;
15551615

15561616
if !ate_colon && consume_colon {

src/test/ui/parser/labeled-no-colon-expr.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ error: expected `while`, `for`, `loop` or `{` after a label
4747
|
4848
LL | 'l4 0;
4949
| ^ expected `while`, `for`, `loop` or `{` after a label
50+
|
51+
help: consider removing the label
52+
|
53+
LL - 'l4 0;
54+
LL + 0;
55+
|
5056

5157
error: labeled expression must be followed by `:`
5258
--> $DIR/labeled-no-colon-expr.rs:8:9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// run-rustfix
2+
#![feature(label_break_value)]
3+
fn main() {
4+
let _ = 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
5+
6+
match () { () => {}, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
7+
'label: { match () { () => break 'label, } }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
8+
#[allow(unused_labels)]
9+
'label: { match () { () => 'lp: loop { break 'lp 0 }, } }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
10+
11+
let x = 1;
12+
let _i = 'label: { match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label
13+
0 => 42,
14+
1 if false => break 'label 17,
15+
1 => {
16+
if true {
17+
break 'label 13
18+
} else {
19+
break 'label 0;
20+
}
21+
}
22+
_ => 1,
23+
} };
24+
25+
let other = 3;
26+
let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
27+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
1+
// run-rustfix
2+
#![feature(label_break_value)]
13
fn main() {
2-
'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
4+
let _ = 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
35

4-
let _recovery_witness: () = 0; //~ ERROR mismatched types
6+
'label: match () { () => {}, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
7+
'label: match () { () => break 'label, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
8+
#[allow(unused_labels)]
9+
'label: match () { () => 'lp: loop { break 'lp 0 }, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
10+
11+
let x = 1;
12+
let _i = 'label: match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label
13+
0 => 42,
14+
1 if false => break 'label 17,
15+
1 => {
16+
if true {
17+
break 'label 13
18+
} else {
19+
break 'label 0;
20+
}
21+
}
22+
_ => 1,
23+
};
24+
25+
let other = 3;
26+
let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other }); //~ ERROR expected `while`, `for`, `loop` or `{` after a label
527
}

src/test/ui/parser/recover-labeled-non-block-expr.stderr

+69-11
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,75 @@
11
error: expected `while`, `for`, `loop` or `{` after a label
2-
--> $DIR/recover-labeled-non-block-expr.rs:2:13
2+
--> $DIR/recover-labeled-non-block-expr.rs:4:21
33
|
4-
LL | 'label: 1 + 1;
5-
| ^ expected `while`, `for`, `loop` or `{` after a label
4+
LL | let _ = 'label: 1 + 1;
5+
| ^ expected `while`, `for`, `loop` or `{` after a label
6+
|
7+
help: consider removing the label
8+
|
9+
LL - let _ = 'label: 1 + 1;
10+
LL + let _ = 1 + 1;
11+
|
12+
13+
error: expected `while`, `for`, `loop` or `{` after a label
14+
--> $DIR/recover-labeled-non-block-expr.rs:6:13
15+
|
16+
LL | 'label: match () { () => {}, };
17+
| ^^^^^ expected `while`, `for`, `loop` or `{` after a label
18+
|
19+
help: consider removing the label
20+
|
21+
LL - 'label: match () { () => {}, };
22+
LL + match () { () => {}, };
23+
|
624

7-
error[E0308]: mismatched types
8-
--> $DIR/recover-labeled-non-block-expr.rs:4:33
25+
error: expected `while`, `for`, `loop` or `{` after a label
26+
--> $DIR/recover-labeled-non-block-expr.rs:7:13
27+
|
28+
LL | 'label: match () { () => break 'label, };
29+
| ^^^^^ expected `while`, `for`, `loop` or `{` after a label
30+
|
31+
help: consider enclosing expression in a block
32+
|
33+
LL | 'label: { match () { () => break 'label, } };
34+
| + +
35+
36+
error: expected `while`, `for`, `loop` or `{` after a label
37+
--> $DIR/recover-labeled-non-block-expr.rs:9:13
38+
|
39+
LL | 'label: match () { () => 'lp: loop { break 'lp 0 }, };
40+
| ^^^^^ expected `while`, `for`, `loop` or `{` after a label
41+
|
42+
help: consider enclosing expression in a block
43+
|
44+
LL | 'label: { match () { () => 'lp: loop { break 'lp 0 }, } };
45+
| + +
46+
47+
error: expected `while`, `for`, `loop` or `{` after a label
48+
--> $DIR/recover-labeled-non-block-expr.rs:12:22
49+
|
50+
LL | let _i = 'label: match x {
51+
| ^^^^^ expected `while`, `for`, `loop` or `{` after a label
52+
|
53+
help: consider enclosing expression in a block
54+
|
55+
LL ~ let _i = 'label: { match x {
56+
LL | 0 => 42,
57+
LL | 1 if false => break 'label 17,
58+
LL | 1 => {
59+
LL | if true {
60+
LL | break 'label 13
61+
...
62+
63+
error: expected `while`, `for`, `loop` or `{` after a label
64+
--> $DIR/recover-labeled-non-block-expr.rs:26:24
65+
|
66+
LL | let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other });
67+
| ^ expected `while`, `for`, `loop` or `{` after a label
68+
|
69+
help: consider enclosing expression in a block
970
|
10-
LL | let _recovery_witness: () = 0;
11-
| -- ^ expected `()`, found integer
12-
| |
13-
| expected due to this
71+
LL | let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) };
72+
| + +
1473

15-
error: aborting due to 2 previous errors
74+
error: aborting due to 6 previous errors
1675

17-
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)