@@ -15,6 +15,8 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty
1515use rustc_ast:: { Arm , Async , BlockCheckMode , Expr , ExprKind , Label , Movability , RangeLimits } ;
1616use rustc_ast_pretty:: pprust;
1717use rustc_errors:: { Applicability , DiagnosticBuilder , PResult } ;
18+ use rustc_session:: lint:: builtin:: BREAK_WITH_LABEL_AND_LOOP ;
19+ use rustc_session:: lint:: BuiltinLintDiagnostics ;
1820use rustc_span:: edition:: LATEST_STABLE_EDITION ;
1921use rustc_span:: source_map:: { self , Span , Spanned } ;
2022use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
@@ -1375,14 +1377,59 @@ impl<'a> Parser<'a> {
13751377 self . maybe_recover_from_bad_qpath ( expr, true )
13761378 }
13771379
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.
13791388 fn parse_break_expr ( & mut self , attrs : AttrVec ) -> PResult < ' a , P < Expr > > {
13801389 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 )
13831410 || !self . restrictions . contains ( Restrictions :: NO_STRUCT_LITERAL )
13841411 {
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
13861433 } else {
13871434 None
13881435 } ;
0 commit comments