1+ // ignore-tidy-filelength
12use super :: diagnostics:: SnapshotParser ;
23use super :: pat:: { CommaRecoveryMode , Expected , RecoverColon , RecoverComma } ;
34use super :: ty:: { AllowPlus , RecoverQPath , RecoverReturnSign } ;
@@ -9,7 +10,7 @@ use super::{
910use crate :: errors;
1011use crate :: maybe_recover_from_interpolated_ty_qpath;
1112use ast:: mut_visit:: { noop_visit_expr, MutVisitor } ;
12- use ast:: { GenBlockKind , Path , PathSegment } ;
13+ use ast:: { GenBlockKind , Pat , Path , PathSegment } ;
1314use core:: mem;
1415use rustc_ast:: ptr:: P ;
1516use rustc_ast:: token:: { self , Delimiter , Token , TokenKind } ;
@@ -2606,30 +2607,72 @@ impl<'a> Parser<'a> {
26062607 }
26072608 }
26082609
2609- /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2610- fn parse_expr_for ( & mut self , opt_label : Option < Label > , lo : Span ) -> PResult < ' a , P < Expr > > {
2611- // Record whether we are about to parse `for (`.
2612- // This is used below for recovery in case of `for ( $stuff ) $block`
2613- // in which case we will suggest `for $stuff $block`.
2614- let begin_paren = match self . token . kind {
2615- token:: OpenDelim ( Delimiter :: Parenthesis ) => Some ( self . token . span ) ,
2616- _ => None ,
2610+ fn parse_for_head ( & mut self ) -> PResult < ' a , ( P < Pat > , P < Expr > ) > {
2611+ let begin_paren = if self . token . kind == token:: OpenDelim ( Delimiter :: Parenthesis ) {
2612+ // Record whether we are about to parse `for (`.
2613+ // This is used below for recovery in case of `for ( $stuff ) $block`
2614+ // in which case we will suggest `for $stuff $block`.
2615+ let start_span = self . token . span ;
2616+ let left = self . prev_token . span . between ( self . look_ahead ( 1 , |t| t. span ) ) ;
2617+ Some ( ( start_span, left) )
2618+ } else {
2619+ None
2620+ } ;
2621+ // Try to parse the pattern `for ($PAT) in $EXPR`.
2622+ let pat = match (
2623+ self . parse_pat_allow_top_alt (
2624+ None ,
2625+ RecoverComma :: Yes ,
2626+ RecoverColon :: Yes ,
2627+ CommaRecoveryMode :: LikelyTuple ,
2628+ ) ,
2629+ begin_paren,
2630+ ) {
2631+ ( Ok ( pat) , _) => pat, // Happy path.
2632+ ( Err ( err) , Some ( ( start_span, left) ) ) if self . eat_keyword ( kw:: In ) => {
2633+ // We know for sure we have seen `for ($SOMETHING in`. In the happy path this would
2634+ // happen right before the return of this method.
2635+ let expr = match self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL , None ) {
2636+ Ok ( expr) => expr,
2637+ Err ( expr_err) => {
2638+ // We don't know what followed the `in`, so cancel and bubble up the
2639+ // original error.
2640+ expr_err. cancel ( ) ;
2641+ return Err ( err) ;
2642+ }
2643+ } ;
2644+ return if self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis ) {
2645+ // We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the
2646+ // parser state and emit a targetted suggestion.
2647+ let span = vec ! [ start_span, self . token. span] ;
2648+ let right = self . prev_token . span . between ( self . look_ahead ( 1 , |t| t. span ) ) ;
2649+ self . bump ( ) ; // )
2650+ err. cancel ( ) ;
2651+ self . sess . emit_err ( errors:: ParenthesesInForHead {
2652+ span,
2653+ // With e.g. `for (x) in y)` this would replace `(x) in y)`
2654+ // with `x) in y)` which is syntactically invalid.
2655+ // However, this is prevented before we get here.
2656+ sugg : errors:: ParenthesesInForHeadSugg { left, right } ,
2657+ } ) ;
2658+ Ok ( ( self . mk_pat ( start_span. to ( right) , ast:: PatKind :: Wild ) , expr) )
2659+ } else {
2660+ Err ( err) // Some other error, bubble up.
2661+ } ;
2662+ }
2663+ ( Err ( err) , _) => return Err ( err) , // Some other error, bubble up.
26172664 } ;
2618-
2619- let pat = self . parse_pat_allow_top_alt (
2620- None ,
2621- RecoverComma :: Yes ,
2622- RecoverColon :: Yes ,
2623- CommaRecoveryMode :: LikelyTuple ,
2624- ) ?;
26252665 if !self . eat_keyword ( kw:: In ) {
26262666 self . error_missing_in_for_loop ( ) ;
26272667 }
26282668 self . check_for_for_in_in_typo ( self . prev_token . span ) ;
26292669 let expr = self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL , None ) ?;
2670+ Ok ( ( pat, expr) )
2671+ }
26302672
2631- let pat = self . recover_parens_around_for_head ( pat, begin_paren) ;
2632-
2673+ /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2674+ fn parse_expr_for ( & mut self , opt_label : Option < Label > , lo : Span ) -> PResult < ' a , P < Expr > > {
2675+ let ( pat, expr) = self . parse_for_head ( ) ?;
26332676 // Recover from missing expression in `for` loop
26342677 if matches ! ( expr. kind, ExprKind :: Block ( ..) )
26352678 && !matches ! ( self . token. kind, token:: OpenDelim ( Delimiter :: Brace ) )
@@ -2850,47 +2893,10 @@ impl<'a> Parser<'a> {
28502893 }
28512894
28522895 pub ( super ) fn parse_arm ( & mut self ) -> PResult < ' a , Arm > {
2853- // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
2854- // `&&` tokens.
2855- fn check_let_expr ( expr : & Expr ) -> ( bool , bool ) {
2856- match & expr. kind {
2857- ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
2858- let lhs_rslt = check_let_expr ( lhs) ;
2859- let rhs_rslt = check_let_expr ( rhs) ;
2860- ( lhs_rslt. 0 || rhs_rslt. 0 , false )
2861- }
2862- ExprKind :: Let ( ..) => ( true , true ) ,
2863- _ => ( false , true ) ,
2864- }
2865- }
28662896 let attrs = self . parse_outer_attributes ( ) ?;
28672897 self . collect_tokens_trailing_token ( attrs, ForceCollect :: No , |this, attrs| {
28682898 let lo = this. token . span ;
2869- let pat = this. parse_pat_allow_top_alt (
2870- None ,
2871- RecoverComma :: Yes ,
2872- RecoverColon :: Yes ,
2873- CommaRecoveryMode :: EitherTupleOrPipe ,
2874- ) ?;
2875- let guard = if this. eat_keyword ( kw:: If ) {
2876- let if_span = this. prev_token . span ;
2877- let mut cond = this. parse_match_guard_condition ( ) ?;
2878-
2879- CondChecker { parser : this, forbid_let_reason : None } . visit_expr ( & mut cond) ;
2880-
2881- let ( has_let_expr, does_not_have_bin_op) = check_let_expr ( & cond) ;
2882- if has_let_expr {
2883- if does_not_have_bin_op {
2884- // Remove the last feature gating of a `let` expression since it's stable.
2885- this. sess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
2886- }
2887- let span = if_span. to ( cond. span ) ;
2888- this. sess . gated_spans . gate ( sym:: if_let_guard, span) ;
2889- }
2890- Some ( cond)
2891- } else {
2892- None
2893- } ;
2899+ let ( pat, guard) = this. parse_match_arm_pat_and_guard ( ) ?;
28942900 let arrow_span = this. token . span ;
28952901 if let Err ( mut err) = this. expect ( & token:: FatArrow ) {
28962902 // We might have a `=>` -> `=` or `->` typo (issue #89396).
@@ -3020,6 +3026,90 @@ impl<'a> Parser<'a> {
30203026 } )
30213027 }
30223028
3029+ fn parse_match_arm_guard ( & mut self ) -> PResult < ' a , Option < P < Expr > > > {
3030+ // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
3031+ // `&&` tokens.
3032+ fn check_let_expr ( expr : & Expr ) -> ( bool , bool ) {
3033+ match & expr. kind {
3034+ ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
3035+ let lhs_rslt = check_let_expr ( lhs) ;
3036+ let rhs_rslt = check_let_expr ( rhs) ;
3037+ ( lhs_rslt. 0 || rhs_rslt. 0 , false )
3038+ }
3039+ ExprKind :: Let ( ..) => ( true , true ) ,
3040+ _ => ( false , true ) ,
3041+ }
3042+ }
3043+ if !self . eat_keyword ( kw:: If ) {
3044+ // No match arm guard present.
3045+ return Ok ( None ) ;
3046+ }
3047+
3048+ let if_span = self . prev_token . span ;
3049+ let mut cond = self . parse_match_guard_condition ( ) ?;
3050+
3051+ CondChecker { parser : self , forbid_let_reason : None } . visit_expr ( & mut cond) ;
3052+
3053+ let ( has_let_expr, does_not_have_bin_op) = check_let_expr ( & cond) ;
3054+ if has_let_expr {
3055+ if does_not_have_bin_op {
3056+ // Remove the last feature gating of a `let` expression since it's stable.
3057+ self . sess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
3058+ }
3059+ let span = if_span. to ( cond. span ) ;
3060+ self . sess . gated_spans . gate ( sym:: if_let_guard, span) ;
3061+ }
3062+ Ok ( Some ( cond) )
3063+ }
3064+
3065+ fn parse_match_arm_pat_and_guard ( & mut self ) -> PResult < ' a , ( P < Pat > , Option < P < Expr > > ) > {
3066+ if self . token . kind == token:: OpenDelim ( Delimiter :: Parenthesis ) {
3067+ // Detect and recover from `($pat if $cond) => $arm`.
3068+ let left = self . token . span ;
3069+ match self . parse_pat_allow_top_alt (
3070+ None ,
3071+ RecoverComma :: Yes ,
3072+ RecoverColon :: Yes ,
3073+ CommaRecoveryMode :: EitherTupleOrPipe ,
3074+ ) {
3075+ Ok ( pat) => Ok ( ( pat, self . parse_match_arm_guard ( ) ?) ) ,
3076+ Err ( err)
3077+ if let prev_sp = self . prev_token . span
3078+ && let true = self . eat_keyword ( kw:: If ) =>
3079+ {
3080+ // We know for certain we've found `($pat if` so far.
3081+ let mut cond = match self . parse_match_guard_condition ( ) {
3082+ Ok ( cond) => cond,
3083+ Err ( cond_err) => {
3084+ cond_err. cancel ( ) ;
3085+ return Err ( err) ;
3086+ }
3087+ } ;
3088+ err. cancel ( ) ;
3089+ CondChecker { parser : self , forbid_let_reason : None } . visit_expr ( & mut cond) ;
3090+ self . eat_to_tokens ( & [ & token:: CloseDelim ( Delimiter :: Parenthesis ) ] ) ;
3091+ self . expect ( & token:: CloseDelim ( Delimiter :: Parenthesis ) ) ?;
3092+ let right = self . prev_token . span ;
3093+ self . sess . emit_err ( errors:: ParenthesesInMatchPat {
3094+ span : vec ! [ left, right] ,
3095+ sugg : errors:: ParenthesesInMatchPatSugg { left, right } ,
3096+ } ) ;
3097+ Ok ( ( self . mk_pat ( left. to ( prev_sp) , ast:: PatKind :: Wild ) , Some ( cond) ) )
3098+ }
3099+ Err ( err) => Err ( err) ,
3100+ }
3101+ } else {
3102+ // Regular parser flow:
3103+ let pat = self . parse_pat_allow_top_alt (
3104+ None ,
3105+ RecoverComma :: Yes ,
3106+ RecoverColon :: Yes ,
3107+ CommaRecoveryMode :: EitherTupleOrPipe ,
3108+ ) ?;
3109+ Ok ( ( pat, self . parse_match_arm_guard ( ) ?) )
3110+ }
3111+ }
3112+
30233113 fn parse_match_guard_condition ( & mut self ) -> PResult < ' a , P < Expr > > {
30243114 self . parse_expr_res ( Restrictions :: ALLOW_LET | Restrictions :: IN_IF_GUARD , None ) . map_err (
30253115 |mut err| {
0 commit comments