diff options
| author | Fabian Wolff <fabian.wolff@alumni.ethz.ch> | 2021-07-10 16:38:55 +0200 |
|---|---|---|
| committer | Fabian Wolff <fabian.wolff@alumni.ethz.ch> | 2021-07-31 19:10:58 +0200 |
| commit | 470cbc0e2eb52c2b80ba6940b6446aecb20f9b04 (patch) | |
| tree | a1323b17d7938540e6161109ae4b0af576e4d385 /compiler/rustc_parse/src/parser/expr.rs | |
| parent | 7069a8c2b78c5d23205de1cabb4c2a65229dbd8f (diff) | |
| download | rust-470cbc0e2eb52c2b80ba6940b6446aecb20f9b04.tar.gz rust-470cbc0e2eb52c2b80ba6940b6446aecb20f9b04.zip | |
Require parentheses to avoid confusions around labeled break and loop expressions
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 55 |
1 files changed, 51 insertions, 4 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b774c76103f..824f2316e58 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,6 +15,8 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; +use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; +use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -1375,14 +1377,59 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr, true) } - /// Parse `"('label ":")? break expr?`. + /// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten. + /// If the label is followed immediately by a `:` token, the label and `:` are + /// parsed as part of the expression (i.e. a labeled loop). The language team has + /// decided in #87026 to require parentheses as a visual aid to avoid confusion if + /// the break expression of an unlabeled break is a labeled loop (as in + /// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value + /// expression only gets a warning for compatibility reasons; and a labeled break + /// with a labeled loop does not even get a warning because there is no ambiguity. fn parse_break_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { let lo = self.prev_token.span; - let label = self.eat_label(); - let kind = if self.token != token::OpenDelim(token::Brace) + let mut label = self.eat_label(); + let kind = if label.is_some() && self.token == token::Colon { + // The value expression can be a labeled loop, see issue #86948, e.g.: + // `loop { break 'label: loop { break 'label 42; }; }` + let lexpr = self.parse_labeled_expr(label.take().unwrap(), AttrVec::new(), true)?; + self.struct_span_err( + lexpr.span, + "parentheses are required around this expression to avoid confusion with a labeled break expression", + ) + .multipart_suggestion( + "wrap the expression in parentheses", + vec![ + (lexpr.span.shrink_to_lo(), "(".to_string()), + (lexpr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + Some(lexpr) + } else if self.token != token::OpenDelim(token::Brace) || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) { - self.parse_expr_opt()? + let expr = self.parse_expr_opt()?; + if let Some(ref expr) = expr { + if label.is_some() + && matches!( + expr.kind, + ExprKind::While(_, _, None) + | ExprKind::ForLoop(_, _, _, None) + | ExprKind::Loop(_, None) + | ExprKind::Block(_, None) + ) + { + self.sess.buffer_lint_with_diagnostic( + BREAK_WITH_LABEL_AND_LOOP, + lo.to(expr.span), + ast::CRATE_NODE_ID, + "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression", + BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span), + ); + } + } + expr } else { None }; |
