@@ -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
@@ -251,23 +253,49 @@ 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 ( ) ,
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) ,
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 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
+
260
292
/// Actually verifies and tracks a given format placeholder
261
293
/// (a.k.a. argument).
262
294
fn verify_arg_type ( & mut self , arg : Position , ty : ArgumentType ) {
263
295
match arg {
264
296
Exact ( arg) => {
265
297
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) ;
271
299
return ;
272
300
}
273
301
match ty {
@@ -691,6 +719,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
691
719
all_pieces_simple : true ,
692
720
macsp,
693
721
fmtsp : fmt. span ,
722
+ invalid_refs : Vec :: new ( ) ,
694
723
} ;
695
724
696
725
let fmt_str = & * fmt. node . 0 . as_str ( ) ;
@@ -736,6 +765,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
736
765
cx. str_pieces . push ( s) ;
737
766
}
738
767
768
+ if cx. invalid_refs . len ( ) >= 1 {
769
+ cx. report_invalid_references ( ) ;
770
+ }
771
+
739
772
// Make sure that all arguments were used and all arguments have types.
740
773
let num_pos_args = cx. args . len ( ) - cx. names . len ( ) ;
741
774
let mut errs = vec ! [ ] ;
0 commit comments