@@ -13,10 +13,13 @@ use rustc_ast::tokenstream::Spacing;
13
13
use rustc_ast:: util:: classify;
14
14
use rustc_ast:: util:: literal:: LitError ;
15
15
use rustc_ast:: util:: parser:: { prec_let_scrutinee_needs_par, AssocOp , Fixity } ;
16
+ use rustc_ast:: visit:: Visitor ;
17
+ use rustc_ast:: StmtKind ;
16
18
use rustc_ast:: { self as ast, AttrStyle , AttrVec , CaptureBy , ExprField , Lit , UnOp , DUMMY_NODE_ID } ;
17
19
use rustc_ast:: { AnonConst , BinOp , BinOpKind , FnDecl , FnRetTy , MacCall , Param , Ty , TyKind } ;
18
20
use rustc_ast:: { Arm , Async , BlockCheckMode , Expr , ExprKind , Label , Movability , RangeLimits } ;
19
21
use rustc_ast_pretty:: pprust;
22
+ use rustc_data_structures:: thin_vec:: ThinVec ;
20
23
use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed , PResult } ;
21
24
use rustc_session:: lint:: builtin:: BREAK_WITH_LABEL_AND_LOOP ;
22
25
use rustc_session:: lint:: BuiltinLintDiagnostics ;
@@ -1548,9 +1551,66 @@ impl<'a> Parser<'a> {
1548
1551
Ok ( self . mk_expr_err ( lo) )
1549
1552
} else {
1550
1553
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
+
1552
1558
// 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
1554
1614
} ?;
1555
1615
1556
1616
if !ate_colon && consume_colon {
0 commit comments