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