@@ -10,7 +10,7 @@ use super::{
1010use crate :: errors;
1111use crate :: maybe_recover_from_interpolated_ty_qpath;
1212use ast:: mut_visit:: { noop_visit_expr, MutVisitor } ;
13- use ast:: { GenBlockKind , Path , PathSegment } ;
13+ use ast:: { GenBlockKind , Pat , Path , PathSegment } ;
1414use core:: mem;
1515use rustc_ast:: ptr:: P ;
1616use rustc_ast:: token:: { self , Delimiter , Token , TokenKind } ;
@@ -2609,30 +2609,72 @@ impl<'a> Parser<'a> {
26092609 }
26102610 }
26112611
2612- /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2613- fn parse_expr_for ( & mut self , opt_label : Option < Label > , lo : Span ) -> PResult < ' a , P < Expr > > {
2614- // Record whether we are about to parse `for (`.
2615- // This is used below for recovery in case of `for ( $stuff ) $block`
2616- // in which case we will suggest `for $stuff $block`.
2617- let begin_paren = match self . token . kind {
2618- token:: OpenDelim ( Delimiter :: Parenthesis ) => Some ( self . token . span ) ,
2619- _ => None ,
2612+ fn parse_for_head ( & mut self ) -> PResult < ' a , ( P < Pat > , P < Expr > ) > {
2613+ let begin_paren = if self . token . kind == token:: OpenDelim ( Delimiter :: Parenthesis ) {
2614+ // Record whether we are about to parse `for (`.
2615+ // This is used below for recovery in case of `for ( $stuff ) $block`
2616+ // in which case we will suggest `for $stuff $block`.
2617+ let start_span = self . token . span ;
2618+ let left = self . prev_token . span . between ( self . look_ahead ( 1 , |t| t. span ) ) ;
2619+ Some ( ( start_span, left) )
2620+ } else {
2621+ None
2622+ } ;
2623+ // Try to parse the pattern `for ($PAT) in $EXPR`.
2624+ let pat = match (
2625+ self . parse_pat_allow_top_alt (
2626+ None ,
2627+ RecoverComma :: Yes ,
2628+ RecoverColon :: Yes ,
2629+ CommaRecoveryMode :: LikelyTuple ,
2630+ ) ,
2631+ begin_paren,
2632+ ) {
2633+ ( Ok ( pat) , _) => pat, // Happy path.
2634+ ( Err ( err) , Some ( ( start_span, left) ) ) if self . eat_keyword ( kw:: In ) => {
2635+ // We know for sure we have seen `for ($SOMETHING in`. In the happy path this would
2636+ // happen right before the return of this method.
2637+ let expr = match self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL , None ) {
2638+ Ok ( expr) => expr,
2639+ Err ( expr_err) => {
2640+ // We don't know what followed the `in`, so cancel and bubble up the
2641+ // original error.
2642+ expr_err. cancel ( ) ;
2643+ return Err ( err) ;
2644+ }
2645+ } ;
2646+ return if self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis ) {
2647+ // We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the
2648+ // parser state and emit a targetted suggestion.
2649+ let span = vec ! [ start_span, self . token. span] ;
2650+ let right = self . prev_token . span . between ( self . look_ahead ( 1 , |t| t. span ) ) ;
2651+ self . bump ( ) ; // )
2652+ err. cancel ( ) ;
2653+ self . sess . emit_err ( errors:: ParenthesesInForHead {
2654+ span,
2655+ // With e.g. `for (x) in y)` this would replace `(x) in y)`
2656+ // with `x) in y)` which is syntactically invalid.
2657+ // However, this is prevented before we get here.
2658+ sugg : errors:: ParenthesesInForHeadSugg { left, right } ,
2659+ } ) ;
2660+ Ok ( ( self . mk_pat ( start_span. to ( right) , ast:: PatKind :: Wild ) , expr) )
2661+ } else {
2662+ Err ( err) // Some other error, bubble up.
2663+ } ;
2664+ }
2665+ ( Err ( err) , _) => return Err ( err) , // Some other error, bubble up.
26202666 } ;
2621-
2622- let pat = self . parse_pat_allow_top_alt (
2623- None ,
2624- RecoverComma :: Yes ,
2625- RecoverColon :: Yes ,
2626- CommaRecoveryMode :: LikelyTuple ,
2627- ) ?;
26282667 if !self . eat_keyword ( kw:: In ) {
26292668 self . error_missing_in_for_loop ( ) ;
26302669 }
26312670 self . check_for_for_in_in_typo ( self . prev_token . span ) ;
26322671 let expr = self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL , None ) ?;
2672+ Ok ( ( pat, expr) )
2673+ }
26332674
2634- let pat = self . recover_parens_around_for_head ( pat, begin_paren) ;
2635-
2675+ /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2676+ fn parse_expr_for ( & mut self , opt_label : Option < Label > , lo : Span ) -> PResult < ' a , P < Expr > > {
2677+ let ( pat, expr) = self . parse_for_head ( ) ?;
26362678 // Recover from missing expression in `for` loop
26372679 if matches ! ( expr. kind, ExprKind :: Block ( ..) )
26382680 && !matches ! ( self . token. kind, token:: OpenDelim ( Delimiter :: Brace ) )
@@ -2853,47 +2895,10 @@ impl<'a> Parser<'a> {
28532895 }
28542896
28552897 pub ( super ) fn parse_arm ( & mut self ) -> PResult < ' a , Arm > {
2856- // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
2857- // `&&` tokens.
2858- fn check_let_expr ( expr : & Expr ) -> ( bool , bool ) {
2859- match & expr. kind {
2860- ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
2861- let lhs_rslt = check_let_expr ( lhs) ;
2862- let rhs_rslt = check_let_expr ( rhs) ;
2863- ( lhs_rslt. 0 || rhs_rslt. 0 , false )
2864- }
2865- ExprKind :: Let ( ..) => ( true , true ) ,
2866- _ => ( false , true ) ,
2867- }
2868- }
28692898 let attrs = self . parse_outer_attributes ( ) ?;
28702899 self . collect_tokens_trailing_token ( attrs, ForceCollect :: No , |this, attrs| {
28712900 let lo = this. token . span ;
2872- let pat = this. parse_pat_allow_top_alt (
2873- None ,
2874- RecoverComma :: Yes ,
2875- RecoverColon :: Yes ,
2876- CommaRecoveryMode :: EitherTupleOrPipe ,
2877- ) ?;
2878- let guard = if this. eat_keyword ( kw:: If ) {
2879- let if_span = this. prev_token . span ;
2880- let mut cond = this. parse_match_guard_condition ( ) ?;
2881-
2882- CondChecker :: new ( this) . visit_expr ( & mut cond) ;
2883-
2884- let ( has_let_expr, does_not_have_bin_op) = check_let_expr ( & cond) ;
2885- if has_let_expr {
2886- if does_not_have_bin_op {
2887- // Remove the last feature gating of a `let` expression since it's stable.
2888- this. sess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
2889- }
2890- let span = if_span. to ( cond. span ) ;
2891- this. sess . gated_spans . gate ( sym:: if_let_guard, span) ;
2892- }
2893- Some ( cond)
2894- } else {
2895- None
2896- } ;
2901+ let ( pat, guard) = this. parse_match_arm_pat_and_guard ( ) ?;
28972902 let arrow_span = this. token . span ;
28982903 if let Err ( mut err) = this. expect ( & token:: FatArrow ) {
28992904 // We might have a `=>` -> `=` or `->` typo (issue #89396).
@@ -3023,6 +3028,90 @@ impl<'a> Parser<'a> {
30233028 } )
30243029 }
30253030
3031+ fn parse_match_arm_guard ( & mut self ) -> PResult < ' a , Option < P < Expr > > > {
3032+ // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
3033+ // `&&` tokens.
3034+ fn check_let_expr ( expr : & Expr ) -> ( bool , bool ) {
3035+ match & expr. kind {
3036+ ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
3037+ let lhs_rslt = check_let_expr ( lhs) ;
3038+ let rhs_rslt = check_let_expr ( rhs) ;
3039+ ( lhs_rslt. 0 || rhs_rslt. 0 , false )
3040+ }
3041+ ExprKind :: Let ( ..) => ( true , true ) ,
3042+ _ => ( false , true ) ,
3043+ }
3044+ }
3045+ if !self . eat_keyword ( kw:: If ) {
3046+ // No match arm guard present.
3047+ return Ok ( None ) ;
3048+ }
3049+
3050+ let if_span = self . prev_token . span ;
3051+ let mut cond = self . parse_match_guard_condition ( ) ?;
3052+
3053+ CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3054+
3055+ let ( has_let_expr, does_not_have_bin_op) = check_let_expr ( & cond) ;
3056+ if has_let_expr {
3057+ if does_not_have_bin_op {
3058+ // Remove the last feature gating of a `let` expression since it's stable.
3059+ self . sess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
3060+ }
3061+ let span = if_span. to ( cond. span ) ;
3062+ self . sess . gated_spans . gate ( sym:: if_let_guard, span) ;
3063+ }
3064+ Ok ( Some ( cond) )
3065+ }
3066+
3067+ fn parse_match_arm_pat_and_guard ( & mut self ) -> PResult < ' a , ( P < Pat > , Option < P < Expr > > ) > {
3068+ if self . token . kind == token:: OpenDelim ( Delimiter :: Parenthesis ) {
3069+ // Detect and recover from `($pat if $cond) => $arm`.
3070+ let left = self . token . span ;
3071+ match self . parse_pat_allow_top_alt (
3072+ None ,
3073+ RecoverComma :: Yes ,
3074+ RecoverColon :: Yes ,
3075+ CommaRecoveryMode :: EitherTupleOrPipe ,
3076+ ) {
3077+ Ok ( pat) => Ok ( ( pat, self . parse_match_arm_guard ( ) ?) ) ,
3078+ Err ( err)
3079+ if let prev_sp = self . prev_token . span
3080+ && let true = self . eat_keyword ( kw:: If ) =>
3081+ {
3082+ // We know for certain we've found `($pat if` so far.
3083+ let mut cond = match self . parse_match_guard_condition ( ) {
3084+ Ok ( cond) => cond,
3085+ Err ( cond_err) => {
3086+ cond_err. cancel ( ) ;
3087+ return Err ( err) ;
3088+ }
3089+ } ;
3090+ err. cancel ( ) ;
3091+ CondChecker :: new ( self ) . visit_expr ( & mut cond) ;
3092+ self . eat_to_tokens ( & [ & token:: CloseDelim ( Delimiter :: Parenthesis ) ] ) ;
3093+ self . expect ( & token:: CloseDelim ( Delimiter :: Parenthesis ) ) ?;
3094+ let right = self . prev_token . span ;
3095+ self . sess . emit_err ( errors:: ParenthesesInMatchPat {
3096+ span : vec ! [ left, right] ,
3097+ sugg : errors:: ParenthesesInMatchPatSugg { left, right } ,
3098+ } ) ;
3099+ Ok ( ( self . mk_pat ( left. to ( prev_sp) , ast:: PatKind :: Wild ) , Some ( cond) ) )
3100+ }
3101+ Err ( err) => Err ( err) ,
3102+ }
3103+ } else {
3104+ // Regular parser flow:
3105+ let pat = self . parse_pat_allow_top_alt (
3106+ None ,
3107+ RecoverComma :: Yes ,
3108+ RecoverColon :: Yes ,
3109+ CommaRecoveryMode :: EitherTupleOrPipe ,
3110+ ) ?;
3111+ Ok ( ( pat, self . parse_match_arm_guard ( ) ?) )
3112+ }
3113+ }
3114+
30263115 fn parse_match_guard_condition ( & mut self ) -> PResult < ' a , P < Expr > > {
30273116 self . parse_expr_res ( Restrictions :: ALLOW_LET | Restrictions :: IN_IF_GUARD , None ) . map_err (
30283117 |mut err| {
0 commit comments