@@ -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
@@ -226,7 +228,7 @@ impl<'a, 'b> Context<'a, 'b> {
226228 // argument second, if it's an implicit positional parameter
227229 // it's written second, so it should come after width/precision.
228230 let pos = match arg. position {
229- parse:: ArgumentIs ( i) => Exact ( i) ,
231+ parse:: ArgumentIs ( i) | parse :: ArgumentImplicitlyIs ( i ) => Exact ( i) ,
230232 parse:: ArgumentNamed ( s) => Named ( s. to_string ( ) ) ,
231233 } ;
232234
@@ -251,23 +253,54 @@ 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 ( ) ,
256+ 0 => "no arguments were given" . to_string ( ) ,
255257 1 => "there is 1 argument" . to_string ( ) ,
256258 x => format ! ( "there are {} 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 or numbered positional arguments in the
265+ /// format string.
266+ fn report_invalid_references ( & self , numbered_position_args : bool ) {
267+ let mut e;
268+ let mut refs: Vec < String > = self . invalid_refs
269+ . iter ( )
270+ . map ( |r| r. to_string ( ) )
271+ . collect ( ) ;
272+
273+ if self . names . is_empty ( ) && !numbered_position_args {
274+ e = self . ecx . mut_span_err ( self . fmtsp ,
275+ & format ! ( "{} positional argument{} in format string, but {}" ,
276+ self . pieces. len( ) ,
277+ if self . pieces. len( ) > 1 { "s" } else { "" } ,
278+ self . describe_num_args( ) ) ) ;
279+ } else {
280+ let arg_list = match refs. len ( ) {
281+ 1 => format ! ( "argument {}" , refs. pop( ) . unwrap( ) ) ,
282+ _ => format ! ( "arguments {head} and {tail}" ,
283+ tail=refs. pop( ) . unwrap( ) ,
284+ head=refs. join( ", " ) )
285+ } ;
286+
287+ e = self . ecx . mut_span_err ( self . fmtsp ,
288+ & format ! ( "invalid reference to positional {} ({})" ,
289+ arg_list,
290+ self . describe_num_args( ) ) ) ;
291+ e. note ( "positional arguments are zero-based" ) ;
292+ } ;
293+
294+ e. emit ( ) ;
295+ }
296+
260297 /// Actually verifies and tracks a given format placeholder
261298 /// (a.k.a. argument).
262299 fn verify_arg_type ( & mut self , arg : Position , ty : ArgumentType ) {
263300 match arg {
264301 Exact ( arg) => {
265302 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[ ..] ) ;
303+ self . invalid_refs . push ( arg) ;
271304 return ;
272305 }
273306 match ty {
@@ -403,7 +436,8 @@ impl<'a, 'b> Context<'a, 'b> {
403436 }
404437 } ;
405438 match arg. position {
406- parse:: ArgumentIs ( i) => {
439+ parse:: ArgumentIs ( i)
440+ | parse:: ArgumentImplicitlyIs ( i) => {
407441 // Map to index in final generated argument array
408442 // in case of multiple types specified
409443 let arg_idx = match arg_index_consumed. get_mut ( i) {
@@ -691,6 +725,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
691725 all_pieces_simple : true ,
692726 macsp,
693727 fmtsp : fmt. span ,
728+ invalid_refs : Vec :: new ( ) ,
694729 } ;
695730
696731 let fmt_str = & * fmt. node . 0 . as_str ( ) ;
@@ -711,6 +746,18 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
711746 }
712747 }
713748
749+ let numbered_position_args = pieces. iter ( ) . any ( |arg : & parse:: Piece | {
750+ match * arg {
751+ parse:: String ( _) => false ,
752+ parse:: NextArgument ( arg) => {
753+ match arg. position {
754+ parse:: Position :: ArgumentIs ( _) => true ,
755+ _ => false ,
756+ }
757+ }
758+ }
759+ } ) ;
760+
714761 cx. build_index_map ( ) ;
715762
716763 let mut arg_index_consumed = vec ! [ 0usize ; cx. arg_index_map. len( ) ] ;
@@ -736,6 +783,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
736783 cx. str_pieces . push ( s) ;
737784 }
738785
786+ if cx. invalid_refs . len ( ) >= 1 {
787+ cx. report_invalid_references ( numbered_position_args) ;
788+ }
789+
739790 // Make sure that all arguments were used and all arguments have types.
740791 let num_pos_args = cx. args . len ( ) - cx. names . len ( ) ;
741792 let mut errs = vec ! [ ] ;
0 commit comments