@@ -23,6 +23,7 @@ enum ArgumentType {
2323
2424enum Position {
2525 Exact ( usize ) ,
26+ Capture ( usize ) ,
2627 Named ( Symbol ) ,
2728}
2829
@@ -47,6 +48,8 @@ struct Context<'a, 'b> {
4748 /// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
4849 /// * `names` (in JSON): `{"foo": 2}`
4950 args : Vec < P < ast:: Expr > > ,
51+ /// The number of arguments that were added by implicit capturing.
52+ num_captured_args : usize ,
5053 /// Placeholder slot numbers indexed by argument.
5154 arg_types : Vec < Vec < usize > > ,
5255 /// Unique format specs seen for each argument.
@@ -229,6 +232,11 @@ fn parse_args<'a>(
229232}
230233
231234impl < ' a , ' b > Context < ' a , ' b > {
235+ /// The number of arguments that were explicitly given.
236+ fn num_args ( & self ) -> usize {
237+ self . args . len ( ) - self . num_captured_args
238+ }
239+
232240 fn resolve_name_inplace ( & self , p : & mut parse:: Piece < ' _ > ) {
233241 // NOTE: the `unwrap_or` branch is needed in case of invalid format
234242 // arguments, e.g., `format_args!("{foo}")`.
@@ -343,7 +351,7 @@ impl<'a, 'b> Context<'a, 'b> {
343351 }
344352
345353 fn describe_num_args ( & self ) -> Cow < ' _ , str > {
346- match self . args . len ( ) {
354+ match self . num_args ( ) {
347355 0 => "no arguments were given" . into ( ) ,
348356 1 => "there is 1 argument" . into ( ) ,
349357 x => format ! ( "there are {} arguments" , x) . into ( ) ,
@@ -369,7 +377,7 @@ impl<'a, 'b> Context<'a, 'b> {
369377
370378 let count = self . pieces . len ( )
371379 + self . arg_with_formatting . iter ( ) . filter ( |fmt| fmt. precision_span . is_some ( ) ) . count ( ) ;
372- if self . names . is_empty ( ) && !numbered_position_args && count != self . args . len ( ) {
380+ if self . names . is_empty ( ) && !numbered_position_args && count != self . num_args ( ) {
373381 e = self . ecx . struct_span_err (
374382 sp,
375383 & format ! (
@@ -417,7 +425,7 @@ impl<'a, 'b> Context<'a, 'b> {
417425 if let Some ( span) = fmt. precision_span {
418426 let span = self . fmtsp . from_inner ( span) ;
419427 match fmt. precision {
420- parse:: CountIsParam ( pos) if pos > self . args . len ( ) => {
428+ parse:: CountIsParam ( pos) if pos > self . num_args ( ) => {
421429 e. span_label (
422430 span,
423431 & format ! (
@@ -460,7 +468,7 @@ impl<'a, 'b> Context<'a, 'b> {
460468 if let Some ( span) = fmt. width_span {
461469 let span = self . fmtsp . from_inner ( span) ;
462470 match fmt. width {
463- parse:: CountIsParam ( pos) if pos > self . args . len ( ) => {
471+ parse:: CountIsParam ( pos) if pos > self . num_args ( ) => {
464472 e. span_label (
465473 span,
466474 & format ! (
@@ -492,12 +500,15 @@ impl<'a, 'b> Context<'a, 'b> {
492500 /// Actually verifies and tracks a given format placeholder
493501 /// (a.k.a. argument).
494502 fn verify_arg_type ( & mut self , arg : Position , ty : ArgumentType ) {
503+ if let Exact ( arg) = arg {
504+ if arg >= self . num_args ( ) {
505+ self . invalid_refs . push ( ( arg, self . curpiece ) ) ;
506+ return ;
507+ }
508+ }
509+
495510 match arg {
496- Exact ( arg) => {
497- if self . args . len ( ) <= arg {
498- self . invalid_refs . push ( ( arg, self . curpiece ) ) ;
499- return ;
500- }
511+ Exact ( arg) | Capture ( arg) => {
501512 match ty {
502513 Placeholder ( _) => {
503514 // record every (position, type) combination only once
@@ -524,7 +535,7 @@ impl<'a, 'b> Context<'a, 'b> {
524535 match self . names . get ( & name) {
525536 Some ( & idx) => {
526537 // Treat as positional arg.
527- self . verify_arg_type ( Exact ( idx) , ty)
538+ self . verify_arg_type ( Capture ( idx) , ty)
528539 }
529540 None => {
530541 // For the moment capturing variables from format strings expanded from macros is
@@ -539,9 +550,10 @@ impl<'a, 'b> Context<'a, 'b> {
539550 } else {
540551 self . fmtsp
541552 } ;
553+ self . num_captured_args += 1 ;
542554 self . args . push ( self . ecx . expr_ident ( span, Ident :: new ( name, span) ) ) ;
543555 self . names . insert ( name, idx) ;
544- self . verify_arg_type ( Exact ( idx) , ty)
556+ self . verify_arg_type ( Capture ( idx) , ty)
545557 } else {
546558 let msg = format ! ( "there is no argument named `{}`" , name) ;
547559 let sp = if self . is_literal {
@@ -1010,6 +1022,7 @@ pub fn expand_preparsed_format_args(
10101022 let mut cx = Context {
10111023 ecx,
10121024 args,
1025+ num_captured_args : 0 ,
10131026 arg_types,
10141027 arg_unique_types,
10151028 names,
0 commit comments