@@ -2834,7 +2834,7 @@ impl<'a> Parser<'a> {
28342834 ) ?;
28352835 let guard = if this. eat_keyword ( kw:: If ) {
28362836 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 ( ) ?;
28382838
28392839 CondChecker { parser : this, forbid_let_reason : None } . visit_expr ( & mut cond) ;
28402840
@@ -2860,9 +2860,9 @@ impl<'a> Parser<'a> {
28602860 {
28612861 err. span_suggestion (
28622862 this. token . span ,
2863- "try using a fat arrow here " ,
2863+ "use a fat arrow to start a match arm " ,
28642864 "=>" ,
2865- Applicability :: MaybeIncorrect ,
2865+ Applicability :: MachineApplicable ,
28662866 ) ;
28672867 err. emit ( ) ;
28682868 this. bump ( ) ;
@@ -2979,6 +2979,33 @@ impl<'a> Parser<'a> {
29792979 } )
29802980 }
29812981
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+
29823009 pub ( crate ) fn is_builtin ( & self ) -> bool {
29833010 self . token . is_keyword ( kw:: Builtin ) && self . look_ahead ( 1 , |t| * t == token:: Pound )
29843011 }
@@ -3049,9 +3076,10 @@ impl<'a> Parser<'a> {
30493076 || self . look_ahead ( 2 , |t| t == & token:: Colon )
30503077 && (
30513078 // `{ 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 ( ) )
30553083 )
30563084 )
30573085 }
@@ -3091,6 +3119,7 @@ impl<'a> Parser<'a> {
30913119 let mut fields = ThinVec :: new ( ) ;
30923120 let mut base = ast:: StructRest :: None ;
30933121 let mut recover_async = false ;
3122+ let in_if_guard = self . restrictions . contains ( Restrictions :: IN_IF_GUARD ) ;
30943123
30953124 let mut async_block_err = |e : & mut Diagnostic , span : Span | {
30963125 recover_async = true ;
@@ -3128,6 +3157,26 @@ impl<'a> Parser<'a> {
31283157 e. span_label ( pth. span , "while parsing this struct" ) ;
31293158 }
31303159
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+
31313180 if !recover {
31323181 return Err ( e) ;
31333182 }
@@ -3173,19 +3222,6 @@ impl<'a> Parser<'a> {
31733222 "," ,
31743223 Applicability :: MachineApplicable ,
31753224 ) ;
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- ) ;
31893225 }
31903226 }
31913227 if !recover {
@@ -3288,6 +3324,24 @@ impl<'a> Parser<'a> {
32883324
32893325 // Check if a colon exists one ahead. This means we're parsing a fieldname.
32903326 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+ }
32913345 let ( ident, expr) = if is_shorthand {
32923346 // Mimic `x: x` for the `x` field shorthand.
32933347 let ident = this. parse_ident_common ( false ) ?;
0 commit comments