@@ -107,6 +107,9 @@ struct Context<'a, 'b> {
107
107
arg_spans : Vec < Span > ,
108
108
/// All the formatting arguments that have formatting flags set, in order for diagnostics.
109
109
arg_with_formatting : Vec < parse:: FormatSpec < ' a > > ,
110
+
111
+ /// Whether this format string came from a string literal, as opposed to a macro.
112
+ is_literal : bool ,
110
113
}
111
114
112
115
/// Parses the arguments from the given list of tokens, returning the diagnostic
@@ -498,10 +501,59 @@ impl<'a, 'b> Context<'a, 'b> {
498
501
self . verify_arg_type ( Exact ( idx) , ty)
499
502
}
500
503
None => {
501
- let msg = format ! ( "there is no argument named `{}`" , name) ;
502
- let sp = * self . arg_spans . get ( self . curpiece ) . unwrap_or ( & self . fmtsp ) ;
503
- let mut err = self . ecx . struct_span_err ( sp, & msg[ ..] ) ;
504
- err. emit ( ) ;
504
+ let capture_feature_enabled = self
505
+ . ecx
506
+ . ecfg
507
+ . features
508
+ . map_or ( false , |features| features. format_args_capture ) ;
509
+
510
+ // For the moment capturing variables from format strings expanded from
511
+ // literals is disabled (see RFC #2795)
512
+ let can_capture = capture_feature_enabled && self . is_literal ;
513
+
514
+ if can_capture {
515
+ // Treat this name as a variable to capture from the surrounding scope
516
+ let idx = self . args . len ( ) ;
517
+ self . arg_types . push ( Vec :: new ( ) ) ;
518
+ self . arg_unique_types . push ( Vec :: new ( ) ) ;
519
+ self . args . push (
520
+ self . ecx . expr_ident ( self . fmtsp , Ident :: new ( name, self . fmtsp ) ) ,
521
+ ) ;
522
+ self . names . insert ( name, idx) ;
523
+ self . verify_arg_type ( Exact ( idx) , ty)
524
+ } else {
525
+ let msg = format ! ( "there is no argument named `{}`" , name) ;
526
+ let sp = if self . is_literal {
527
+ * self . arg_spans . get ( self . curpiece ) . unwrap_or ( & self . fmtsp )
528
+ } else {
529
+ self . fmtsp
530
+ } ;
531
+ let mut err = self . ecx . struct_span_err ( sp, & msg[ ..] ) ;
532
+
533
+ if capture_feature_enabled && !self . is_literal {
534
+ err. note ( & format ! (
535
+ "did you intend to capture a variable `{}` from \
536
+ the surrounding scope?",
537
+ name
538
+ ) ) ;
539
+ err. note (
540
+ "for hygiene reasons format_args! cannot capture variables \
541
+ when the format string is expanded from a macro",
542
+ ) ;
543
+ } else if self . ecx . parse_sess ( ) . unstable_features . is_nightly_build ( ) {
544
+ err. note ( & format ! (
545
+ "did you intend to capture a variable `{}` from \
546
+ the surrounding scope?",
547
+ name
548
+ ) ) ;
549
+ err. help (
550
+ "add `#![feature(format_args_capture)]` to the crate \
551
+ attributes to enable",
552
+ ) ;
553
+ }
554
+
555
+ err. emit ( ) ;
556
+ }
505
557
}
506
558
}
507
559
}
@@ -951,6 +1003,7 @@ pub fn expand_preparsed_format_args(
951
1003
invalid_refs : Vec :: new ( ) ,
952
1004
arg_spans,
953
1005
arg_with_formatting : Vec :: new ( ) ,
1006
+ is_literal : parser. is_literal ,
954
1007
} ;
955
1008
956
1009
// This needs to happen *after* the Parser has consumed all pieces to create all the spans
0 commit comments