@@ -2606,7 +2606,10 @@ impl<'a> Parser<'a> {
2606
2606
/// Parses an `if` expression (`if` token already eaten).
2607
2607
fn parse_expr_if ( & mut self ) -> PResult < ' a , P < Expr > > {
2608
2608
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) ?;
2610
2613
self . parse_if_after_cond ( lo, cond)
2611
2614
}
2612
2615
@@ -2716,41 +2719,16 @@ impl<'a> Parser<'a> {
2716
2719
2717
2720
/// Parses the condition of a `if` or `while` expression.
2718
2721
///
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..`.
2721
2725
// 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 > > {
2723
2727
let attrs = self . parse_outer_attributes ( ) ?;
2724
2728
let ( mut cond, _) =
2725
2729
self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL | Restrictions :: ALLOW_LET , attrs) ?;
2726
2730
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) ;
2754
2732
2755
2733
Ok ( cond)
2756
2734
}
@@ -3045,7 +3023,8 @@ impl<'a> Parser<'a> {
3045
3023
3046
3024
/// Parses a `while` or `while let` expression (`while` token already eaten).
3047
3025
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| {
3049
3028
err. span_label ( lo, "while parsing the condition of this `while` expression" ) ;
3050
3029
err
3051
3030
} ) ?;
@@ -3429,7 +3408,7 @@ impl<'a> Parser<'a> {
3429
3408
}
3430
3409
3431
3410
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
3433
3412
// `&&` tokens.
3434
3413
fn has_let_expr ( expr : & Expr ) -> bool {
3435
3414
match & expr. kind {
@@ -3450,23 +3429,9 @@ impl<'a> Parser<'a> {
3450
3429
let if_span = self . prev_token . span ;
3451
3430
let mut cond = self . parse_match_guard_condition ( ) ?;
3452
3431
3453
- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3432
+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
3454
3433
3455
3434
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) ;
3470
3435
let span = if_span. to ( cond. span ) ;
3471
3436
self . psess . gated_spans . gate ( sym:: if_let_guard, span) ;
3472
3437
}
@@ -3493,7 +3458,7 @@ impl<'a> Parser<'a> {
3493
3458
unreachable ! ( )
3494
3459
} ;
3495
3460
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) ;
3497
3462
let right = self . prev_token . span ;
3498
3463
self . dcx ( ) . emit_err ( errors:: ParenthesesInMatchPat {
3499
3464
span : vec ! [ left, right] ,
@@ -4072,7 +4037,14 @@ pub(crate) enum ForbiddenLetReason {
4072
4037
NotSupportedParentheses ( #[ primary_span] Span ) ,
4073
4038
}
4074
4039
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
4076
4048
/// easily be caught in parsing. For example:
4077
4049
///
4078
4050
/// ```rust,ignore (example)
@@ -4083,19 +4055,29 @@ pub(crate) enum ForbiddenLetReason {
4083
4055
/// ```
4084
4056
struct CondChecker < ' a > {
4085
4057
parser : & ' a Parser < ' a > ,
4058
+ let_chains_policy : LetChainsPolicy ,
4059
+ depth : u32 ,
4086
4060
forbid_let_reason : Option < ForbiddenLetReason > ,
4087
4061
missing_let : Option < errors:: MaybeMissingLet > ,
4088
4062
comparison : Option < errors:: MaybeComparison > ,
4089
4063
}
4090
4064
4091
4065
impl < ' 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
+ }
4094
4075
}
4095
4076
}
4096
4077
4097
4078
impl MutVisitor for CondChecker < ' _ > {
4098
4079
fn visit_expr ( & mut self , e : & mut P < Expr > ) {
4080
+ self . depth += 1 ;
4099
4081
use ForbiddenLetReason :: * ;
4100
4082
4101
4083
let span = e. span ;
@@ -4110,8 +4092,16 @@ impl MutVisitor for CondChecker<'_> {
4110
4092
comparison : self . comparison ,
4111
4093
} ,
4112
4094
) ) ;
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
+ }
4115
4105
}
4116
4106
}
4117
4107
ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , _, _) => {
@@ -4213,5 +4203,6 @@ impl MutVisitor for CondChecker<'_> {
4213
4203
// These would forbid any let expressions they contain already.
4214
4204
}
4215
4205
}
4206
+ self . depth -= 1 ;
4216
4207
}
4217
4208
}
0 commit comments