@@ -250,6 +250,11 @@ struct Context<'a, 'b> {
250250 unused_names_lint : PositionalNamedArgsLint ,
251251}
252252
253+ pub struct FormatArg {
254+ expr : P < ast:: Expr > ,
255+ named : bool ,
256+ }
257+
253258/// Parses the arguments from the given list of tokens, returning the diagnostic
254259/// if there's a parse error so we can continue parsing other format!
255260/// expressions.
@@ -263,8 +268,8 @@ fn parse_args<'a>(
263268 ecx : & mut ExtCtxt < ' a > ,
264269 sp : Span ,
265270 tts : TokenStream ,
266- ) -> PResult < ' a , ( P < ast:: Expr > , Vec < P < ast :: Expr > > , FxHashMap < Symbol , ( usize , Span ) > ) > {
267- let mut args = Vec :: < P < ast :: Expr > > :: new ( ) ;
271+ ) -> PResult < ' a , ( P < ast:: Expr > , Vec < FormatArg > , FxHashMap < Symbol , ( usize , Span ) > ) > {
272+ let mut args = Vec :: < FormatArg > :: new ( ) ;
268273 let mut names = FxHashMap :: < Symbol , ( usize , Span ) > :: default ( ) ;
269274
270275 let mut p = ecx. new_parser_from_tts ( tts) ;
@@ -332,7 +337,7 @@ fn parse_args<'a>(
332337 let e = p. parse_expr ( ) ?;
333338 if let Some ( ( prev, _) ) = names. get ( & ident. name ) {
334339 ecx. struct_span_err ( e. span , & format ! ( "duplicate argument named `{}`" , ident) )
335- . span_label ( args[ * prev] . span , "previously here" )
340+ . span_label ( args[ * prev] . expr . span , "previously here" )
336341 . span_label ( e. span , "duplicate argument" )
337342 . emit ( ) ;
338343 continue ;
@@ -344,7 +349,7 @@ fn parse_args<'a>(
344349 // args. And remember the names.
345350 let slot = args. len ( ) ;
346351 names. insert ( ident. name , ( slot, ident. span ) ) ;
347- args. push ( e ) ;
352+ args. push ( FormatArg { expr : e , named : true } ) ;
348353 }
349354 _ => {
350355 let e = p. parse_expr ( ) ?;
@@ -355,11 +360,11 @@ fn parse_args<'a>(
355360 ) ;
356361 err. span_label ( e. span , "positional arguments must be before named arguments" ) ;
357362 for pos in names. values ( ) {
358- err. span_label ( args[ pos. 0 ] . span , "named argument" ) ;
363+ err. span_label ( args[ pos. 0 ] . expr . span , "named argument" ) ;
359364 }
360365 err. emit ( ) ;
361366 }
362- args. push ( e ) ;
367+ args. push ( FormatArg { expr : e , named : false } ) ;
363368 }
364369 }
365370 }
@@ -1180,7 +1185,7 @@ pub fn expand_preparsed_format_args(
11801185 ecx : & mut ExtCtxt < ' _ > ,
11811186 sp : Span ,
11821187 efmt : P < ast:: Expr > ,
1183- args : Vec < P < ast :: Expr > > ,
1188+ args : Vec < FormatArg > ,
11841189 names : FxHashMap < Symbol , ( usize , Span ) > ,
11851190 append_newline : bool ,
11861191) -> P < ast:: Expr > {
@@ -1270,6 +1275,24 @@ pub fn expand_preparsed_format_args(
12701275 e. span_label ( fmt_span. from_inner ( InnerSpan :: new ( span. start , span. end ) ) , label) ;
12711276 }
12721277 }
1278+ if err. should_be_replaced_with_positional_argument {
1279+ let captured_arg_span =
1280+ fmt_span. from_inner ( InnerSpan :: new ( err. span . start , err. span . end ) ) ;
1281+ let positional_args = args. iter ( ) . filter ( |arg| !arg. named ) . collect :: < Vec < _ > > ( ) ;
1282+ let mut suggestions = vec ! [ ( captured_arg_span, positional_args. len( ) . to_string( ) ) ] ;
1283+ if let Ok ( arg) = ecx. source_map ( ) . span_to_snippet ( captured_arg_span) {
1284+ let span = match positional_args. last ( ) {
1285+ Some ( arg) => arg. expr . span ,
1286+ None => fmt_sp,
1287+ } ;
1288+ suggestions. push ( ( span. shrink_to_hi ( ) , format ! ( ", {}" , arg) ) )
1289+ }
1290+ e. multipart_suggestion_verbose (
1291+ "consider using a positional formatting argument instead" ,
1292+ suggestions,
1293+ Applicability :: MachineApplicable ,
1294+ ) ;
1295+ }
12731296 e. emit ( ) ;
12741297 return DummyResult :: raw_expr ( sp, true ) ;
12751298 }
@@ -1284,7 +1307,7 @@ pub fn expand_preparsed_format_args(
12841307
12851308 let mut cx = Context {
12861309 ecx,
1287- args,
1310+ args : args . into_iter ( ) . map ( |arg| arg . expr ) . collect ( ) ,
12881311 num_captured_args : 0 ,
12891312 arg_types,
12901313 arg_unique_types,
0 commit comments