diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 83 |
1 files changed, 54 insertions, 29 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 11e60175a1f..f3ed798eba4 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -26,6 +26,7 @@ use rustc_macros::Subdiagnostic; use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; +use rustc_span::edition::Edition; use rustc_span::source_map::{self, Spanned}; use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -2602,7 +2603,10 @@ impl<'a> Parser<'a> { /// Parses an `if` expression (`if` token already eaten). fn parse_expr_if(&mut self) -> PResult<'a, P<Expr>> { let lo = self.prev_token.span; - let cond = self.parse_expr_cond()?; + // Scoping code checks the top level edition of the `if`; let's match it here. + // The `CondChecker` also checks the edition of the `let` itself, just to make sure. + let let_chains_policy = LetChainsPolicy::EditionDependent { current_edition: lo.edition() }; + let cond = self.parse_expr_cond(let_chains_policy)?; self.parse_if_after_cond(lo, cond) } @@ -2711,18 +2715,17 @@ impl<'a> Parser<'a> { } /// Parses the condition of a `if` or `while` expression. + /// + /// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct, + /// i.e. the same span we use to later decide whether the drop behaviour should be that of + /// edition `..=2021` or that of `2024..`. // Public because it is used in rustfmt forks such as https://github.com/tucant/rustfmt/blob/30c83df9e1db10007bdd16dafce8a86b404329b2/src/parse/macros/html.rs#L57 for custom if expressions. - pub fn parse_expr_cond(&mut self) -> PResult<'a, P<Expr>> { + pub fn parse_expr_cond(&mut self, let_chains_policy: LetChainsPolicy) -> PResult<'a, P<Expr>> { let attrs = self.parse_outer_attributes()?; let (mut cond, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?; - CondChecker::new(self).visit_expr(&mut cond); - - if let ExprKind::Let(_, _, _, Recovered::No) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); - } + CondChecker::new(self, let_chains_policy).visit_expr(&mut cond); Ok(cond) } @@ -3017,7 +3020,8 @@ impl<'a> Parser<'a> { /// Parses a `while` or `while let` expression (`while` token already eaten). fn parse_expr_while(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { - let cond = self.parse_expr_cond().map_err(|mut err| { + let policy = LetChainsPolicy::EditionDependent { current_edition: lo.edition() }; + let cond = self.parse_expr_cond(policy).map_err(|mut err| { err.span_label(lo, "while parsing the condition of this `while` expression"); err })?; @@ -3401,17 +3405,17 @@ impl<'a> Parser<'a> { } fn parse_match_arm_guard(&mut self) -> PResult<'a, Option<P<Expr>>> { - // Used to check the `let_chains` and `if_let_guard` features mostly by scanning + // Used to check the `if_let_guard` feature mostly by scanning // `&&` tokens. - fn check_let_expr(expr: &Expr) -> (bool, bool) { + fn has_let_expr(expr: &Expr) -> bool { match &expr.kind { ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => { - let lhs_rslt = check_let_expr(lhs); - let rhs_rslt = check_let_expr(rhs); - (lhs_rslt.0 || rhs_rslt.0, false) + let lhs_rslt = has_let_expr(lhs); + let rhs_rslt = has_let_expr(rhs); + lhs_rslt || rhs_rslt } - ExprKind::Let(..) => (true, true), - _ => (false, true), + ExprKind::Let(..) => true, + _ => false, } } if !self.eat_keyword(exp!(If)) { @@ -3422,14 +3426,9 @@ impl<'a> Parser<'a> { let if_span = self.prev_token.span; let mut cond = self.parse_match_guard_condition()?; - CondChecker::new(self).visit_expr(&mut cond); + CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); - let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); - if has_let_expr { - if does_not_have_bin_op { - // Remove the last feature gating of a `let` expression since it's stable. - self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); - } + if has_let_expr(&cond) { let span = if_span.to(cond.span); self.psess.gated_spans.gate(sym::if_let_guard, span); } @@ -3456,7 +3455,7 @@ impl<'a> Parser<'a> { unreachable!() }; self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); - CondChecker::new(self).visit_expr(&mut cond); + CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); let right = self.prev_token.span; self.dcx().emit_err(errors::ParenthesesInMatchPat { span: vec![left, right], @@ -4027,7 +4026,14 @@ pub(crate) enum ForbiddenLetReason { NotSupportedParentheses(#[primary_span] Span), } -/// Visitor to check for invalid/unstable use of `ExprKind::Let` that can't +/// Whether let chains are allowed on all editions, or it's edition dependent (allowed only on +/// 2024 and later). In case of edition dependence, specify the currently present edition. +pub enum LetChainsPolicy { + AlwaysAllowed, + EditionDependent { current_edition: Edition }, +} + +/// Visitor to check for invalid use of `ExprKind::Let` that can't /// easily be caught in parsing. For example: /// /// ```rust,ignore (example) @@ -4038,19 +4044,29 @@ pub(crate) enum ForbiddenLetReason { /// ``` struct CondChecker<'a> { parser: &'a Parser<'a>, + let_chains_policy: LetChainsPolicy, + depth: u32, forbid_let_reason: Option<ForbiddenLetReason>, missing_let: Option<errors::MaybeMissingLet>, comparison: Option<errors::MaybeComparison>, } impl<'a> CondChecker<'a> { - fn new(parser: &'a Parser<'a>) -> Self { - CondChecker { parser, forbid_let_reason: None, missing_let: None, comparison: None } + fn new(parser: &'a Parser<'a>, let_chains_policy: LetChainsPolicy) -> Self { + CondChecker { + parser, + forbid_let_reason: None, + missing_let: None, + comparison: None, + let_chains_policy, + depth: 0, + } } } impl MutVisitor for CondChecker<'_> { fn visit_expr(&mut self, e: &mut P<Expr>) { + self.depth += 1; use ForbiddenLetReason::*; let span = e.span; @@ -4065,8 +4081,16 @@ impl MutVisitor for CondChecker<'_> { comparison: self.comparison, }, )); - } else { - self.parser.psess.gated_spans.gate(sym::let_chains, span); + } else if self.depth > 1 { + // Top level `let` is always allowed; only gate chains + match self.let_chains_policy { + LetChainsPolicy::AlwaysAllowed => (), + LetChainsPolicy::EditionDependent { current_edition } => { + if !current_edition.at_least_rust_2024() || !span.at_least_rust_2024() { + self.parser.psess.gated_spans.gate(sym::let_chains, span); + } + } + } } } ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, _, _) => { @@ -4168,5 +4192,6 @@ impl MutVisitor for CondChecker<'_> { // These would forbid any let expressions they contain already. } } + self.depth -= 1; } } |
