@@ -3710,26 +3710,89 @@ impl<'a> Parser<'a> {
3710
3710
Ok ( ( before, slice, after) )
3711
3711
}
3712
3712
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
+
3713
3771
/// Parse the fields of a struct-like pattern
3714
3772
fn parse_pat_fields ( & mut self ) -> PResult < ' a , ( Vec < codemap:: Spanned < ast:: FieldPat > > , bool ) > {
3715
3773
let mut fields = Vec :: new ( ) ;
3716
3774
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 ;
3726
3778
3779
+ while self . token != token:: CloseDelim ( token:: Brace ) {
3727
3780
let attrs = self . parse_outer_attributes ( ) ?;
3728
3781
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 ;
3730
3789
3731
3790
if self . check ( & token:: DotDot ) || self . token == token:: DotDotDot {
3791
+ etc = true ;
3792
+ let mut etc_sp = self . span ;
3793
+
3732
3794
if self . token == token:: DotDotDot { // Issue #46718
3795
+ // Accept `...` as if it were `..` to avoid further errors
3733
3796
let mut err = self . struct_span_err ( self . span ,
3734
3797
"expected field pattern, found `...`" ) ;
3735
3798
err. span_suggestion_with_applicability (
@@ -3740,73 +3803,76 @@ impl<'a> Parser<'a> {
3740
3803
) ;
3741
3804
err. emit ( ) ;
3742
3805
}
3806
+ self . bump ( ) ; // `..` || `...`:w
3743
3807
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) ;
3752
3842
} 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 ( ) ;
3754
3848
}
3755
3849
return Err ( err) ;
3756
3850
}
3757
- etc = true ;
3758
- break ;
3759
3851
}
3760
3852
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
+ }
3809
3861
} ) ;
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 ( ) ;
3810
3876
}
3811
3877
return Ok ( ( fields, etc) ) ;
3812
3878
}
0 commit comments