@@ -26,6 +26,7 @@ use rustc_macros::Subdiagnostic;
26
26
use rustc_session:: errors:: { ExprParenthesesNeeded , report_lit_error} ;
27
27
use rustc_session:: lint:: BuiltinLintDiag ;
28
28
use rustc_session:: lint:: builtin:: BREAK_WITH_LABEL_AND_LOOP ;
29
+ use rustc_span:: edition:: Edition ;
29
30
use rustc_span:: source_map:: { self , Spanned } ;
30
31
use rustc_span:: { BytePos , ErrorGuaranteed , Ident , Pos , Span , Symbol , kw, sym} ;
31
32
use thin_vec:: { ThinVec , thin_vec} ;
@@ -2145,6 +2146,17 @@ impl<'a> Parser<'a> {
2145
2146
/// Keep this in sync with `Token::can_begin_literal_maybe_minus` and
2146
2147
/// `Lit::from_token` (excluding unary negation).
2147
2148
fn eat_token_lit ( & mut self ) -> Option < token:: Lit > {
2149
+ let check_expr = |expr : P < Expr > | {
2150
+ if let ast:: ExprKind :: Lit ( token_lit) = expr. kind {
2151
+ Some ( token_lit)
2152
+ } else if let ast:: ExprKind :: Unary ( UnOp :: Neg , inner) = & expr. kind
2153
+ && let ast:: Expr { kind : ast:: ExprKind :: Lit ( _) , .. } = * * inner
2154
+ {
2155
+ None
2156
+ } else {
2157
+ panic ! ( "unexpected reparsed expr/literal: {:?}" , expr. kind) ;
2158
+ }
2159
+ } ;
2148
2160
match self . token . uninterpolate ( ) . kind {
2149
2161
token:: Ident ( name, IdentIsRaw :: No ) if name. is_bool_lit ( ) => {
2150
2162
self . bump ( ) ;
@@ -2158,26 +2170,15 @@ impl<'a> Parser<'a> {
2158
2170
let lit = self
2159
2171
. eat_metavar_seq ( MetaVarKind :: Literal , |this| this. parse_literal_maybe_minus ( ) )
2160
2172
. expect ( "metavar seq literal" ) ;
2161
- let ast:: ExprKind :: Lit ( token_lit) = lit. kind else {
2162
- panic ! ( "didn't reparse a literal" ) ;
2163
- } ;
2164
- Some ( token_lit)
2173
+ check_expr ( lit)
2165
2174
}
2166
2175
token:: OpenInvisible ( InvisibleOrigin :: MetaVar (
2167
2176
mv_kind @ MetaVarKind :: Expr { can_begin_literal_maybe_minus : true , .. } ,
2168
2177
) ) => {
2169
2178
let expr = self
2170
2179
. eat_metavar_seq ( mv_kind, |this| this. parse_expr ( ) )
2171
2180
. expect ( "metavar seq expr" ) ;
2172
- if let ast:: ExprKind :: Lit ( token_lit) = expr. kind {
2173
- Some ( token_lit)
2174
- } else if let ast:: ExprKind :: Unary ( UnOp :: Neg , inner) = & expr. kind
2175
- && let ast:: Expr { kind : ast:: ExprKind :: Lit ( _) , .. } = * * inner
2176
- {
2177
- None
2178
- } else {
2179
- panic ! ( "unexpected reparsed expr: {:?}" , expr. kind) ;
2180
- }
2181
+ check_expr ( expr)
2181
2182
}
2182
2183
_ => None ,
2183
2184
}
@@ -2602,7 +2603,10 @@ impl<'a> Parser<'a> {
2602
2603
/// Parses an `if` expression (`if` token already eaten).
2603
2604
fn parse_expr_if ( & mut self ) -> PResult < ' a , P < Expr > > {
2604
2605
let lo = self . prev_token . span ;
2605
- let cond = self . parse_expr_cond ( ) ?;
2606
+ // Scoping code checks the top level edition of the `if`; let's match it here.
2607
+ // The `CondChecker` also checks the edition of the `let` itself, just to make sure.
2608
+ let let_chains_policy = LetChainsPolicy :: EditionDependent { current_edition : lo. edition ( ) } ;
2609
+ let cond = self . parse_expr_cond ( let_chains_policy) ?;
2606
2610
self . parse_if_after_cond ( lo, cond)
2607
2611
}
2608
2612
@@ -2711,18 +2715,17 @@ impl<'a> Parser<'a> {
2711
2715
}
2712
2716
2713
2717
/// Parses the condition of a `if` or `while` expression.
2718
+ ///
2719
+ /// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct,
2720
+ /// i.e. the same span we use to later decide whether the drop behaviour should be that of
2721
+ /// edition `..=2021` or that of `2024..`.
2714
2722
// 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.
2715
- pub fn parse_expr_cond ( & mut self ) -> PResult < ' a , P < Expr > > {
2723
+ pub fn parse_expr_cond ( & mut self , let_chains_policy : LetChainsPolicy ) -> PResult < ' a , P < Expr > > {
2716
2724
let attrs = self . parse_outer_attributes ( ) ?;
2717
2725
let ( mut cond, _) =
2718
2726
self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL | Restrictions :: ALLOW_LET , attrs) ?;
2719
2727
2720
- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
2721
-
2722
- if let ExprKind :: Let ( _, _, _, Recovered :: No ) = cond. kind {
2723
- // Remove the last feature gating of a `let` expression since it's stable.
2724
- self . psess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
2725
- }
2728
+ CondChecker :: new ( self , let_chains_policy) . visit_expr ( & mut cond) ;
2726
2729
2727
2730
Ok ( cond)
2728
2731
}
@@ -3017,7 +3020,8 @@ impl<'a> Parser<'a> {
3017
3020
3018
3021
/// Parses a `while` or `while let` expression (`while` token already eaten).
3019
3022
fn parse_expr_while ( & mut self , opt_label : Option < Label > , lo : Span ) -> PResult < ' a , P < Expr > > {
3020
- let cond = self . parse_expr_cond ( ) . map_err ( |mut err| {
3023
+ let policy = LetChainsPolicy :: EditionDependent { current_edition : lo. edition ( ) } ;
3024
+ let cond = self . parse_expr_cond ( policy) . map_err ( |mut err| {
3021
3025
err. span_label ( lo, "while parsing the condition of this `while` expression" ) ;
3022
3026
err
3023
3027
} ) ?;
@@ -3401,17 +3405,17 @@ impl<'a> Parser<'a> {
3401
3405
}
3402
3406
3403
3407
fn parse_match_arm_guard ( & mut self ) -> PResult < ' a , Option < P < Expr > > > {
3404
- // Used to check the `let_chains` and ` if_let_guard` features mostly by scanning
3408
+ // Used to check the `if_let_guard` feature mostly by scanning
3405
3409
// `&&` tokens.
3406
- fn check_let_expr ( expr : & Expr ) -> ( bool , bool ) {
3410
+ fn has_let_expr ( expr : & Expr ) -> bool {
3407
3411
match & expr. kind {
3408
3412
ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
3409
- let lhs_rslt = check_let_expr ( lhs) ;
3410
- let rhs_rslt = check_let_expr ( rhs) ;
3411
- ( lhs_rslt. 0 || rhs_rslt. 0 , false )
3413
+ let lhs_rslt = has_let_expr ( lhs) ;
3414
+ let rhs_rslt = has_let_expr ( rhs) ;
3415
+ lhs_rslt || rhs_rslt
3412
3416
}
3413
- ExprKind :: Let ( ..) => ( true , true ) ,
3414
- _ => ( false , true ) ,
3417
+ ExprKind :: Let ( ..) => true ,
3418
+ _ => false ,
3415
3419
}
3416
3420
}
3417
3421
if !self . eat_keyword ( exp ! ( If ) ) {
@@ -3422,14 +3426,9 @@ impl<'a> Parser<'a> {
3422
3426
let if_span = self . prev_token . span ;
3423
3427
let mut cond = self . parse_match_guard_condition ( ) ?;
3424
3428
3425
- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3429
+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
3426
3430
3427
- let ( has_let_expr, does_not_have_bin_op) = check_let_expr ( & cond) ;
3428
- if has_let_expr {
3429
- if does_not_have_bin_op {
3430
- // Remove the last feature gating of a `let` expression since it's stable.
3431
- self . psess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
3432
- }
3431
+ if has_let_expr ( & cond) {
3433
3432
let span = if_span. to ( cond. span ) ;
3434
3433
self . psess . gated_spans . gate ( sym:: if_let_guard, span) ;
3435
3434
}
@@ -3456,7 +3455,7 @@ impl<'a> Parser<'a> {
3456
3455
unreachable ! ( )
3457
3456
} ;
3458
3457
self . psess . gated_spans . ungate_last ( sym:: guard_patterns, cond. span ) ;
3459
- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3458
+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
3460
3459
let right = self . prev_token . span ;
3461
3460
self . dcx ( ) . emit_err ( errors:: ParenthesesInMatchPat {
3462
3461
span : vec ! [ left, right] ,
@@ -4027,7 +4026,14 @@ pub(crate) enum ForbiddenLetReason {
4027
4026
NotSupportedParentheses ( #[ primary_span] Span ) ,
4028
4027
}
4029
4028
4030
- /// Visitor to check for invalid/unstable use of `ExprKind::Let` that can't
4029
+ /// Whether let chains are allowed on all editions, or it's edition dependent (allowed only on
4030
+ /// 2024 and later). In case of edition dependence, specify the currently present edition.
4031
+ pub enum LetChainsPolicy {
4032
+ AlwaysAllowed ,
4033
+ EditionDependent { current_edition : Edition } ,
4034
+ }
4035
+
4036
+ /// Visitor to check for invalid use of `ExprKind::Let` that can't
4031
4037
/// easily be caught in parsing. For example:
4032
4038
///
4033
4039
/// ```rust,ignore (example)
@@ -4038,19 +4044,29 @@ pub(crate) enum ForbiddenLetReason {
4038
4044
/// ```
4039
4045
struct CondChecker < ' a > {
4040
4046
parser : & ' a Parser < ' a > ,
4047
+ let_chains_policy : LetChainsPolicy ,
4048
+ depth : u32 ,
4041
4049
forbid_let_reason : Option < ForbiddenLetReason > ,
4042
4050
missing_let : Option < errors:: MaybeMissingLet > ,
4043
4051
comparison : Option < errors:: MaybeComparison > ,
4044
4052
}
4045
4053
4046
4054
impl < ' a > CondChecker < ' a > {
4047
- fn new ( parser : & ' a Parser < ' a > ) -> Self {
4048
- CondChecker { parser, forbid_let_reason : None , missing_let : None , comparison : None }
4055
+ fn new ( parser : & ' a Parser < ' a > , let_chains_policy : LetChainsPolicy ) -> Self {
4056
+ CondChecker {
4057
+ parser,
4058
+ forbid_let_reason : None ,
4059
+ missing_let : None ,
4060
+ comparison : None ,
4061
+ let_chains_policy,
4062
+ depth : 0 ,
4063
+ }
4049
4064
}
4050
4065
}
4051
4066
4052
4067
impl MutVisitor for CondChecker < ' _ > {
4053
4068
fn visit_expr ( & mut self , e : & mut P < Expr > ) {
4069
+ self . depth += 1 ;
4054
4070
use ForbiddenLetReason :: * ;
4055
4071
4056
4072
let span = e. span ;
@@ -4065,8 +4081,16 @@ impl MutVisitor for CondChecker<'_> {
4065
4081
comparison : self . comparison ,
4066
4082
} ,
4067
4083
) ) ;
4068
- } else {
4069
- self . parser . psess . gated_spans . gate ( sym:: let_chains, span) ;
4084
+ } else if self . depth > 1 {
4085
+ // Top level `let` is always allowed; only gate chains
4086
+ match self . let_chains_policy {
4087
+ LetChainsPolicy :: AlwaysAllowed => ( ) ,
4088
+ LetChainsPolicy :: EditionDependent { current_edition } => {
4089
+ if !current_edition. at_least_rust_2024 ( ) || !span. at_least_rust_2024 ( ) {
4090
+ self . parser . psess . gated_spans . gate ( sym:: let_chains, span) ;
4091
+ }
4092
+ }
4093
+ }
4070
4094
}
4071
4095
}
4072
4096
ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , _, _) => {
@@ -4168,5 +4192,6 @@ impl MutVisitor for CondChecker<'_> {
4168
4192
// These would forbid any let expressions they contain already.
4169
4193
}
4170
4194
}
4195
+ self . depth -= 1 ;
4171
4196
}
4172
4197
}
0 commit comments