@@ -15,6 +15,8 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty
15
15
use rustc_ast:: { Arm , Async , BlockCheckMode , Expr , ExprKind , Label , Movability , RangeLimits } ;
16
16
use rustc_ast_pretty:: pprust;
17
17
use rustc_errors:: { Applicability , DiagnosticBuilder , PResult } ;
18
+ use rustc_session:: lint:: builtin:: BREAK_WITH_LABEL_AND_LOOP ;
19
+ use rustc_session:: lint:: BuiltinLintDiagnostics ;
18
20
use rustc_span:: edition:: LATEST_STABLE_EDITION ;
19
21
use rustc_span:: source_map:: { self , Span , Spanned } ;
20
22
use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
@@ -1375,14 +1377,59 @@ impl<'a> Parser<'a> {
1375
1377
self . maybe_recover_from_bad_qpath ( expr, true )
1376
1378
}
1377
1379
1378
- /// Parse `"('label ":")? break expr?`.
1380
+ /// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.
1381
+ /// If the label is followed immediately by a `:` token, the label and `:` are
1382
+ /// parsed as part of the expression (i.e. a labeled loop). The language team has
1383
+ /// decided in #87026 to require parentheses as a visual aid to avoid confusion if
1384
+ /// the break expression of an unlabeled break is a labeled loop (as in
1385
+ /// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value
1386
+ /// expression only gets a warning for compatibility reasons; and a labeled break
1387
+ /// with a labeled loop does not even get a warning because there is no ambiguity.
1379
1388
fn parse_break_expr ( & mut self , attrs : AttrVec ) -> PResult < ' a , P < Expr > > {
1380
1389
let lo = self . prev_token . span ;
1381
- let label = self . eat_label ( ) ;
1382
- let kind = if self . token != token:: OpenDelim ( token:: Brace )
1390
+ let mut label = self . eat_label ( ) ;
1391
+ let kind = if label. is_some ( ) && self . token == token:: Colon {
1392
+ // The value expression can be a labeled loop, see issue #86948, e.g.:
1393
+ // `loop { break 'label: loop { break 'label 42; }; }`
1394
+ let lexpr = self . parse_labeled_expr ( label. take ( ) . unwrap ( ) , AttrVec :: new ( ) , true ) ?;
1395
+ self . struct_span_err (
1396
+ lexpr. span ,
1397
+ "parentheses are required around this expression to avoid confusion with a labeled break expression" ,
1398
+ )
1399
+ . multipart_suggestion (
1400
+ "wrap the expression in parentheses" ,
1401
+ vec ! [
1402
+ ( lexpr. span. shrink_to_lo( ) , "(" . to_string( ) ) ,
1403
+ ( lexpr. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
1404
+ ] ,
1405
+ Applicability :: MachineApplicable ,
1406
+ )
1407
+ . emit ( ) ;
1408
+ Some ( lexpr)
1409
+ } else if self . token != token:: OpenDelim ( token:: Brace )
1383
1410
|| !self . restrictions . contains ( Restrictions :: NO_STRUCT_LITERAL )
1384
1411
{
1385
- self . parse_expr_opt ( ) ?
1412
+ let expr = self . parse_expr_opt ( ) ?;
1413
+ if let Some ( ref expr) = expr {
1414
+ if label. is_some ( )
1415
+ && matches ! (
1416
+ expr. kind,
1417
+ ExprKind :: While ( _, _, None )
1418
+ | ExprKind :: ForLoop ( _, _, _, None )
1419
+ | ExprKind :: Loop ( _, None )
1420
+ | ExprKind :: Block ( _, None )
1421
+ )
1422
+ {
1423
+ self . sess . buffer_lint_with_diagnostic (
1424
+ BREAK_WITH_LABEL_AND_LOOP ,
1425
+ lo. to ( expr. span ) ,
1426
+ ast:: CRATE_NODE_ID ,
1427
+ "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression" ,
1428
+ BuiltinLintDiagnostics :: BreakWithLabelAndLoop ( expr. span ) ,
1429
+ ) ;
1430
+ }
1431
+ }
1432
+ expr
1386
1433
} else {
1387
1434
None
1388
1435
} ;
0 commit comments