@@ -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 :: 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,15 @@ 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
2722
+ /// The specified `edition` in LetChainsPolicy should be that of the whole `if` construct: the same
2720
2723
/// span that we later decide the drop behaviour on (editions ..=2021 vs 2024..)
2721
2724
// 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 > > {
2723
2726
let attrs = self . parse_outer_attributes ( ) ?;
2724
2727
let ( mut cond, _) =
2725
2728
self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL | Restrictions :: ALLOW_LET , attrs) ?;
2726
2729
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) ;
2754
2731
2755
2732
Ok ( cond)
2756
2733
}
@@ -3045,7 +3022,7 @@ impl<'a> Parser<'a> {
3045
3022
3046
3023
/// Parses a `while` or `while let` expression (`while` token already eaten).
3047
3024
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| {
3049
3026
err. span_label ( lo, "while parsing the condition of this `while` expression" ) ;
3050
3027
err
3051
3028
} ) ?;
@@ -3429,7 +3406,7 @@ impl<'a> Parser<'a> {
3429
3406
}
3430
3407
3431
3408
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
3433
3410
// `&&` tokens.
3434
3411
fn has_let_expr ( expr : & Expr ) -> bool {
3435
3412
match & expr. kind {
@@ -3450,23 +3427,9 @@ impl<'a> Parser<'a> {
3450
3427
let if_span = self . prev_token . span ;
3451
3428
let mut cond = self . parse_match_guard_condition ( ) ?;
3452
3429
3453
- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3430
+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
3454
3431
3455
3432
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
3433
let span = if_span. to ( cond. span ) ;
3471
3434
self . psess . gated_spans . gate ( sym:: if_let_guard, span) ;
3472
3435
}
@@ -3493,7 +3456,7 @@ impl<'a> Parser<'a> {
3493
3456
unreachable ! ( )
3494
3457
} ;
3495
3458
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) ;
3497
3460
let right = self . prev_token . span ;
3498
3461
self . dcx ( ) . emit_err ( errors:: ParenthesesInMatchPat {
3499
3462
span : vec ! [ left, right] ,
@@ -4072,7 +4035,14 @@ pub(crate) enum ForbiddenLetReason {
4072
4035
NotSupportedParentheses ( #[ primary_span] Span ) ,
4073
4036
}
4074
4037
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
4076
4046
/// easily be caught in parsing. For example:
4077
4047
///
4078
4048
/// ```rust,ignore (example)
@@ -4083,19 +4053,29 @@ pub(crate) enum ForbiddenLetReason {
4083
4053
/// ```
4084
4054
struct CondChecker < ' a > {
4085
4055
parser : & ' a Parser < ' a > ,
4056
+ let_chains_policy : LetChainsPolicy ,
4057
+ depth : u32 ,
4086
4058
forbid_let_reason : Option < ForbiddenLetReason > ,
4087
4059
missing_let : Option < errors:: MaybeMissingLet > ,
4088
4060
comparison : Option < errors:: MaybeComparison > ,
4089
4061
}
4090
4062
4091
4063
impl < ' 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
+ }
4094
4073
}
4095
4074
}
4096
4075
4097
4076
impl MutVisitor for CondChecker < ' _ > {
4098
4077
fn visit_expr ( & mut self , e : & mut P < Expr > ) {
4078
+ self . depth += 1 ;
4099
4079
use ForbiddenLetReason :: * ;
4100
4080
4101
4081
let span = e. span ;
@@ -4110,8 +4090,16 @@ impl MutVisitor for CondChecker<'_> {
4110
4090
comparison : self . comparison ,
4111
4091
} ,
4112
4092
) ) ;
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
+ }
4115
4103
}
4116
4104
}
4117
4105
ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , _, _) => {
@@ -4213,5 +4201,6 @@ impl MutVisitor for CondChecker<'_> {
4213
4201
// These would forbid any let expressions they contain already.
4214
4202
}
4215
4203
}
4204
+ self . depth -= 1 ;
4216
4205
}
4217
4206
}
0 commit comments