Require parentheses to avoid confusions around labeled break and loop expressions
This commit is contained in:
parent
7069a8c2b7
commit
470cbc0e2e
6 changed files with 152 additions and 21 deletions
|
@ -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
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue