@@ -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 :: 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,15 @@ 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
2722+ /// The specified `edition` in LetChainsPolicy should be that of the whole `if` construct: the same
27202723 /// span that we later decide the drop behaviour on (editions ..=2021 vs 2024..)
27212724 // 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 > > {
2725+ pub fn parse_expr_cond ( & mut self , let_chains_policy : LetChainsPolicy ) -> PResult < ' a , P < Expr > > {
27232726 let attrs = self . parse_outer_attributes ( ) ?;
27242727 let ( mut cond, _) =
27252728 self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL | Restrictions :: ALLOW_LET , attrs) ?;
27262729
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- }
2730+ CondChecker :: new ( self , let_chains_policy) . visit_expr ( & mut cond) ;
27542731
27552732 Ok ( cond)
27562733 }
@@ -3045,7 +3022,7 @@ impl<'a> Parser<'a> {
30453022
30463023 /// Parses a `while` or `while let` expression (`while` token already eaten).
30473024 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| {
3025+ let cond = self . parse_expr_cond ( LetChainsPolicy :: AlwaysAllowed ) . map_err ( |mut err| {
30493026 err. span_label ( lo, "while parsing the condition of this `while` expression" ) ;
30503027 err
30513028 } ) ?;
@@ -3429,7 +3406,7 @@ impl<'a> Parser<'a> {
34293406 }
34303407
34313408 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
3409+ // Used to check the `if_let_guard` feature mostly by scanning
34333410 // `&&` tokens.
34343411 fn has_let_expr ( expr : & Expr ) -> bool {
34353412 match & expr. kind {
@@ -3450,23 +3427,9 @@ impl<'a> Parser<'a> {
34503427 let if_span = self . prev_token . span ;
34513428 let mut cond = self . parse_match_guard_condition ( ) ?;
34523429
3453- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3430+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
34543431
34553432 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) ;
34703433 let span = if_span. to ( cond. span ) ;
34713434 self . psess . gated_spans . gate ( sym:: if_let_guard, span) ;
34723435 }
@@ -3493,7 +3456,7 @@ impl<'a> Parser<'a> {
34933456 unreachable ! ( )
34943457 } ;
34953458 self . psess . gated_spans . ungate_last ( sym:: guard_patterns, cond. span ) ;
3496- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3459+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
34973460 let right = self . prev_token . span ;
34983461 self . dcx ( ) . emit_err ( errors:: ParenthesesInMatchPat {
34993462 span : vec ! [ left, right] ,
@@ -4072,7 +4035,14 @@ pub(crate) enum ForbiddenLetReason {
40724035 NotSupportedParentheses ( #[ primary_span] Span ) ,
40734036}
40744037
4075- /// Visitor to check for invalid/unstable use of `ExprKind::Let` that can't
4038+ /// Whether let chains are allowed on all editions, or it's edition dependent.
4039+ /// In case of edition dependence, specify the currently present edition.
4040+ pub enum LetChainsPolicy {
4041+ AlwaysAllowed ,
4042+ Edition ( Edition ) ,
4043+ }
4044+
4045+ /// Visitor to check for invalid use of `ExprKind::Let` that can't
40764046/// easily be caught in parsing. For example:
40774047///
40784048/// ```rust,ignore (example)
@@ -4083,19 +4053,29 @@ pub(crate) enum ForbiddenLetReason {
40834053/// ```
40844054struct CondChecker < ' a > {
40854055 parser : & ' a Parser < ' a > ,
4056+ let_chains_policy : LetChainsPolicy ,
4057+ depth : u32 ,
40864058 forbid_let_reason : Option < ForbiddenLetReason > ,
40874059 missing_let : Option < errors:: MaybeMissingLet > ,
40884060 comparison : Option < errors:: MaybeComparison > ,
40894061}
40904062
40914063impl < ' a > CondChecker < ' a > {
4092- fn new ( parser : & ' a Parser < ' a > ) -> Self {
4093- CondChecker { parser, forbid_let_reason : None , missing_let : None , comparison : None }
4064+ fn new ( parser : & ' a Parser < ' a > , let_chains_policy : LetChainsPolicy ) -> Self {
4065+ CondChecker {
4066+ parser,
4067+ forbid_let_reason : None ,
4068+ missing_let : None ,
4069+ comparison : None ,
4070+ let_chains_policy,
4071+ depth : 0 ,
4072+ }
40944073 }
40954074}
40964075
40974076impl MutVisitor for CondChecker < ' _ > {
40984077 fn visit_expr ( & mut self , e : & mut P < Expr > ) {
4078+ self . depth += 1 ;
40994079 use ForbiddenLetReason :: * ;
41004080
41014081 let span = e. span ;
@@ -4110,8 +4090,16 @@ impl MutVisitor for CondChecker<'_> {
41104090 comparison : self . comparison ,
41114091 } ,
41124092 ) ) ;
4113- } else {
4114- self . parser . psess . gated_spans . gate ( sym:: let_chains, span) ;
4093+ } else if self . depth > 1 {
4094+ // Top level let is always allowed, only gate chains
4095+ match self . let_chains_policy {
4096+ LetChainsPolicy :: AlwaysAllowed => ( ) ,
4097+ LetChainsPolicy :: Edition ( edition) => {
4098+ if !edition. at_least_rust_2024 ( ) || !span. at_least_rust_2024 ( ) {
4099+ self . parser . psess . gated_spans . gate ( sym:: let_chains, span) ;
4100+ }
4101+ }
4102+ }
41154103 }
41164104 }
41174105 ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , _, _) => {
@@ -4213,5 +4201,6 @@ impl MutVisitor for CondChecker<'_> {
42134201 // These would forbid any let expressions they contain already.
42144202 }
42154203 }
4204+ self . depth -= 1 ;
42164205 }
42174206}
0 commit comments