@@ -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,73 +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") ;
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) ;
37523842 } else {
3753- 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 ( ) ;
37543848 }
37553849 return Err ( err) ;
37563850 }
3757- etc = true ;
3758- break ;
37593851 }
37603852
3761- // Check if a colon exists one ahead. This means we're parsing a fieldname.
3762- let ( subpat, fieldname, is_shorthand) = if self . look_ahead ( 1 , |t| t == & token:: Colon ) {
3763- // Parsing a pattern of the form "fieldname: pat"
3764- let fieldname = self . parse_field_name ( ) ?;
3765- self . bump ( ) ;
3766- let pat = self . parse_pat ( ) ?;
3767- hi = pat. span ;
3768- ( pat, fieldname, false )
3769- } else {
3770- // Parsing a pattern of the form "(box) (ref) (mut) fieldname"
3771- let is_box = self . eat_keyword ( keywords:: Box ) ;
3772- let boxed_span = self . span ;
3773- let is_ref = self . eat_keyword ( keywords:: Ref ) ;
3774- let is_mut = self . eat_keyword ( keywords:: Mut ) ;
3775- let fieldname = self . parse_ident ( ) ?;
3776- hi = self . prev_span ;
3777-
3778- let bind_type = match ( is_ref, is_mut) {
3779- ( true , true ) => BindingMode :: ByRef ( Mutability :: Mutable ) ,
3780- ( true , false ) => BindingMode :: ByRef ( Mutability :: Immutable ) ,
3781- ( false , true ) => BindingMode :: ByValue ( Mutability :: Mutable ) ,
3782- ( false , false ) => BindingMode :: ByValue ( Mutability :: Immutable ) ,
3783- } ;
3784- let fieldpat = P ( Pat {
3785- id : ast:: DUMMY_NODE_ID ,
3786- node : PatKind :: Ident ( bind_type, fieldname, None ) ,
3787- span : boxed_span. to ( hi) ,
3788- } ) ;
3789-
3790- let subpat = if is_box {
3791- P ( Pat {
3792- id : ast:: DUMMY_NODE_ID ,
3793- node : PatKind :: Box ( fieldpat) ,
3794- span : lo. to ( hi) ,
3795- } )
3796- } else {
3797- fieldpat
3798- } ;
3799- ( subpat, fieldname, true )
3800- } ;
3801-
3802- fields. push ( codemap:: Spanned { span : lo. to ( hi) ,
3803- node : ast:: FieldPat {
3804- ident : fieldname,
3805- pat : subpat,
3806- is_shorthand,
3807- attrs : attrs. into ( ) ,
3808- }
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+ }
38093861 } ) ;
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, format!( "{}.. }}" , if ate_comma { "" } else { ", " } ) ) ,
3872+ ] ,
3873+ ) ;
3874+ }
3875+ err. emit ( ) ;
38103876 }
38113877 return Ok ( ( fields, etc) ) ;
38123878 }
0 commit comments