@@ -26,6 +26,7 @@ use rustc_macros::Subdiagnostic;
2626use rustc_session:: errors:: { ExprParenthesesNeeded , report_lit_error} ;
2727use rustc_session:: lint:: BuiltinLintDiag ;
2828use rustc_session:: lint:: builtin:: BREAK_WITH_LABEL_AND_LOOP ;
29+ use rustc_span:: edition:: Edition ;
2930use rustc_span:: source_map:: { self , Spanned } ;
3031use rustc_span:: { BytePos , ErrorGuaranteed , Ident , Pos , Span , Symbol , kw, sym} ;
3132use thin_vec:: { ThinVec , thin_vec} ;
@@ -2145,6 +2146,17 @@ impl<'a> Parser<'a> {
21452146 /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and
21462147 /// `Lit::from_token` (excluding unary negation).
21472148 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+ } ;
21482160 match self . token . uninterpolate ( ) . kind {
21492161 token:: Ident ( name, IdentIsRaw :: No ) if name. is_bool_lit ( ) => {
21502162 self . bump ( ) ;
@@ -2158,26 +2170,15 @@ impl<'a> Parser<'a> {
21582170 let lit = self
21592171 . eat_metavar_seq ( MetaVarKind :: Literal , |this| this. parse_literal_maybe_minus ( ) )
21602172 . 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)
21652174 }
21662175 token:: OpenInvisible ( InvisibleOrigin :: MetaVar (
21672176 mv_kind @ MetaVarKind :: Expr { can_begin_literal_maybe_minus : true , .. } ,
21682177 ) ) => {
21692178 let expr = self
21702179 . eat_metavar_seq ( mv_kind, |this| this. parse_expr ( ) )
21712180 . 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)
21812182 }
21822183 _ => None ,
21832184 }
@@ -2602,7 +2603,10 @@ impl<'a> Parser<'a> {
26022603 /// Parses an `if` expression (`if` token already eaten).
26032604 fn parse_expr_if ( & mut self ) -> PResult < ' a , P < Expr > > {
26042605 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) ?;
26062610 self . parse_if_after_cond ( lo, cond)
26072611 }
26082612
@@ -2711,18 +2715,17 @@ impl<'a> Parser<'a> {
27112715 }
27122716
27132717 /// 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..`.
27142722 // 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 > > {
27162724 let attrs = self . parse_outer_attributes ( ) ?;
27172725 let ( mut cond, _) =
27182726 self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL | Restrictions :: ALLOW_LET , attrs) ?;
27192727
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) ;
27262729
27272730 Ok ( cond)
27282731 }
@@ -3017,7 +3020,8 @@ impl<'a> Parser<'a> {
30173020
30183021 /// Parses a `while` or `while let` expression (`while` token already eaten).
30193022 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| {
30213025 err. span_label ( lo, "while parsing the condition of this `while` expression" ) ;
30223026 err
30233027 } ) ?;
@@ -3401,17 +3405,17 @@ impl<'a> Parser<'a> {
34013405 }
34023406
34033407 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
34053409 // `&&` tokens.
3406- fn check_let_expr ( expr : & Expr ) -> ( bool , bool ) {
3410+ fn has_let_expr ( expr : & Expr ) -> bool {
34073411 match & expr. kind {
34083412 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
34123416 }
3413- ExprKind :: Let ( ..) => ( true , true ) ,
3414- _ => ( false , true ) ,
3417+ ExprKind :: Let ( ..) => true ,
3418+ _ => false ,
34153419 }
34163420 }
34173421 if !self . eat_keyword ( exp ! ( If ) ) {
@@ -3422,14 +3426,9 @@ impl<'a> Parser<'a> {
34223426 let if_span = self . prev_token . span ;
34233427 let mut cond = self . parse_match_guard_condition ( ) ?;
34243428
3425- CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3429+ CondChecker :: new ( self , LetChainsPolicy :: AlwaysAllowed ) . visit_expr ( & mut cond) ;
34263430
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) {
34333432 let span = if_span. to ( cond. span ) ;
34343433 self . psess . gated_spans . gate ( sym:: if_let_guard, span) ;
34353434 }
@@ -3456,7 +3455,7 @@ impl<'a> Parser<'a> {
34563455 unreachable ! ( )
34573456 } ;
34583457 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) ;
34603459 let right = self . prev_token . span ;
34613460 self . dcx ( ) . emit_err ( errors:: ParenthesesInMatchPat {
34623461 span : vec ! [ left, right] ,
@@ -4027,7 +4026,14 @@ pub(crate) enum ForbiddenLetReason {
40274026 NotSupportedParentheses ( #[ primary_span] Span ) ,
40284027}
40294028
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
40314037/// easily be caught in parsing. For example:
40324038///
40334039/// ```rust,ignore (example)
@@ -4038,19 +4044,29 @@ pub(crate) enum ForbiddenLetReason {
40384044/// ```
40394045struct CondChecker < ' a > {
40404046 parser : & ' a Parser < ' a > ,
4047+ let_chains_policy : LetChainsPolicy ,
4048+ depth : u32 ,
40414049 forbid_let_reason : Option < ForbiddenLetReason > ,
40424050 missing_let : Option < errors:: MaybeMissingLet > ,
40434051 comparison : Option < errors:: MaybeComparison > ,
40444052}
40454053
40464054impl < ' 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+ }
40494064 }
40504065}
40514066
40524067impl MutVisitor for CondChecker < ' _ > {
40534068 fn visit_expr ( & mut self , e : & mut P < Expr > ) {
4069+ self . depth += 1 ;
40544070 use ForbiddenLetReason :: * ;
40554071
40564072 let span = e. span ;
@@ -4065,8 +4081,16 @@ impl MutVisitor for CondChecker<'_> {
40654081 comparison : self . comparison ,
40664082 } ,
40674083 ) ) ;
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+ }
40704094 }
40714095 }
40724096 ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , _, _) => {
@@ -4168,5 +4192,6 @@ impl MutVisitor for CondChecker<'_> {
41684192 // These would forbid any let expressions they contain already.
41694193 }
41704194 }
4195+ self . depth -= 1 ;
41714196 }
41724197}
0 commit comments