@@ -280,6 +280,11 @@ struct Context<'a, 'b> {
280280 unused_names_lint : PositionalNamedArgsLint ,
281281}
282282
283+ pub struct FormatArg {
284+ expr : P < ast:: Expr > ,
285+ named : bool ,
286+ }
287+
283288/// Parses the arguments from the given list of tokens, returning the diagnostic
284289/// if there's a parse error so we can continue parsing other format!
285290/// expressions.
@@ -293,8 +298,8 @@ fn parse_args<'a>(
293298 ecx : & mut ExtCtxt < ' a > ,
294299 sp : Span ,
295300 tts : TokenStream ,
296- ) -> PResult < ' a , ( P < ast:: Expr > , Vec < P < ast :: Expr > > , FxHashMap < Symbol , ( usize , Span ) > ) > {
297- let mut args = Vec :: < P < ast :: Expr > > :: new ( ) ;
301+ ) -> PResult < ' a , ( P < ast:: Expr > , Vec < FormatArg > , FxHashMap < Symbol , ( usize , Span ) > ) > {
302+ let mut args = Vec :: < FormatArg > :: new ( ) ;
298303 let mut names = FxHashMap :: < Symbol , ( usize , Span ) > :: default ( ) ;
299304
300305 let mut p = ecx. new_parser_from_tts ( tts) ;
@@ -362,7 +367,7 @@ fn parse_args<'a>(
362367 let e = p. parse_expr ( ) ?;
363368 if let Some ( ( prev, _) ) = names. get ( & ident. name ) {
364369 ecx. struct_span_err ( e. span , & format ! ( "duplicate argument named `{}`" , ident) )
365- . span_label ( args[ * prev] . span , "previously here" )
370+ . span_label ( args[ * prev] . expr . span , "previously here" )
366371 . span_label ( e. span , "duplicate argument" )
367372 . emit ( ) ;
368373 continue ;
@@ -374,7 +379,7 @@ fn parse_args<'a>(
374379 // args. And remember the names.
375380 let slot = args. len ( ) ;
376381 names. insert ( ident. name , ( slot, ident. span ) ) ;
377- args. push ( e ) ;
382+ args. push ( FormatArg { expr : e , named : true } ) ;
378383 }
379384 _ => {
380385 let e = p. parse_expr ( ) ?;
@@ -385,11 +390,11 @@ fn parse_args<'a>(
385390 ) ;
386391 err. span_label ( e. span , "positional arguments must be before named arguments" ) ;
387392 for pos in names. values ( ) {
388- err. span_label ( args[ pos. 0 ] . span , "named argument" ) ;
393+ err. span_label ( args[ pos. 0 ] . expr . span , "named argument" ) ;
389394 }
390395 err. emit ( ) ;
391396 }
392- args. push ( e ) ;
397+ args. push ( FormatArg { expr : e , named : false } ) ;
393398 }
394399 }
395400 }
@@ -1214,7 +1219,7 @@ pub fn expand_preparsed_format_args(
12141219 ecx : & mut ExtCtxt < ' _ > ,
12151220 sp : Span ,
12161221 efmt : P < ast:: Expr > ,
1217- args : Vec < P < ast :: Expr > > ,
1222+ args : Vec < FormatArg > ,
12181223 names : FxHashMap < Symbol , ( usize , Span ) > ,
12191224 append_newline : bool ,
12201225) -> P < ast:: Expr > {
@@ -1304,6 +1309,25 @@ pub fn expand_preparsed_format_args(
13041309 e. span_label ( fmt_span. from_inner ( InnerSpan :: new ( span. start , span. end ) ) , label) ;
13051310 }
13061311 }
1312+ if err. should_be_replaced_with_positional_argument {
1313+ let captured_arg_span =
1314+ fmt_span. from_inner ( InnerSpan :: new ( err. span . start , err. span . end ) ) ;
1315+ let positional_args = args. iter ( ) . filter ( |arg| !arg. named ) . collect :: < Vec < _ > > ( ) ;
1316+ if let Ok ( arg) = ecx. source_map ( ) . span_to_snippet ( captured_arg_span) {
1317+ let span = match positional_args. last ( ) {
1318+ Some ( arg) => arg. expr . span ,
1319+ None => fmt_sp,
1320+ } ;
1321+ e. multipart_suggestion_verbose (
1322+ "consider using a positional formatting argument instead" ,
1323+ vec ! [
1324+ ( captured_arg_span, positional_args. len( ) . to_string( ) ) ,
1325+ ( span. shrink_to_hi( ) , format!( ", {}" , arg) ) ,
1326+ ] ,
1327+ Applicability :: MachineApplicable ,
1328+ ) ;
1329+ }
1330+ }
13071331 e. emit ( ) ;
13081332 return DummyResult :: raw_expr ( sp, true ) ;
13091333 }
@@ -1318,7 +1342,7 @@ pub fn expand_preparsed_format_args(
13181342
13191343 let mut cx = Context {
13201344 ecx,
1321- args,
1345+ args : args . into_iter ( ) . map ( |arg| arg . expr ) . collect ( ) ,
13221346 num_captured_args : 0 ,
13231347 arg_types,
13241348 arg_unique_types,
0 commit comments