@@ -3719,26 +3719,89 @@ impl<'a> Parser<'a> {
37193719 Ok ( ( before, slice, after) )
37203720 }
37213721
3722+ fn parse_pat_field (
3723+ & mut self ,
3724+ lo : Span ,
3725+ attrs : Vec < Attribute >
3726+ ) -> PResult < ' a , codemap:: Spanned < ast:: FieldPat > > {
3727+ // Check if a colon exists one ahead. This means we're parsing a fieldname.
3728+ let hi;
3729+ let ( subpat, fieldname, is_shorthand) = if self . look_ahead ( 1 , |t| t == & token:: Colon ) {
3730+ // Parsing a pattern of the form "fieldname: pat"
3731+ let fieldname = self . parse_field_name ( ) ?;
3732+ self . bump ( ) ;
3733+ let pat = self . parse_pat ( ) ?;
3734+ hi = pat. span ;
3735+ ( pat, fieldname, false )
3736+ } else {
3737+ // Parsing a pattern of the form "(box) (ref) (mut) fieldname"
3738+ let is_box = self . eat_keyword ( keywords:: Box ) ;
3739+ let boxed_span = self . span ;
3740+ let is_ref = self . eat_keyword ( keywords:: Ref ) ;
3741+ let is_mut = self . eat_keyword ( keywords:: Mut ) ;
3742+ let fieldname = self . parse_ident ( ) ?;
3743+ hi = self . prev_span ;
3744+
3745+ let bind_type = match ( is_ref, is_mut) {
3746+ ( true , true ) => BindingMode :: ByRef ( Mutability :: Mutable ) ,
3747+ ( true , false ) => BindingMode :: ByRef ( Mutability :: Immutable ) ,
3748+ ( false , true ) => BindingMode :: ByValue ( Mutability :: Mutable ) ,
3749+ ( false , false ) => BindingMode :: ByValue ( Mutability :: Immutable ) ,
3750+ } ;
3751+ let fieldpat = P ( Pat {
3752+ id : ast:: DUMMY_NODE_ID ,
3753+ node : PatKind :: Ident ( bind_type, fieldname, None ) ,
3754+ span : boxed_span. to ( hi) ,
3755+ } ) ;
3756+
3757+ let subpat = if is_box {
3758+ P ( Pat {
3759+ id : ast:: DUMMY_NODE_ID ,
3760+ node : PatKind :: Box ( fieldpat) ,
3761+ span : lo. to ( hi) ,
3762+ } )
3763+ } else {
3764+ fieldpat
3765+ } ;
3766+ ( subpat, fieldname, true )
3767+ } ;
3768+
3769+ Ok ( codemap:: Spanned {
3770+ span : lo. to ( hi) ,
3771+ node : ast:: FieldPat {
3772+ ident : fieldname,
3773+ pat : subpat,
3774+ is_shorthand,
3775+ attrs : attrs. into ( ) ,
3776+ }
3777+ } )
3778+ }
3779+
37223780 /// Parse the fields of a struct-like pattern
37233781 fn parse_pat_fields ( & mut self ) -> PResult < ' a , ( Vec < codemap:: Spanned < ast:: FieldPat > > , bool ) > {
37243782 let mut fields = Vec :: new ( ) ;
37253783 let mut etc = false ;
3726- let mut first = true ;
3727- while self . token != token:: CloseDelim ( token:: Brace ) {
3728- if first {
3729- first = false ;
3730- } else {
3731- self . expect ( & token:: Comma ) ?;
3732- // accept trailing commas
3733- if self . check ( & token:: CloseDelim ( token:: Brace ) ) { break }
3734- }
3784+ let mut ate_comma = true ;
3785+ let mut delayed_err: Option < DiagnosticBuilder < ' a > > = None ;
3786+ let mut etc_span = None ;
37353787
3788+ while self . token != token:: CloseDelim ( token:: Brace ) {
37363789 let attrs = self . parse_outer_attributes ( ) ?;
37373790 let lo = self . span ;
3738- let hi;
3791+
3792+ // check that a comma comes after every field
3793+ if !ate_comma {
3794+ let err = self . struct_span_err ( self . prev_span , "expected `,`" ) ;
3795+ return Err ( err) ;
3796+ }
3797+ ate_comma = false ;
37393798
37403799 if self . check ( & token:: DotDot ) || self . token == token:: DotDotDot {
3800+ etc = true ;
3801+ let mut etc_sp = self . span ;
3802+
37413803 if self . token == token:: DotDotDot { // Issue #46718
3804+ // Accept `...` as if it were `..` to avoid further errors
37423805 let mut err = self . struct_span_err ( self . span ,
37433806 "expected field pattern, found `...`" ) ;
37443807 err. span_suggestion_with_applicability (
@@ -3749,73 +3812,76 @@ impl<'a> Parser<'a> {
37493812 ) ;
37503813 err. emit ( ) ;
37513814 }
3815+ self . bump ( ) ; // `..` || `...`:w
37523816
3753- self . bump ( ) ;
3754- if self . token != token:: CloseDelim ( token:: Brace ) {
3755- let token_str = self . this_token_to_string ( ) ;
3756- let mut err = self . fatal ( & format ! ( "expected `{}`, found `{}`" , "}" , token_str) ) ;
3757- if self . token == token:: Comma { // Issue #49257
3758- err. span_label ( self . span ,
3759- "`..` must be in the last position, \
3760- and cannot have a trailing comma") ;
3817+ if self . token == token:: CloseDelim ( token:: Brace ) {
3818+ etc_span = Some ( etc_sp) ;
3819+ break ;
3820+ }
3821+ let token_str = self . this_token_to_string ( ) ;
3822+ let mut err = self . fatal ( & format ! ( "expected `}}`, found `{}`" , token_str) ) ;
3823+
3824+ err. span_label ( self . span , "expected `}`" ) ;
3825+ let mut comma_sp = None ;
3826+ if self . token == token:: Comma { // Issue #49257
3827+ etc_sp = etc_sp. to ( self . sess . codemap ( ) . span_until_non_whitespace ( self . span ) ) ;
3828+ err. span_label ( etc_sp,
3829+ "`..` must be at the end and cannot have a trailing comma" ) ;
3830+ comma_sp = Some ( self . span ) ;
3831+ self . bump ( ) ;
3832+ ate_comma = true ;
3833+ }
3834+
3835+ etc_span = Some ( etc_sp) ;
3836+ if self . token == token:: CloseDelim ( token:: Brace ) {
3837+ // If the struct looks otherwise well formed, recover and continue.
3838+ if let Some ( sp) = comma_sp {
3839+ err. span_suggestion_short ( sp, "remove this comma" , "" . into ( ) ) ;
3840+ }
3841+ err. emit ( ) ;
3842+ break ;
3843+ } else if self . token . is_ident ( ) && ate_comma {
3844+ // Accept fields coming after `..,`.
3845+ // This way we avoid "pattern missing fields" errors afterwards.
3846+ // We delay this error until the end in order to have a span for a
3847+ // suggested fix.
3848+ if let Some ( mut delayed_err) = delayed_err {
3849+ delayed_err. emit ( ) ;
3850+ return Err ( err) ;
37613851 } else {
3762- err. span_label ( self . span , "expected `}`" ) ;
3852+ delayed_err = Some ( err) ;
3853+ }
3854+ } else {
3855+ if let Some ( mut err) = delayed_err {
3856+ err. emit ( ) ;
37633857 }
37643858 return Err ( err) ;
37653859 }
3766- etc = true ;
3767- break ;
37683860 }
37693861
3770- // Check if a colon exists one ahead. This means we're parsing a fieldname.
3771- let ( subpat, fieldname, is_shorthand) = if self . look_ahead ( 1 , |t| t == & token:: Colon ) {
3772- // Parsing a pattern of the form "fieldname: pat"
3773- let fieldname = self . parse_field_name ( ) ?;
3774- self . bump ( ) ;
3775- let pat = self . parse_pat ( ) ?;
3776- hi = pat. span ;
3777- ( pat, fieldname, false )
3778- } else {
3779- // Parsing a pattern of the form "(box) (ref) (mut) fieldname"
3780- let is_box = self . eat_keyword ( keywords:: Box ) ;
3781- let boxed_span = self . span ;
3782- let is_ref = self . eat_keyword ( keywords:: Ref ) ;
3783- let is_mut = self . eat_keyword ( keywords:: Mut ) ;
3784- let fieldname = self . parse_ident ( ) ?;
3785- hi = self . prev_span ;
3786-
3787- let bind_type = match ( is_ref, is_mut) {
3788- ( true , true ) => BindingMode :: ByRef ( Mutability :: Mutable ) ,
3789- ( true , false ) => BindingMode :: ByRef ( Mutability :: Immutable ) ,
3790- ( false , true ) => BindingMode :: ByValue ( Mutability :: Mutable ) ,
3791- ( false , false ) => BindingMode :: ByValue ( Mutability :: Immutable ) ,
3792- } ;
3793- let fieldpat = P ( Pat {
3794- id : ast:: DUMMY_NODE_ID ,
3795- node : PatKind :: Ident ( bind_type, fieldname, None ) ,
3796- span : boxed_span. to ( hi) ,
3797- } ) ;
3798-
3799- let subpat = if is_box {
3800- P ( Pat {
3801- id : ast:: DUMMY_NODE_ID ,
3802- node : PatKind :: Box ( fieldpat) ,
3803- span : lo. to ( hi) ,
3804- } )
3805- } else {
3806- fieldpat
3807- } ;
3808- ( subpat, fieldname, true )
3809- } ;
3810-
3811- fields. push ( codemap:: Spanned { span : lo. to ( hi) ,
3812- node : ast:: FieldPat {
3813- ident : fieldname,
3814- pat : subpat,
3815- is_shorthand,
3816- attrs : attrs. into ( ) ,
3817- }
3862+ fields. push ( match self . parse_pat_field ( lo, attrs) {
3863+ Ok ( field) => field,
3864+ Err ( err) => {
3865+ if let Some ( mut delayed_err) = delayed_err {
3866+ delayed_err. emit ( ) ;
3867+ }
3868+ return Err ( err) ;
3869+ }
38183870 } ) ;
3871+ ate_comma = self . eat ( & token:: Comma ) ;
3872+ }
3873+
3874+ if let Some ( mut err) = delayed_err {
3875+ if let Some ( etc_span) = etc_span {
3876+ err. multipart_suggestion (
3877+ "move the `..` to the end of the field list" ,
3878+ vec ! [
3879+ ( etc_span, "" . into( ) ) ,
3880+ ( self . span, format!( "{}.. }}" , if ate_comma { "" } else { ", " } ) ) ,
3881+ ] ,
3882+ ) ;
3883+ }
3884+ err. emit ( ) ;
38193885 }
38203886 return Ok ( ( fields, etc) ) ;
38213887 }
0 commit comments