@@ -25,6 +25,7 @@ enum ArgumentType {
2525
2626enum Position {
2727 Exact ( usize ) ,
28+ Capture ( usize ) ,
2829 Named ( Symbol ) ,
2930}
3031
@@ -49,6 +50,8 @@ struct Context<'a, 'b> {
4950 /// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
5051 /// * `names` (in JSON): `{"foo": 2}`
5152 args : Vec < P < ast:: Expr > > ,
53+ /// The number of arguments that were added by implicit capturing.
54+ num_captured_args : usize ,
5255 /// Placeholder slot numbers indexed by argument.
5356 arg_types : Vec < Vec < usize > > ,
5457 /// Unique format specs seen for each argument.
@@ -231,6 +234,11 @@ fn parse_args<'a>(
231234}
232235
233236impl < ' a , ' b > Context < ' a , ' b > {
237+ /// The number of arguments that were explicitly given.
238+ fn num_args ( & self ) -> usize {
239+ self . args . len ( ) - self . num_captured_args
240+ }
241+
234242 fn resolve_name_inplace ( & self , p : & mut parse:: Piece < ' _ > ) {
235243 // NOTE: the `unwrap_or` branch is needed in case of invalid format
236244 // arguments, e.g., `format_args!("{foo}")`.
@@ -345,7 +353,7 @@ impl<'a, 'b> Context<'a, 'b> {
345353 }
346354
347355 fn describe_num_args ( & self ) -> Cow < ' _ , str > {
348- match self . args . len ( ) {
356+ match self . num_args ( ) {
349357 0 => "no arguments were given" . into ( ) ,
350358 1 => "there is 1 argument" . into ( ) ,
351359 x => format ! ( "there are {} arguments" , x) . into ( ) ,
@@ -371,7 +379,7 @@ impl<'a, 'b> Context<'a, 'b> {
371379
372380 let count = self . pieces . len ( )
373381 + self . arg_with_formatting . iter ( ) . filter ( |fmt| fmt. precision_span . is_some ( ) ) . count ( ) ;
374- if self . names . is_empty ( ) && !numbered_position_args && count != self . args . len ( ) {
382+ if self . names . is_empty ( ) && !numbered_position_args && count != self . num_args ( ) {
375383 e = self . ecx . struct_span_err (
376384 sp,
377385 & format ! (
@@ -419,7 +427,7 @@ impl<'a, 'b> Context<'a, 'b> {
419427 if let Some ( span) = fmt. precision_span {
420428 let span = self . fmtsp . from_inner ( span) ;
421429 match fmt. precision {
422- parse:: CountIsParam ( pos) if pos > self . args . len ( ) => {
430+ parse:: CountIsParam ( pos) if pos > self . num_args ( ) => {
423431 e. span_label (
424432 span,
425433 & format ! (
@@ -462,7 +470,7 @@ impl<'a, 'b> Context<'a, 'b> {
462470 if let Some ( span) = fmt. width_span {
463471 let span = self . fmtsp . from_inner ( span) ;
464472 match fmt. width {
465- parse:: CountIsParam ( pos) if pos > self . args . len ( ) => {
473+ parse:: CountIsParam ( pos) if pos > self . num_args ( ) => {
466474 e. span_label (
467475 span,
468476 & format ! (
@@ -494,12 +502,15 @@ impl<'a, 'b> Context<'a, 'b> {
494502 /// Actually verifies and tracks a given format placeholder
495503 /// (a.k.a. argument).
496504 fn verify_arg_type ( & mut self , arg : Position , ty : ArgumentType ) {
505+ if let Exact ( arg) = arg {
506+ if arg >= self . num_args ( ) {
507+ self . invalid_refs . push ( ( arg, self . curpiece ) ) ;
508+ return ;
509+ }
510+ }
511+
497512 match arg {
498- Exact ( arg) => {
499- if self . args . len ( ) <= arg {
500- self . invalid_refs . push ( ( arg, self . curpiece ) ) ;
501- return ;
502- }
513+ Exact ( arg) | Capture ( arg) => {
503514 match ty {
504515 Placeholder ( _) => {
505516 // record every (position, type) combination only once
@@ -526,7 +537,7 @@ impl<'a, 'b> Context<'a, 'b> {
526537 match self . names . get ( & name) {
527538 Some ( & idx) => {
528539 // Treat as positional arg.
529- self . verify_arg_type ( Exact ( idx) , ty)
540+ self . verify_arg_type ( Capture ( idx) , ty)
530541 }
531542 None => {
532543 // For the moment capturing variables from format strings expanded from macros is
@@ -541,9 +552,10 @@ impl<'a, 'b> Context<'a, 'b> {
541552 } else {
542553 self . fmtsp
543554 } ;
555+ self . num_captured_args += 1 ;
544556 self . args . push ( self . ecx . expr_ident ( span, Ident :: new ( name, span) ) ) ;
545557 self . names . insert ( name, idx) ;
546- self . verify_arg_type ( Exact ( idx) , ty)
558+ self . verify_arg_type ( Capture ( idx) , ty)
547559 } else {
548560 let msg = format ! ( "there is no argument named `{}`" , name) ;
549561 let sp = if self . is_literal {
@@ -1051,6 +1063,7 @@ pub fn expand_preparsed_format_args(
10511063 let mut cx = Context {
10521064 ecx,
10531065 args,
1066+ num_captured_args : 0 ,
10541067 arg_types,
10551068 arg_unique_types,
10561069 names,
0 commit comments