@@ -3719,26 +3719,89 @@ impl<'a> Parser<'a> {
3719
3719
Ok ( ( before, slice, after) )
3720
3720
}
3721
3721
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
+
3722
3780
/// Parse the fields of a struct-like pattern
3723
3781
fn parse_pat_fields ( & mut self ) -> PResult < ' a , ( Vec < codemap:: Spanned < ast:: FieldPat > > , bool ) > {
3724
3782
let mut fields = Vec :: new ( ) ;
3725
3783
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 ;
3735
3787
3788
+ while self . token != token:: CloseDelim ( token:: Brace ) {
3736
3789
let attrs = self . parse_outer_attributes ( ) ?;
3737
3790
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 ;
3739
3798
3740
3799
if self . check ( & token:: DotDot ) || self . token == token:: DotDotDot {
3800
+ etc = true ;
3801
+ let mut etc_sp = self . span ;
3802
+
3741
3803
if self . token == token:: DotDotDot { // Issue #46718
3804
+ // Accept `...` as if it were `..` to avoid further errors
3742
3805
let mut err = self . struct_span_err ( self . span ,
3743
3806
"expected field pattern, found `...`" ) ;
3744
3807
err. span_suggestion_with_applicability (
@@ -3749,73 +3812,76 @@ impl<'a> Parser<'a> {
3749
3812
) ;
3750
3813
err. emit ( ) ;
3751
3814
}
3815
+ self . bump ( ) ; // `..` || `...`:w
3752
3816
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) ;
3761
3851
} 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 ( ) ;
3763
3857
}
3764
3858
return Err ( err) ;
3765
3859
}
3766
- etc = true ;
3767
- break ;
3768
3860
}
3769
3861
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
+ }
3818
3870
} ) ;
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 ( ) ;
3819
3885
}
3820
3886
return Ok ( ( fields, etc) ) ;
3821
3887
}
0 commit comments