@@ -9,7 +9,7 @@ use super::{
9
9
use crate :: errors;
10
10
use crate :: maybe_recover_from_interpolated_ty_qpath;
11
11
use ast:: mut_visit:: { noop_visit_expr, MutVisitor } ;
12
- use ast:: { GenBlockKind , Path , PathSegment } ;
12
+ use ast:: { GenBlockKind , Pat , Path , PathSegment } ;
13
13
use core:: mem;
14
14
use rustc_ast:: ptr:: P ;
15
15
use rustc_ast:: token:: { self , Delimiter , Token , TokenKind } ;
@@ -2589,30 +2589,66 @@ impl<'a> Parser<'a> {
2589
2589
}
2590
2590
}
2591
2591
2592
- /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2593
- fn parse_expr_for ( & mut self , opt_label : Option < Label > , lo : Span ) -> PResult < ' a , P < Expr > > {
2594
- // Record whether we are about to parse `for (`.
2595
- // This is used below for recovery in case of `for ( $stuff ) $block`
2596
- // in which case we will suggest `for $stuff $block`.
2597
- let begin_paren = match self . token . kind {
2598
- token:: OpenDelim ( Delimiter :: Parenthesis ) => Some ( self . token . span ) ,
2599
- _ => None ,
2592
+ fn parse_for_head ( & mut self ) -> PResult < ' a , ( P < Pat > , P < Expr > ) > {
2593
+ let pat = if self . token . kind == token:: OpenDelim ( Delimiter :: Parenthesis ) {
2594
+ // Record whether we are about to parse `for (`.
2595
+ // This is used below for recovery in case of `for ( $stuff ) $block`
2596
+ // in which case we will suggest `for $stuff $block`.
2597
+ let start_span = self . token . span ;
2598
+ let left = self . prev_token . span . between ( self . look_ahead ( 1 , |t| t. span ) ) ;
2599
+ match self . parse_pat_allow_top_alt (
2600
+ None ,
2601
+ RecoverComma :: Yes ,
2602
+ RecoverColon :: Yes ,
2603
+ CommaRecoveryMode :: LikelyTuple ,
2604
+ ) {
2605
+ Ok ( pat) => pat,
2606
+ Err ( err) if self . eat_keyword ( kw:: In ) => {
2607
+ let expr = match self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL , None ) {
2608
+ Ok ( expr) => expr,
2609
+ Err ( expr_err) => {
2610
+ expr_err. cancel ( ) ;
2611
+ return Err ( err) ;
2612
+ }
2613
+ } ;
2614
+ return if self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis ) {
2615
+ let span = vec ! [ start_span, self . token. span] ;
2616
+ let right = self . prev_token . span . between ( self . look_ahead ( 1 , |t| t. span ) ) ;
2617
+ self . bump ( ) ; // )
2618
+ err. cancel ( ) ;
2619
+ self . sess . emit_err ( errors:: ParenthesesInForHead {
2620
+ span,
2621
+ // With e.g. `for (x) in y)` this would replace `(x) in y)`
2622
+ // with `x) in y)` which is syntactically invalid.
2623
+ // However, this is prevented before we get here.
2624
+ sugg : errors:: ParenthesesInForHeadSugg { left, right } ,
2625
+ } ) ;
2626
+ Ok ( ( self . mk_pat ( start_span. to ( right) , ast:: PatKind :: Wild ) , expr) )
2627
+ } else {
2628
+ Err ( err)
2629
+ } ;
2630
+ }
2631
+ Err ( err) => return Err ( err) ,
2632
+ }
2633
+ } else {
2634
+ self . parse_pat_allow_top_alt (
2635
+ None ,
2636
+ RecoverComma :: Yes ,
2637
+ RecoverColon :: Yes ,
2638
+ CommaRecoveryMode :: LikelyTuple ,
2639
+ ) ?
2600
2640
} ;
2601
-
2602
- let pat = self . parse_pat_allow_top_alt (
2603
- None ,
2604
- RecoverComma :: Yes ,
2605
- RecoverColon :: Yes ,
2606
- CommaRecoveryMode :: LikelyTuple ,
2607
- ) ?;
2608
2641
if !self . eat_keyword ( kw:: In ) {
2609
2642
self . error_missing_in_for_loop ( ) ;
2610
2643
}
2611
2644
self . check_for_for_in_in_typo ( self . prev_token . span ) ;
2612
2645
let expr = self . parse_expr_res ( Restrictions :: NO_STRUCT_LITERAL , None ) ?;
2646
+ Ok ( ( pat, expr) )
2647
+ }
2613
2648
2614
- let pat = self . recover_parens_around_for_head ( pat, begin_paren) ;
2615
-
2649
+ /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2650
+ fn parse_expr_for ( & mut self , opt_label : Option < Label > , lo : Span ) -> PResult < ' a , P < Expr > > {
2651
+ let ( pat, expr) = self . parse_for_head ( ) ?;
2616
2652
// Recover from missing expression in `for` loop
2617
2653
if matches ! ( expr. kind, ExprKind :: Block ( ..) )
2618
2654
&& !matches ! ( self . token. kind, token:: OpenDelim ( Delimiter :: Brace ) )
@@ -2833,47 +2869,10 @@ impl<'a> Parser<'a> {
2833
2869
}
2834
2870
2835
2871
pub ( super ) fn parse_arm ( & mut self ) -> PResult < ' a , Arm > {
2836
- // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
2837
- // `&&` tokens.
2838
- fn check_let_expr ( expr : & Expr ) -> ( bool , bool ) {
2839
- match & expr. kind {
2840
- ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
2841
- let lhs_rslt = check_let_expr ( lhs) ;
2842
- let rhs_rslt = check_let_expr ( rhs) ;
2843
- ( lhs_rslt. 0 || rhs_rslt. 0 , false )
2844
- }
2845
- ExprKind :: Let ( ..) => ( true , true ) ,
2846
- _ => ( false , true ) ,
2847
- }
2848
- }
2849
2872
let attrs = self . parse_outer_attributes ( ) ?;
2850
2873
self . collect_tokens_trailing_token ( attrs, ForceCollect :: No , |this, attrs| {
2851
2874
let lo = this. token . span ;
2852
- let pat = this. parse_pat_allow_top_alt (
2853
- None ,
2854
- RecoverComma :: Yes ,
2855
- RecoverColon :: Yes ,
2856
- CommaRecoveryMode :: EitherTupleOrPipe ,
2857
- ) ?;
2858
- let guard = if this. eat_keyword ( kw:: If ) {
2859
- let if_span = this. prev_token . span ;
2860
- let mut cond = this. parse_match_guard_condition ( ) ?;
2861
-
2862
- CondChecker { parser : this, forbid_let_reason : None } . visit_expr ( & mut cond) ;
2863
-
2864
- let ( has_let_expr, does_not_have_bin_op) = check_let_expr ( & cond) ;
2865
- if has_let_expr {
2866
- if does_not_have_bin_op {
2867
- // Remove the last feature gating of a `let` expression since it's stable.
2868
- this. sess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
2869
- }
2870
- let span = if_span. to ( cond. span ) ;
2871
- this. sess . gated_spans . gate ( sym:: if_let_guard, span) ;
2872
- }
2873
- Some ( cond)
2874
- } else {
2875
- None
2876
- } ;
2875
+ let ( pat, guard) = this. parse_match_arm_pat_and_guard ( ) ?;
2877
2876
let arrow_span = this. token . span ;
2878
2877
if let Err ( mut err) = this. expect ( & token:: FatArrow ) {
2879
2878
// We might have a `=>` -> `=` or `->` typo (issue #89396).
@@ -3002,6 +3001,87 @@ impl<'a> Parser<'a> {
3002
3001
} )
3003
3002
}
3004
3003
3004
+ fn parse_match_arm_guard ( & mut self ) -> PResult < ' a , Option < P < Expr > > > {
3005
+ // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
3006
+ // `&&` tokens.
3007
+ fn check_let_expr ( expr : & Expr ) -> ( bool , bool ) {
3008
+ match & expr. kind {
3009
+ ExprKind :: Binary ( BinOp { node : BinOpKind :: And , .. } , lhs, rhs) => {
3010
+ let lhs_rslt = check_let_expr ( lhs) ;
3011
+ let rhs_rslt = check_let_expr ( rhs) ;
3012
+ ( lhs_rslt. 0 || rhs_rslt. 0 , false )
3013
+ }
3014
+ ExprKind :: Let ( ..) => ( true , true ) ,
3015
+ _ => ( false , true ) ,
3016
+ }
3017
+ }
3018
+ if !self . eat_keyword ( kw:: If ) {
3019
+ // No match arm guard present.
3020
+ return Ok ( None ) ;
3021
+ }
3022
+
3023
+ let if_span = self . prev_token . span ;
3024
+ let mut cond = self . parse_match_guard_condition ( ) ?;
3025
+
3026
+ CondChecker { parser : self , forbid_let_reason : None } . visit_expr ( & mut cond) ;
3027
+
3028
+ let ( has_let_expr, does_not_have_bin_op) = check_let_expr ( & cond) ;
3029
+ if has_let_expr {
3030
+ if does_not_have_bin_op {
3031
+ // Remove the last feature gating of a `let` expression since it's stable.
3032
+ self . sess . gated_spans . ungate_last ( sym:: let_chains, cond. span ) ;
3033
+ }
3034
+ let span = if_span. to ( cond. span ) ;
3035
+ self . sess . gated_spans . gate ( sym:: if_let_guard, span) ;
3036
+ }
3037
+ Ok ( Some ( cond) )
3038
+ }
3039
+
3040
+ fn parse_match_arm_pat_and_guard ( & mut self ) -> PResult < ' a , ( P < Pat > , Option < P < Expr > > ) > {
3041
+ if self . token . kind == token:: OpenDelim ( Delimiter :: Parenthesis ) {
3042
+ // Detect and recover from `($pat if $cond) => $arm`.
3043
+ let left = self . token . span ;
3044
+ match self . parse_pat_allow_top_alt (
3045
+ None ,
3046
+ RecoverComma :: Yes ,
3047
+ RecoverColon :: Yes ,
3048
+ CommaRecoveryMode :: EitherTupleOrPipe ,
3049
+ ) {
3050
+ Ok ( pat) => Ok ( ( pat, self . parse_match_arm_guard ( ) ?) ) ,
3051
+ Err ( err) if let prev_sp = self . prev_token . span && let true = self . eat_keyword ( kw:: If ) => {
3052
+ // We know for certain we've found `($pat if` so far.
3053
+ let mut cond = match self . parse_match_guard_condition ( ) {
3054
+ Ok ( cond) => cond,
3055
+ Err ( cond_err) => {
3056
+ cond_err. cancel ( ) ;
3057
+ return Err ( err) ;
3058
+ }
3059
+ } ;
3060
+ err. cancel ( ) ;
3061
+ CondChecker { parser : self , forbid_let_reason : None } . visit_expr ( & mut cond) ;
3062
+ self . eat_to_tokens ( & [ & token:: CloseDelim ( Delimiter :: Parenthesis ) ] ) ;
3063
+ self . expect ( & token:: CloseDelim ( Delimiter :: Parenthesis ) ) ?;
3064
+ let right = self . prev_token . span ;
3065
+ self . sess . emit_err ( errors:: ParenthesesInMatchPat {
3066
+ span : vec ! [ left, right] ,
3067
+ sugg : errors:: ParenthesesInMatchPatSugg { left, right } ,
3068
+ } ) ;
3069
+ Ok ( ( self . mk_pat ( left. to ( prev_sp) , ast:: PatKind :: Wild ) , Some ( cond) ) )
3070
+ }
3071
+ Err ( err) => Err ( err) ,
3072
+ }
3073
+ } else {
3074
+ // Regular parser flow:
3075
+ let pat = self . parse_pat_allow_top_alt (
3076
+ None ,
3077
+ RecoverComma :: Yes ,
3078
+ RecoverColon :: Yes ,
3079
+ CommaRecoveryMode :: EitherTupleOrPipe ,
3080
+ ) ?;
3081
+ Ok ( ( pat, self . parse_match_arm_guard ( ) ?) )
3082
+ }
3083
+ }
3084
+
3005
3085
fn parse_match_guard_condition ( & mut self ) -> PResult < ' a , P < Expr > > {
3006
3086
self . parse_expr_res ( Restrictions :: ALLOW_LET | Restrictions :: IN_IF_GUARD , None ) . map_err (
3007
3087
|mut err| {
0 commit comments