@@ -938,7 +938,8 @@ impl<'a> Parser<'a> {
938
938
let mut etc = false ;
939
939
let mut ate_comma = true ;
940
940
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 ;
942
943
943
944
while self . token != token:: CloseDelim ( Delimiter :: Brace ) {
944
945
let attrs = match self . parse_outer_attributes ( ) {
@@ -969,12 +970,27 @@ impl<'a> Parser<'a> {
969
970
{
970
971
etc = true ;
971
972
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
+ }
972
989
973
990
self . recover_bad_dot_dot ( ) ;
974
991
self . bump ( ) ; // `..` || `...` || `_`
975
992
976
993
if self . token == token:: CloseDelim ( Delimiter :: Brace ) {
977
- etc_span = Some ( etc_sp) ;
978
994
break ;
979
995
}
980
996
let token_str = super :: token_descr ( & self . token ) ;
@@ -996,7 +1012,6 @@ impl<'a> Parser<'a> {
996
1012
ate_comma = true ;
997
1013
}
998
1014
999
- etc_span = Some ( etc_sp. until ( self . token . span ) ) ;
1000
1015
if self . token == token:: CloseDelim ( Delimiter :: Brace ) {
1001
1016
// If the struct looks otherwise well formed, recover and continue.
1002
1017
if let Some ( sp) = comma_sp {
@@ -1040,6 +1055,9 @@ impl<'a> Parser<'a> {
1040
1055
}
1041
1056
} ?;
1042
1057
ate_comma = this. eat ( & token:: Comma ) ;
1058
+
1059
+ last_non_comma_dotdot_span = Some ( this. prev_token . span ) ;
1060
+
1043
1061
// We just ate a comma, so there's no need to use
1044
1062
// `TrailingToken::Comma`
1045
1063
Ok ( ( field, TrailingToken :: None ) )
@@ -1049,15 +1067,30 @@ impl<'a> Parser<'a> {
1049
1067
}
1050
1068
1051
1069
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
+ }
1061
1094
}
1062
1095
err. emit ( ) ;
1063
1096
}
0 commit comments