@@ -938,7 +938,8 @@ impl<'a> Parser<'a> {
938938 let mut etc = false ;
939939 let mut ate_comma = true ;
940940 let mut delayed_err: Option < DiagnosticBuilder < ' a , ErrorGuaranteed > > = None ;
941- let mut etc_span = None ;
941+ let mut first_etc_and_maybe_comma_span = None ;
942+ let mut last_non_comma_dotdot_span = None ;
942943
943944 while self . token != token:: CloseDelim ( Delimiter :: Brace ) {
944945 let attrs = match self . parse_outer_attributes ( ) {
@@ -969,12 +970,27 @@ impl<'a> Parser<'a> {
969970 {
970971 etc = true ;
971972 let mut etc_sp = self . token . span ;
973+ if first_etc_and_maybe_comma_span. is_none ( ) {
974+ if let Some ( comma_tok) = self
975+ . look_ahead ( 1 , |t| if * t == token:: Comma { Some ( t. clone ( ) ) } else { None } )
976+ {
977+ let nw_span = self
978+ . sess
979+ . source_map ( )
980+ . span_extend_to_line ( comma_tok. span )
981+ . trim_start ( comma_tok. span . shrink_to_lo ( ) )
982+ . map ( |s| self . sess . source_map ( ) . span_until_non_whitespace ( s) ) ;
983+ first_etc_and_maybe_comma_span = nw_span. map ( |s| etc_sp. to ( s) ) ;
984+ } else {
985+ first_etc_and_maybe_comma_span =
986+ Some ( self . sess . source_map ( ) . span_until_non_whitespace ( etc_sp) ) ;
987+ }
988+ }
972989
973990 self . recover_bad_dot_dot ( ) ;
974991 self . bump ( ) ; // `..` || `...` || `_`
975992
976993 if self . token == token:: CloseDelim ( Delimiter :: Brace ) {
977- etc_span = Some ( etc_sp) ;
978994 break ;
979995 }
980996 let token_str = super :: token_descr ( & self . token ) ;
@@ -996,7 +1012,6 @@ impl<'a> Parser<'a> {
9961012 ate_comma = true ;
9971013 }
9981014
999- etc_span = Some ( etc_sp. until ( self . token . span ) ) ;
10001015 if self . token == token:: CloseDelim ( Delimiter :: Brace ) {
10011016 // If the struct looks otherwise well formed, recover and continue.
10021017 if let Some ( sp) = comma_sp {
@@ -1040,6 +1055,9 @@ impl<'a> Parser<'a> {
10401055 }
10411056 } ?;
10421057 ate_comma = this. eat ( & token:: Comma ) ;
1058+
1059+ last_non_comma_dotdot_span = Some ( this. prev_token . span ) ;
1060+
10431061 // We just ate a comma, so there's no need to use
10441062 // `TrailingToken::Comma`
10451063 Ok ( ( field, TrailingToken :: None ) )
@@ -1049,15 +1067,30 @@ impl<'a> Parser<'a> {
10491067 }
10501068
10511069 if let Some ( mut err) = delayed_err {
1052- if let Some ( etc_span) = etc_span {
1053- err. multipart_suggestion (
1054- "move the `..` to the end of the field list" ,
1055- vec ! [
1056- ( etc_span, String :: new( ) ) ,
1057- ( self . token. span, format!( "{}.. }}" , if ate_comma { "" } else { ", " } ) ) ,
1058- ] ,
1059- Applicability :: MachineApplicable ,
1060- ) ;
1070+ if let Some ( first_etc_span) = first_etc_and_maybe_comma_span {
1071+ if self . prev_token == token:: DotDot {
1072+ // We have `.., x, ..`.
1073+ err. multipart_suggestion (
1074+ "remove the starting `..`" ,
1075+ vec ! [ ( first_etc_span, String :: new( ) ) ] ,
1076+ Applicability :: MachineApplicable ,
1077+ ) ;
1078+ } else {
1079+ if let Some ( last_non_comma_dotdot_span) = last_non_comma_dotdot_span {
1080+ // We have `.., x`.
1081+ err. multipart_suggestion (
1082+ "move the `..` to the end of the field list" ,
1083+ vec ! [
1084+ ( first_etc_span, String :: new( ) ) ,
1085+ (
1086+ self . token. span. to( last_non_comma_dotdot_span. shrink_to_hi( ) ) ,
1087+ format!( "{} .. }}" , if ate_comma { "" } else { "," } ) ,
1088+ ) ,
1089+ ] ,
1090+ Applicability :: MachineApplicable ,
1091+ ) ;
1092+ }
1093+ }
10611094 }
10621095 err. emit ( ) ;
10631096 }
0 commit comments