@@ -2834,7 +2834,7 @@ impl<'a> Parser<'a> {
2834
2834
) ?;
2835
2835
let guard = if this. eat_keyword ( kw:: If ) {
2836
2836
let if_span = this. prev_token . span ;
2837
- let mut cond = this. parse_expr_res ( Restrictions :: ALLOW_LET , None ) ?;
2837
+ let mut cond = this. parse_match_guard_condition ( ) ?;
2838
2838
2839
2839
CondChecker { parser : this, forbid_let_reason : None } . visit_expr ( & mut cond) ;
2840
2840
@@ -2860,9 +2860,9 @@ impl<'a> Parser<'a> {
2860
2860
{
2861
2861
err. span_suggestion (
2862
2862
this. token . span ,
2863
- "try using a fat arrow here " ,
2863
+ "use a fat arrow to start a match arm " ,
2864
2864
"=>" ,
2865
- Applicability :: MaybeIncorrect ,
2865
+ Applicability :: MachineApplicable ,
2866
2866
) ;
2867
2867
err. emit ( ) ;
2868
2868
this. bump ( ) ;
@@ -2979,6 +2979,33 @@ impl<'a> Parser<'a> {
2979
2979
} )
2980
2980
}
2981
2981
2982
+ fn parse_match_guard_condition ( & mut self ) -> PResult < ' a , P < Expr > > {
2983
+ self . parse_expr_res ( Restrictions :: ALLOW_LET | Restrictions :: IN_IF_GUARD , None ) . map_err (
2984
+ |mut err| {
2985
+ if self . prev_token == token:: OpenDelim ( Delimiter :: Brace ) {
2986
+ let sugg_sp = self . prev_token . span . shrink_to_lo ( ) ;
2987
+ // Consume everything within the braces, let's avoid further parse
2988
+ // errors.
2989
+ self . recover_stmt_ ( SemiColonMode :: Ignore , BlockMode :: Ignore ) ;
2990
+ let msg = "you might have meant to start a match arm after the match guard" ;
2991
+ if self . eat ( & token:: CloseDelim ( Delimiter :: Brace ) ) {
2992
+ let applicability = if self . token . kind != token:: FatArrow {
2993
+ // We have high confidence that we indeed didn't have a struct
2994
+ // literal in the match guard, but rather we had some operation
2995
+ // that ended in a path, immediately followed by a block that was
2996
+ // meant to be the match arm.
2997
+ Applicability :: MachineApplicable
2998
+ } else {
2999
+ Applicability :: MaybeIncorrect
3000
+ } ;
3001
+ err. span_suggestion_verbose ( sugg_sp, msg, "=> " . to_string ( ) , applicability) ;
3002
+ }
3003
+ }
3004
+ err
3005
+ } ,
3006
+ )
3007
+ }
3008
+
2982
3009
pub ( crate ) fn is_builtin ( & self ) -> bool {
2983
3010
self . token . is_keyword ( kw:: Builtin ) && self . look_ahead ( 1 , |t| * t == token:: Pound )
2984
3011
}
@@ -3049,9 +3076,10 @@ impl<'a> Parser<'a> {
3049
3076
|| self . look_ahead ( 2 , |t| t == & token:: Colon )
3050
3077
&& (
3051
3078
// `{ ident: token, ` cannot start a block.
3052
- self . look_ahead ( 4 , |t| t == & token:: Comma ) ||
3053
- // `{ ident: ` cannot start a block unless it's a type ascription `ident: Type`.
3054
- self . look_ahead ( 3 , |t| !t. can_begin_type ( ) )
3079
+ self . look_ahead ( 4 , |t| t == & token:: Comma )
3080
+ // `{ ident: ` cannot start a block unless it's a type ascription
3081
+ // `ident: Type`.
3082
+ || self . look_ahead ( 3 , |t| !t. can_begin_type ( ) )
3055
3083
)
3056
3084
)
3057
3085
}
@@ -3091,6 +3119,7 @@ impl<'a> Parser<'a> {
3091
3119
let mut fields = ThinVec :: new ( ) ;
3092
3120
let mut base = ast:: StructRest :: None ;
3093
3121
let mut recover_async = false ;
3122
+ let in_if_guard = self . restrictions . contains ( Restrictions :: IN_IF_GUARD ) ;
3094
3123
3095
3124
let mut async_block_err = |e : & mut Diagnostic , span : Span | {
3096
3125
recover_async = true ;
@@ -3128,6 +3157,26 @@ impl<'a> Parser<'a> {
3128
3157
e. span_label ( pth. span , "while parsing this struct" ) ;
3129
3158
}
3130
3159
3160
+ if let Some ( ( ident, _) ) = self . token . ident ( )
3161
+ && !self . token . is_reserved_ident ( )
3162
+ && self . look_ahead ( 1 , |t| {
3163
+ AssocOp :: from_token ( & t) . is_some ( )
3164
+ || matches ! ( t. kind, token:: OpenDelim ( _) )
3165
+ || t. kind == token:: Dot
3166
+ } )
3167
+ {
3168
+ // Looks like they tried to write a shorthand, complex expression.
3169
+ e. span_suggestion_verbose (
3170
+ self . token . span . shrink_to_lo ( ) ,
3171
+ "try naming a field" ,
3172
+ & format ! ( "{ident}: " , ) ,
3173
+ Applicability :: MaybeIncorrect ,
3174
+ ) ;
3175
+ }
3176
+ if in_if_guard && close_delim == Delimiter :: Brace {
3177
+ return Err ( e) ;
3178
+ }
3179
+
3131
3180
if !recover {
3132
3181
return Err ( e) ;
3133
3182
}
@@ -3173,19 +3222,6 @@ impl<'a> Parser<'a> {
3173
3222
"," ,
3174
3223
Applicability :: MachineApplicable ,
3175
3224
) ;
3176
- } else if is_shorthand
3177
- && ( AssocOp :: from_token ( & self . token ) . is_some ( )
3178
- || matches ! ( & self . token. kind, token:: OpenDelim ( _) )
3179
- || self . token . kind == token:: Dot )
3180
- {
3181
- // Looks like they tried to write a shorthand, complex expression.
3182
- let ident = parsed_field. expect ( "is_shorthand implies Some" ) . ident ;
3183
- e. span_suggestion (
3184
- ident. span . shrink_to_lo ( ) ,
3185
- "try naming a field" ,
3186
- & format ! ( "{ident}: " ) ,
3187
- Applicability :: HasPlaceholders ,
3188
- ) ;
3189
3225
}
3190
3226
}
3191
3227
if !recover {
@@ -3288,6 +3324,24 @@ impl<'a> Parser<'a> {
3288
3324
3289
3325
// Check if a colon exists one ahead. This means we're parsing a fieldname.
3290
3326
let is_shorthand = !this. look_ahead ( 1 , |t| t == & token:: Colon || t == & token:: Eq ) ;
3327
+ // Proactively check whether parsing the field will be incorrect.
3328
+ let is_wrong = this. token . is_ident ( )
3329
+ && !this. token . is_reserved_ident ( )
3330
+ && !this. look_ahead ( 1 , |t| {
3331
+ t == & token:: Colon
3332
+ || t == & token:: Eq
3333
+ || t == & token:: Comma
3334
+ || t == & token:: CloseDelim ( Delimiter :: Brace )
3335
+ || t == & token:: CloseDelim ( Delimiter :: Parenthesis )
3336
+ } ) ;
3337
+ if is_wrong {
3338
+ return Err ( errors:: ExpectedStructField {
3339
+ span : this. look_ahead ( 1 , |t| t. span ) ,
3340
+ ident_span : this. token . span ,
3341
+ token : this. look_ahead ( 1 , |t| t. clone ( ) ) ,
3342
+ }
3343
+ . into_diagnostic ( & self . sess . span_diagnostic ) ) ;
3344
+ }
3291
3345
let ( ident, expr) = if is_shorthand {
3292
3346
// Mimic `x: x` for the `x` field shorthand.
3293
3347
let ident = this. parse_ident_common ( false ) ?;
0 commit comments