@@ -2606,7 +2606,10 @@ impl<'a> Parser<'a> {
26062606 /// Parses an `if` expression (`if` token already eaten).
26072607 fn parse_expr_if ( & mut self ) -> PResult < ' a , P < Expr > > {
26082608 let lo = self . prev_token . span ;
2609- let cond = self . parse_expr_cond ( lo. edition ( ) ) ?;
2609+ // Scoping code checks the top level edition of the `if`; let's match it here.
2610+ // The `CondChecker` also checks the edition of the `let` itself, just to make sure.
2611+ let let_chains_policy = LetChainsPolicy :: EditionDependent { current_edition : lo. edition ( ) } ;
2612+ let cond = self . parse_expr_cond ( let_chains_policy) ?;
26102613 self . parse_if_after_cond ( lo, cond)
26112614 }
26122615
@@ -2716,41 +2719,16 @@ impl<'a> Parser<'a> {
27162719
27172720 /// Parses the condition of a `if` or `while` expression.
27182721 ///
2719- /// The specified `edition` should be that of the whole `if` or `while` construct: the same
2720- /// span that we later decide the drop behaviour on (editions ..=2021 vs 2024..)
2722+ /// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct,
2723+ /// i.e. the same span we use to later decide whether the drop behaviour should be that of
2724+ /// edition `..=2021` or that of `2024..`.
27212725 // 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.
2722- pub fn parse_expr_cond ( & mut self , edition : Edition ) -> PResult < ' a , P < Expr > > {
2726+ pub fn parse_expr_cond ( & mut self , let_chains_policy : LetChainsPolicy ) -> PResult < ' a , P < Expr > > {
27232727 let attrs = self . parse_outer_attributes ( ) ?;
27242728 let ( mut cond, _) =
27252729 self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL | Restrictions :: ALLOW_LET , attrs) ?;
27262730
2727- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
2728-
2729- if let ExprKind :: Let ( _, _, _, Recovered :: No ) = cond. kind {
2730- // Remove the last feature gating of a `let` expression since it's stable.
2731- self . psess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
2732- } else {
2733- fn ungate_let_exprs ( this : & mut Parser < ' _ > , expr : & Expr ) {
2734- if !expr. span . at_least_rust_2024 ( ) {
2735- return ;
2736- }
2737- match & expr. kind {
2738- ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
2739- ungate_let_exprs ( this, rhs) ;
2740- ungate_let_exprs ( this, lhs) ;
2741- }
2742- ExprKind :: Let ( ..) => {
2743- this. psess . gated_spans . ungate_last ( sym:: let_chains, expr. span )
2744- }
2745- _ => ( ) ,
2746- }
2747- }
2748- if edition. at_least_rust_2024 ( ) {
2749- // Scoping code checks the top level edition of the `if`: let's match it here.
2750- // Also check all editions in between, just to make sure.
2751- ungate_let_exprs ( self , & cond) ;
2752- }
2753- }
2731+ CondChecker :: new ( self , let_chains_policy) . visit_expr ( & mut cond) ;
27542732
27552733 Ok ( cond)
27562734 }
@@ -3045,7 +3023,8 @@ impl<'a> Parser<'a> {
30453023
30463024 /// Parses a `while` or `while let` expression (`while` token already eaten).
30473025 fn parse_expr_while ( & mut self , opt_label : Option < Label > , lo : Span ) -> PResult < ' a , P < Expr > > {
3048- let cond = self . parse_expr_cond ( lo. edition ( ) ) . map_err ( |mut err| {
3026+ let policy = LetChainsPolicy :: EditionDependent { current_edition : lo. edition ( ) } ;
3027+ let cond = self . parse_expr_cond ( policy) . map_err ( |mut err| {
30493028 err. span_label ( lo, "while parsing the condition of this `while` expression" ) ;
30503029 err
30513030 } ) ?;
@@ -3429,7 +3408,7 @@ impl<'a> Parser<'a> {
34293408 }
34303409
34313410 fn parse_match_arm_guard ( & mut self ) -> PResult < ' a , Option < P < Expr > > > {
3432- // Used to check the `let_chains` and ` if_let_guard` features mostly by scanning
3411+ // Used to check the `if_let_guard` feature mostly by scanning
34333412 // `&&` tokens.
34343413 fn has_let_expr ( expr : & Expr ) -> bool {
34353414 match & expr. kind {
@@ -3450,23 +3429,9 @@ impl<'a> Parser<'a> {
34503429 let if_span = self . prev_token . span ;
34513430 let mut cond = self . parse_match_guard_condition ( ) ?;
34523431
3453- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3432+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
34543433
34553434 if has_let_expr ( & cond) {
3456- // Let chains are allowed in match guards, but only there
3457- fn ungate_let_exprs ( this : & mut Parser < ' _ > , expr : & Expr ) {
3458- match & expr. kind {
3459- ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
3460- ungate_let_exprs ( this, rhs) ;
3461- ungate_let_exprs ( this, lhs) ;
3462- }
3463- ExprKind :: Let ( ..) => {
3464- this. psess . gated_spans . ungate_last ( sym:: let_chains, expr. span )
3465- }
3466- _ => ( ) ,
3467- }
3468- }
3469- ungate_let_exprs ( self , & cond) ;
34703435 let span = if_span. to ( cond. span ) ;
34713436 self . psess . gated_spans . gate ( sym:: if_let_guard, span) ;
34723437 }
@@ -3493,7 +3458,7 @@ impl<'a> Parser<'a> {
34933458 unreachable ! ( )
34943459 } ;
34953460 self . psess . gated_spans . ungate_last ( sym:: guard_patterns, cond. span ) ;
3496- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3461+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
34973462 let right = self . prev_token . span ;
34983463 self . dcx ( ) . emit_err ( errors:: ParenthesesInMatchPat {
34993464 span : vec ! [ left, right] ,
@@ -4072,7 +4037,14 @@ pub(crate) enum ForbiddenLetReason {
40724037 NotSupportedParentheses ( #[ primary_span] Span ) ,
40734038}
40744039
4075- /// Visitor to check for invalid/unstable use of `ExprKind::Let` that can't
4040+ /// Whether let chains are allowed on all editions, or it's edition dependent (allowed only on
4041+ /// 2024 and later). In case of edition dependence, specify the currently present edition.
4042+ pub enum LetChainsPolicy {
4043+ AlwaysAllowed ,
4044+ EditionDependent { current_edition : Edition } ,
4045+ }
4046+
4047+ /// Visitor to check for invalid use of `ExprKind::Let` that can't
40764048/// easily be caught in parsing. For example:
40774049///
40784050/// ```rust,ignore (example)
@@ -4083,19 +4055,29 @@ pub(crate) enum ForbiddenLetReason {
40834055/// ```
40844056struct CondChecker < ' a > {
40854057 parser : & ' a Parser < ' a > ,
4058+ let_chains_policy : LetChainsPolicy ,
4059+ depth : u32 ,
40864060 forbid_let_reason : Option < ForbiddenLetReason > ,
40874061 missing_let : Option < errors:: MaybeMissingLet > ,
40884062 comparison : Option < errors:: MaybeComparison > ,
40894063}
40904064
40914065impl < ' a > CondChecker < ' a > {
4092- fn new ( parser : & ' a Parser < ' a > ) -> Self {
4093- CondChecker { parser, forbid_let_reason : None , missing_let : None , comparison : None }
4066+ fn new ( parser : & ' a Parser < ' a > , let_chains_policy : LetChainsPolicy ) -> Self {
4067+ CondChecker {
4068+ parser,
4069+ forbid_let_reason : None ,
4070+ missing_let : None ,
4071+ comparison : None ,
4072+ let_chains_policy,
4073+ depth : 0 ,
4074+ }
40944075 }
40954076}
40964077
40974078impl MutVisitor for CondChecker < ' _ > {
40984079 fn visit_expr ( & mut self , e : & mut P < Expr > ) {
4080+ self . depth += 1 ;
40994081 use ForbiddenLetReason :: * ;
41004082
41014083 let span = e. span ;
@@ -4110,8 +4092,16 @@ impl MutVisitor for CondChecker<'_> {
41104092 comparison : self . comparison ,
41114093 } ,
41124094 ) ) ;
4113- } else {
4114- self . parser . psess . gated_spans . gate ( sym:: let_chains, span) ;
4095+ } else if self . depth > 1 {
4096+ // Top level `let` is always allowed; only gate chains
4097+ match self . let_chains_policy {
4098+ LetChainsPolicy :: AlwaysAllowed => ( ) ,
4099+ LetChainsPolicy :: EditionDependent { current_edition } => {
4100+ if !current_edition. at_least_rust_2024 ( ) || !span. at_least_rust_2024 ( ) {
4101+ self . parser . psess . gated_spans . gate ( sym:: let_chains, span) ;
4102+ }
4103+ }
4104+ }
41154105 }
41164106 }
41174107 ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , _, _) => {
@@ -4213,5 +4203,6 @@ impl MutVisitor for CondChecker<'_> {
42134203 // These would forbid any let expressions they contain already.
42144204 }
42154205 }
4206+ self . depth -= 1 ;
42164207 }
42174208}
0 commit comments