@@ -110,6 +110,8 @@ struct Context<'a, 'b: 'a> {
110110 /// still existed in this phase of processing.
111111 /// Used only for `all_pieces_simple` tracking in `trans_piece`.
112112 curarg : usize ,
113+ /// Keep track of invalid references to positional arguments
114+ invalid_refs : Vec < usize > ,
113115}
114116
115117/// Parses the arguments from the given list of tokens, returning None
@@ -251,23 +253,49 @@ impl<'a, 'b> Context<'a, 'b> {
251253
252254 fn describe_num_args ( & self ) -> String {
253255 match self . args . len ( ) {
254- 0 => "no arguments given" . to_string ( ) ,
255- 1 => "there is 1 argument" . to_string ( ) ,
256- x => format ! ( "there are {} arguments" , x) ,
256+ 0 => "no arguments were given" . to_string ( ) ,
257+ 1 => "there is only 1 argument" . to_string ( ) ,
258+ x => format ! ( "there are only {} arguments" , x) ,
257259 }
258260 }
259261
262+ /// Handle invalid references to positional arguments. Output different
263+ /// errors for the case where all arguments are positional and for when
264+ /// there are named arguments in the format string.
265+ fn report_invalid_references ( & self ) {
266+ let mut refs: Vec < String > = self . invalid_refs
267+ . iter ( )
268+ . map ( |r| r. to_string ( ) )
269+ . collect ( ) ;
270+
271+ let msg = if self . names . is_empty ( ) {
272+ format ! ( "{} positional argument{} in format string, but {}" ,
273+ self . pieces. len( ) ,
274+ if self . pieces. len( ) > 1 { "s" } else { "" } ,
275+ self . describe_num_args( ) )
276+ } else {
277+ let arg_list = match refs. len ( ) {
278+ 1 => format ! ( "argument {}" , refs. pop( ) . unwrap( ) ) ,
279+ _ => format ! ( "arguments {head} and {tail}" ,
280+ tail=refs. pop( ) . unwrap( ) ,
281+ head=refs. join( ", " ) )
282+ } ;
283+
284+ format ! ( "invalid reference to positional {} ({})" ,
285+ arg_list,
286+ self . describe_num_args( ) )
287+ } ;
288+
289+ self . ecx . span_err ( self . fmtsp , & msg[ ..] ) ;
290+ }
291+
260292 /// Actually verifies and tracks a given format placeholder
261293 /// (a.k.a. argument).
262294 fn verify_arg_type ( & mut self , arg : Position , ty : ArgumentType ) {
263295 match arg {
264296 Exact ( arg) => {
265297 if self . args . len ( ) <= arg {
266- let msg = format ! ( "invalid reference to argument `{}` ({})" ,
267- arg,
268- self . describe_num_args( ) ) ;
269-
270- self . ecx . span_err ( self . fmtsp , & msg[ ..] ) ;
298+ self . invalid_refs . push ( arg) ;
271299 return ;
272300 }
273301 match ty {
@@ -691,6 +719,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
691719 all_pieces_simple : true ,
692720 macsp,
693721 fmtsp : fmt. span ,
722+ invalid_refs : Vec :: new ( ) ,
694723 } ;
695724
696725 let fmt_str = & * fmt. node . 0 . as_str ( ) ;
@@ -736,6 +765,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
736765 cx. str_pieces . push ( s) ;
737766 }
738767
768+ if cx. invalid_refs . len ( ) >= 1 {
769+ cx. report_invalid_references ( ) ;
770+ }
771+
739772 // Make sure that all arguments were used and all arguments have types.
740773 let num_pos_args = cx. args . len ( ) - cx. names . len ( ) ;
741774 let mut errs = vec ! [ ] ;
0 commit comments