@@ -110,6 +110,8 @@ struct Context<'a, 'b: 'a> {
110
110
/// still existed in this phase of processing.
111
111
/// Used only for `all_pieces_simple` tracking in `trans_piece`.
112
112
curarg : usize ,
113
+ /// Keep track of invalid references to positional arguments
114
+ invalid_refs : Vec < usize > ,
113
115
}
114
116
115
117
/// Parses the arguments from the given list of tokens, returning None
@@ -226,7 +228,7 @@ impl<'a, 'b> Context<'a, 'b> {
226
228
// argument second, if it's an implicit positional parameter
227
229
// it's written second, so it should come after width/precision.
228
230
let pos = match arg. position {
229
- parse:: ArgumentIs ( i) => Exact ( i) ,
231
+ parse:: ArgumentIs ( i) | parse :: ArgumentImplicitlyIs ( i ) => Exact ( i) ,
230
232
parse:: ArgumentNamed ( s) => Named ( s. to_string ( ) ) ,
231
233
} ;
232
234
@@ -251,23 +253,54 @@ impl<'a, 'b> Context<'a, 'b> {
251
253
252
254
fn describe_num_args ( & self ) -> String {
253
255
match self . args . len ( ) {
254
- 0 => "no arguments given" . to_string ( ) ,
256
+ 0 => "no arguments were given" . to_string ( ) ,
255
257
1 => "there is 1 argument" . to_string ( ) ,
256
258
x => format ! ( "there are {} arguments" , x) ,
257
259
}
258
260
}
259
261
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
+
260
297
/// Actually verifies and tracks a given format placeholder
261
298
/// (a.k.a. argument).
262
299
fn verify_arg_type ( & mut self , arg : Position , ty : ArgumentType ) {
263
300
match arg {
264
301
Exact ( arg) => {
265
302
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) ;
271
304
return ;
272
305
}
273
306
match ty {
@@ -403,7 +436,8 @@ impl<'a, 'b> Context<'a, 'b> {
403
436
}
404
437
} ;
405
438
match arg. position {
406
- parse:: ArgumentIs ( i) => {
439
+ parse:: ArgumentIs ( i)
440
+ | parse:: ArgumentImplicitlyIs ( i) => {
407
441
// Map to index in final generated argument array
408
442
// in case of multiple types specified
409
443
let arg_idx = match arg_index_consumed. get_mut ( i) {
@@ -691,6 +725,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
691
725
all_pieces_simple : true ,
692
726
macsp,
693
727
fmtsp : fmt. span ,
728
+ invalid_refs : Vec :: new ( ) ,
694
729
} ;
695
730
696
731
let fmt_str = & * fmt. node . 0 . as_str ( ) ;
@@ -711,6 +746,18 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
711
746
}
712
747
}
713
748
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
+
714
761
cx. build_index_map ( ) ;
715
762
716
763
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,
736
783
cx. str_pieces . push ( s) ;
737
784
}
738
785
786
+ if cx. invalid_refs . len ( ) >= 1 {
787
+ cx. report_invalid_references ( numbered_position_args) ;
788
+ }
789
+
739
790
// Make sure that all arguments were used and all arguments have types.
740
791
let num_pos_args = cx. args . len ( ) - cx. names . len ( ) ;
741
792
let mut errs = vec ! [ ] ;
0 commit comments