@@ -10,7 +10,7 @@ use super::{
10
10
use crate :: errors;
11
11
use crate :: maybe_recover_from_interpolated_ty_qpath;
12
12
use ast:: mut_visit:: { noop_visit_expr, MutVisitor } ;
13
- use ast:: { GenBlockKind , Path , PathSegment } ;
13
+ use ast:: { GenBlockKind , Pat , Path , PathSegment } ;
14
14
use core:: mem;
15
15
use rustc_ast:: ptr:: P ;
16
16
use rustc_ast:: token:: { self , Delimiter , Token , TokenKind } ;
@@ -2609,30 +2609,72 @@ impl<'a> Parser<'a> {
2609
2609
}
2610
2610
}
2611
2611
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.
2620
2666
} ;
2621
-
2622
- let pat = self . parse_pat_allow_top_alt (
2623
- None ,
2624
- RecoverComma :: Yes ,
2625
- RecoverColon :: Yes ,
2626
- CommaRecoveryMode :: LikelyTuple ,
2627
- ) ?;
2628
2667
if !self . eat_keyword ( kw:: In ) {
2629
2668
self . error_missing_in_for_loop ( ) ;
2630
2669
}
2631
2670
self . check_for_for_in_in_typo ( self . prev_token . span ) ;
2632
2671
let expr = self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL , None ) ?;
2672
+ Ok ( ( pat, expr) )
2673
+ }
2633
2674
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 ( ) ?;
2636
2678
// Recover from missing expression in `for` loop
2637
2679
if matches ! ( expr. kind, ExprKind :: Block ( ..) )
2638
2680
&& !matches ! ( self . token. kind, token:: OpenDelim ( Delimiter :: Brace ) )
@@ -2853,47 +2895,10 @@ impl<'a> Parser<'a> {
2853
2895
}
2854
2896
2855
2897
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
- }
2869
2898
let attrs = self . parse_outer_attributes ( ) ?;
2870
2899
self . collect_tokens_trailing_token ( attrs, ForceCollect :: No , |this, attrs| {
2871
2900
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 ( ) ?;
2897
2902
let arrow_span = this. token . span ;
2898
2903
if let Err ( mut err) = this. expect ( & token:: FatArrow ) {
2899
2904
// We might have a `=>` -> `=` or `->` typo (issue #89396).
@@ -3023,6 +3028,90 @@ impl<'a> Parser<'a> {
3023
3028
} )
3024
3029
}
3025
3030
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
+
3026
3115
fn parse_match_guard_condition ( & mut self ) -> PResult < ' a , P < Expr > > {
3027
3116
self . parse_expr_res ( Restrictions :: ALLOW_LET | Restrictions :: IN_IF_GUARD , None ) . map_err (
3028
3117
|mut err| {
0 commit comments