7 use rustc_ast
::tokenstream
::TokenStream
;
8 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
9 use rustc_errors
::{pluralize, Applicability, DiagnosticBuilder}
;
10 use rustc_expand
::base
::{self, *}
;
11 use rustc_parse_format
as parse
;
12 use rustc_span
::symbol
::{sym, Ident, Symbol}
;
13 use rustc_span
::{MultiSpan, Span}
;
16 use std
::collections
::hash_map
::Entry
;
20 Placeholder(&'
static str),
29 struct Context
<'a
, 'b
> {
30 ecx
: &'a
mut ExtCtxt
<'b
>,
31 /// The macro's call site. References to unstable formatting internals must
32 /// use this span to pass the stability checker.
34 /// The span of the format string literal.
37 /// List of parsed argument expressions.
38 /// Named expressions are resolved early, and are appended to the end of
39 /// argument expressions.
41 /// Example showing the various data structures in motion:
43 /// * Original: `"{foo:o} {:o} {foo:x} {0:x} {1:o} {:x} {1:x} {0:o}"`
44 /// * Implicit argument resolution: `"{foo:o} {0:o} {foo:x} {0:x} {1:o} {1:x} {1:x} {0:o}"`
45 /// * Name resolution: `"{2:o} {0:o} {2:x} {0:x} {1:o} {1:x} {1:x} {0:o}"`
46 /// * `arg_types` (in JSON): `[[0, 1, 0], [0, 1, 1], [0, 1]]`
47 /// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
48 /// * `names` (in JSON): `{"foo": 2}`
49 args
: Vec
<P
<ast
::Expr
>>,
50 /// Placeholder slot numbers indexed by argument.
51 arg_types
: Vec
<Vec
<usize>>,
52 /// Unique format specs seen for each argument.
53 arg_unique_types
: Vec
<Vec
<ArgumentType
>>,
54 /// Map from named arguments to their resolved indices.
55 names
: FxHashMap
<Symbol
, usize>,
57 /// The latest consecutive literal strings, or empty if there weren't any.
60 /// Collection of the compiled `rt::Argument` structures
61 pieces
: Vec
<P
<ast
::Expr
>>,
62 /// Collection of string literals
63 str_pieces
: Vec
<P
<ast
::Expr
>>,
64 /// Stays `true` if all formatting parameters are default (as in "{}{}").
65 all_pieces_simple
: bool
,
67 /// Mapping between positional argument references and indices into the
68 /// final generated static argument array. We record the starting indices
69 /// corresponding to each positional argument, and number of references
70 /// consumed so far for each argument, to facilitate correct `Position`
71 /// mapping in `build_piece`. In effect this can be seen as a "flattened"
72 /// version of `arg_unique_types`.
74 /// Again with the example described above in docstring for `args`:
76 /// * `arg_index_map` (in JSON): `[[0, 1, 0], [2, 3, 3], [4, 5]]`
77 arg_index_map
: Vec
<Vec
<usize>>,
79 /// Starting offset of count argument slots.
80 count_args_index_offset
: usize,
82 /// Count argument slots and tracking data structures.
83 /// Count arguments are separately tracked for de-duplication in case
84 /// multiple references are made to one argument. For example, in this
87 /// * Original: `"{:.*} {:.foo$} {1:.*} {:.0$}"`
88 /// * Implicit argument resolution: `"{1:.0$} {2:.foo$} {1:.3$} {4:.0$}"`
89 /// * Name resolution: `"{1:.0$} {2:.5$} {1:.3$} {4:.0$}"`
90 /// * `count_positions` (in JSON): `{0: 0, 5: 1, 3: 2}`
91 /// * `count_args`: `vec![Exact(0), Exact(5), Exact(3)]`
92 count_args
: Vec
<Position
>,
93 /// Relative slot numbers for count arguments.
94 count_positions
: FxHashMap
<usize, usize>,
95 /// Number of count slots assigned.
96 count_positions_count
: usize,
98 /// Current position of the implicit positional arg pointer, as if it
99 /// still existed in this phase of processing.
100 /// Used only for `all_pieces_simple` tracking in `build_piece`.
102 /// Current piece being evaluated, used for error reporting.
104 /// Keep track of invalid references to positional arguments.
105 invalid_refs
: Vec
<(usize, usize)>,
106 /// Spans of all the formatting arguments, in order.
107 arg_spans
: Vec
<Span
>,
108 /// All the formatting arguments that have formatting flags set, in order for diagnostics.
109 arg_with_formatting
: Vec
<parse
::FormatSpec
<'a
>>,
111 /// Whether this format string came from a string literal, as opposed to a macro.
115 /// Parses the arguments from the given list of tokens, returning the diagnostic
116 /// if there's a parse error so we can continue parsing other format!
119 /// If parsing succeeds, the return value is:
122 /// Some((fmtstr, parsed arguments, index map for named arguments))
125 ecx
: &mut ExtCtxt
<'a
>,
128 ) -> Result
<(P
<ast
::Expr
>, Vec
<P
<ast
::Expr
>>, FxHashMap
<Symbol
, usize>), DiagnosticBuilder
<'a
>> {
129 let mut args
= Vec
::<P
<ast
::Expr
>>::new();
130 let mut names
= FxHashMap
::<Symbol
, usize>::default();
132 let mut p
= ecx
.new_parser_from_tts(tts
);
134 if p
.token
== token
::Eof
{
135 return Err(ecx
.struct_span_err(sp
, "requires at least a format string argument"));
138 let fmtstr
= p
.parse_expr()?
;
139 let mut first
= true;
140 let mut named
= false;
142 while p
.token
!= token
::Eof
{
143 if !p
.eat(&token
::Comma
) {
145 // After `format!(""` we always expect *only* a comma...
146 let mut err
= ecx
.struct_span_err(p
.token
.span
, "expected token: `,`");
147 err
.span_label(p
.token
.span
, "expected `,`");
148 p
.maybe_annotate_with_ascription(&mut err
, false);
151 // ...after that delegate to `expect` to also include the other expected tokens.
152 return Err(p
.expect(&token
::Comma
).err().unwrap());
156 if p
.token
== token
::Eof
{
158 } // accept trailing commas
159 match p
.token
.ident() {
160 Some((ident
, _
)) if p
.look_ahead(1, |t
| *t
== token
::Eq
) => {
163 p
.expect(&token
::Eq
)?
;
164 let e
= p
.parse_expr()?
;
165 if let Some(prev
) = names
.get(&ident
.name
) {
166 ecx
.struct_span_err(e
.span
, &format
!("duplicate argument named `{}`", ident
))
167 .span_label(args
[*prev
].span
, "previously here")
168 .span_label(e
.span
, "duplicate argument")
173 // Resolve names into slots early.
174 // Since all the positional args are already seen at this point
175 // if the input is valid, we can simply append to the positional
176 // args. And remember the names.
177 let slot
= args
.len();
178 names
.insert(ident
.name
, slot
);
182 let e
= p
.parse_expr()?
;
184 let mut err
= ecx
.struct_span_err(
186 "positional arguments cannot follow named arguments",
188 err
.span_label(e
.span
, "positional arguments must be before named arguments");
189 for pos
in names
.values() {
190 err
.span_label(args
[*pos
].span
, "named argument");
198 Ok((fmtstr
, args
, names
))
201 impl<'a
, 'b
> Context
<'a
, 'b
> {
202 fn resolve_name_inplace(&self, p
: &mut parse
::Piece
<'_
>) {
203 // NOTE: the `unwrap_or` branch is needed in case of invalid format
204 // arguments, e.g., `format_args!("{foo}")`.
205 let lookup
= |s
: Symbol
| *self.names
.get(&s
).unwrap_or(&0);
208 parse
::String(_
) => {}
209 parse
::NextArgument(ref mut arg
) => {
210 if let parse
::ArgumentNamed(s
) = arg
.position
{
211 arg
.position
= parse
::ArgumentIs(lookup(s
));
213 if let parse
::CountIsName(s
) = arg
.format
.width
{
214 arg
.format
.width
= parse
::CountIsParam(lookup(s
));
216 if let parse
::CountIsName(s
) = arg
.format
.precision
{
217 arg
.format
.precision
= parse
::CountIsParam(lookup(s
));
223 /// Verifies one piece of a parse string, and remembers it if valid.
224 /// All errors are not emitted as fatal so we can continue giving errors
225 /// about this and possibly other format strings.
226 fn verify_piece(&mut self, p
: &parse
::Piece
<'_
>) {
228 parse
::String(..) => {}
229 parse
::NextArgument(ref arg
) => {
230 // width/precision first, if they have implicit positional
231 // parameters it makes more sense to consume them first.
232 self.verify_count(arg
.format
.width
);
233 self.verify_count(arg
.format
.precision
);
235 // argument second, if it's an implicit positional parameter
236 // it's written second, so it should come after width/precision.
237 let pos
= match arg
.position
{
238 parse
::ArgumentIs(i
) | parse
::ArgumentImplicitlyIs(i
) => Exact(i
),
239 parse
::ArgumentNamed(s
) => Named(s
),
242 let ty
= Placeholder(match &arg
.format
.ty
[..] {
253 let fmtsp
= self.fmtsp
;
254 let sp
= arg
.format
.ty_span
.map(|sp
| fmtsp
.from_inner(sp
));
255 let mut err
= self.ecx
.struct_span_err(
257 &format
!("unknown format trait `{}`", arg
.format
.ty
),
260 "the only appropriate formatting traits are:\n\
261 - ``, which uses the `Display` trait\n\
262 - `?`, which uses the `Debug` trait\n\
263 - `e`, which uses the `LowerExp` trait\n\
264 - `E`, which uses the `UpperExp` trait\n\
265 - `o`, which uses the `Octal` trait\n\
266 - `p`, which uses the `Pointer` trait\n\
267 - `b`, which uses the `Binary` trait\n\
268 - `x`, which uses the `LowerHex` trait\n\
269 - `X`, which uses the `UpperHex` trait",
271 if let Some(sp
) = sp
{
272 for (fmt
, name
) in &[
283 // FIXME: rustfix (`run-rustfix`) fails to apply suggestions.
284 // > "Cannot replace slice of data that was already replaced"
285 err
.tool_only_span_suggestion(
287 &format
!("use the `{}` trait", name
),
289 Applicability
::MaybeIncorrect
,
297 self.verify_arg_type(pos
, ty
);
303 fn verify_count(&mut self, c
: parse
::Count
) {
305 parse
::CountImplied
| parse
::CountIs(..) => {}
306 parse
::CountIsParam(i
) => {
307 self.verify_arg_type(Exact(i
), Count
);
309 parse
::CountIsName(s
) => {
310 self.verify_arg_type(Named(s
), Count
);
315 fn describe_num_args(&self) -> Cow
<'_
, str> {
316 match self.args
.len() {
317 0 => "no arguments were given".into(),
318 1 => "there is 1 argument".into(),
319 x
=> format
!("there are {} arguments", x
).into(),
323 /// Handle invalid references to positional arguments. Output different
324 /// errors for the case where all arguments are positional and for when
325 /// there are named arguments or numbered positional arguments in the
327 fn report_invalid_references(&self, numbered_position_args
: bool
) {
329 let sp
= if !self.arg_spans
.is_empty() {
330 // Point at the formatting arguments.
331 MultiSpan
::from_spans(self.arg_spans
.clone())
333 MultiSpan
::from_span(self.fmtsp
)
336 self.invalid_refs
.iter().map(|(r
, pos
)| (r
.to_string(), self.arg_spans
.get(*pos
)));
338 let mut zero_based_note
= false;
340 let count
= self.pieces
.len()
341 + self.arg_with_formatting
.iter().filter(|fmt
| fmt
.precision_span
.is_some()).count();
342 if self.names
.is_empty() && !numbered_position_args
&& count
!= self.args
.len() {
343 e
= self.ecx
.struct_span_err(
346 "{} positional argument{} in format string, but {}",
349 self.describe_num_args(),
352 for arg
in &self.args
{
353 // Point at the arguments that will be formatted.
354 e
.span_label(arg
.span
, "");
357 let (mut refs
, spans
): (Vec
<_
>, Vec
<_
>) = refs
.unzip();
358 // Avoid `invalid reference to positional arguments 7 and 7 (there is 1 argument)`
359 // for `println!("{7:7$}", 1);`
362 let (arg_list
, mut sp
) = if refs
.len() == 1 {
363 let spans
: Vec
<_
> = spans
.into_iter().filter_map(|sp
| sp
.copied()).collect();
365 format
!("argument {}", refs
[0]),
366 if spans
.is_empty() {
367 MultiSpan
::from_span(self.fmtsp
)
369 MultiSpan
::from_spans(spans
)
373 let pos
= MultiSpan
::from_spans(spans
.into_iter().map(|s
| *s
.unwrap()).collect());
374 let reg
= refs
.pop().unwrap();
375 (format
!("arguments {head} and {tail}", head
= refs
.join(", "), tail
= reg
,), pos
)
377 if self.arg_spans
.is_empty() {
378 sp
= MultiSpan
::from_span(self.fmtsp
);
381 e
= self.ecx
.struct_span_err(
384 "invalid reference to positional {} ({})",
386 self.describe_num_args()
389 zero_based_note
= true;
392 for fmt
in &self.arg_with_formatting
{
393 if let Some(span
) = fmt
.precision_span
{
394 let span
= self.fmtsp
.from_inner(span
);
395 match fmt
.precision
{
396 parse
::CountIsParam(pos
) if pos
> self.args
.len() => {
400 "this precision flag expects an `usize` argument at position {}, \
403 self.describe_num_args(),
406 zero_based_note
= true;
408 parse
::CountIsParam(pos
) => {
409 let count
= self.pieces
.len()
413 .filter(|fmt
| fmt
.precision_span
.is_some())
415 e
.span_label(span
, &format
!(
416 "this precision flag adds an extra required argument at position {}, \
417 which is why there {} expected",
420 "is 1 argument".to_string()
422 format
!("are {} arguments", count
)
425 if let Some(arg
) = self.args
.get(pos
) {
428 "this parameter corresponds to the precision flag",
431 zero_based_note
= true;
436 if let Some(span
) = fmt
.width_span
{
437 let span
= self.fmtsp
.from_inner(span
);
439 parse
::CountIsParam(pos
) if pos
> self.args
.len() => {
443 "this width flag expects an `usize` argument at position {}, \
446 self.describe_num_args(),
449 zero_based_note
= true;
456 e
.note("positional arguments are zero-based");
458 if !self.arg_with_formatting
.is_empty() {
460 "for information about formatting flags, visit \
461 https://doc.rust-lang.org/std/fmt/index.html",
468 /// Actually verifies and tracks a given format placeholder
469 /// (a.k.a. argument).
470 fn verify_arg_type(&mut self, arg
: Position
, ty
: ArgumentType
) {
473 if self.args
.len() <= arg
{
474 self.invalid_refs
.push((arg
, self.curpiece
));
479 // record every (position, type) combination only once
480 let seen_ty
= &mut self.arg_unique_types
[arg
];
481 let i
= seen_ty
.iter().position(|x
| *x
== ty
).unwrap_or_else(|| {
482 let i
= seen_ty
.len();
486 self.arg_types
[arg
].push(i
);
489 if let Entry
::Vacant(e
) = self.count_positions
.entry(arg
) {
490 let i
= self.count_positions_count
;
492 self.count_args
.push(Exact(arg
));
493 self.count_positions_count
+= 1;
500 match self.names
.get(&name
) {
502 // Treat as positional arg.
503 self.verify_arg_type(Exact(idx
), ty
)
506 let capture_feature_enabled
= self
510 .map_or(false, |features
| features
.format_args_capture
);
512 // For the moment capturing variables from format strings expanded from macros is
513 // disabled (see RFC #2795)
514 let can_capture
= capture_feature_enabled
&& self.is_literal
;
517 // Treat this name as a variable to capture from the surrounding scope
518 let idx
= self.args
.len();
519 self.arg_types
.push(Vec
::new());
520 self.arg_unique_types
.push(Vec
::new());
522 self.ecx
.expr_ident(self.fmtsp
, Ident
::new(name
, self.fmtsp
)),
524 self.names
.insert(name
, idx
);
525 self.verify_arg_type(Exact(idx
), ty
)
527 let msg
= format
!("there is no argument named `{}`", name
);
528 let sp
= if self.is_literal
{
529 *self.arg_spans
.get(self.curpiece
).unwrap_or(&self.fmtsp
)
533 let mut err
= self.ecx
.struct_span_err(sp
, &msg
[..]);
535 if capture_feature_enabled
&& !self.is_literal
{
537 "did you intend to capture a variable `{}` from \
538 the surrounding scope?",
542 "to avoid ambiguity, `format_args!` cannot capture variables \
543 when the format string is expanded from a macro",
545 } else if self.ecx
.parse_sess().unstable_features
.is_nightly_build() {
547 "if you intended to capture `{}` from the surrounding scope, add \
548 `#![feature(format_args_capture)]` to the crate attributes",
561 /// Builds the mapping between format placeholders and argument objects.
562 fn build_index_map(&mut self) {
563 // NOTE: Keep the ordering the same as `into_expr`'s expansion would do!
564 let args_len
= self.args
.len();
565 self.arg_index_map
.reserve(args_len
);
567 let mut sofar
= 0usize
;
570 for i
in 0..args_len
{
571 let arg_types
= &self.arg_types
[i
];
572 let arg_offsets
= arg_types
.iter().map(|offset
| sofar
+ *offset
).collect
::<Vec
<_
>>();
573 self.arg_index_map
.push(arg_offsets
);
574 sofar
+= self.arg_unique_types
[i
].len();
577 // Record starting index for counts, which appear just after arguments
578 self.count_args_index_offset
= sofar
;
581 fn rtpath(ecx
: &ExtCtxt
<'_
>, s
: &str) -> Vec
<Ident
> {
582 ecx
.std_path(&[sym
::fmt
, sym
::rt
, sym
::v1
, Symbol
::intern(s
)])
585 fn build_count(&self, c
: parse
::Count
) -> P
<ast
::Expr
> {
587 let count
= |c
, arg
| {
588 let mut path
= Context
::rtpath(self.ecx
, "Count");
589 path
.push(self.ecx
.ident_of(c
, sp
));
591 Some(arg
) => self.ecx
.expr_call_global(sp
, path
, vec
![arg
]),
592 None
=> self.ecx
.expr_path(self.ecx
.path_global(sp
, path
)),
596 parse
::CountIs(i
) => count("Is", Some(self.ecx
.expr_usize(sp
, i
))),
597 parse
::CountIsParam(i
) => {
598 // This needs mapping too, as `i` is referring to a macro
599 // argument. If `i` is not found in `count_positions` then
600 // the error had already been emitted elsewhere.
601 let i
= self.count_positions
.get(&i
).cloned().unwrap_or(0)
602 + self.count_args_index_offset
;
603 count("Param", Some(self.ecx
.expr_usize(sp
, i
)))
605 parse
::CountImplied
=> count("Implied", None
),
606 // should never be the case, names are already resolved
607 parse
::CountIsName(_
) => panic
!("should never happen"),
611 /// Build a literal expression from the accumulated string literals
612 fn build_literal_string(&mut self) -> P
<ast
::Expr
> {
614 let s
= Symbol
::intern(&self.literal
);
615 self.literal
.clear();
616 self.ecx
.expr_str(sp
, s
)
619 /// Builds a static `rt::Argument` from a `parse::Piece` or append
620 /// to the `literal` string.
623 piece
: &parse
::Piece
<'a
>,
624 arg_index_consumed
: &mut Vec
<usize>,
625 ) -> Option
<P
<ast
::Expr
>> {
628 parse
::String(s
) => {
629 self.literal
.push_str(s
);
632 parse
::NextArgument(ref arg
) => {
633 // Build the position
636 parse
::ArgumentIs(i
) | parse
::ArgumentImplicitlyIs(i
) => {
637 // Map to index in final generated argument array
638 // in case of multiple types specified
639 let arg_idx
= match arg_index_consumed
.get_mut(i
) {
640 None
=> 0, // error already emitted elsewhere
642 let idx_map
= &self.arg_index_map
[i
];
643 // unwrap_or branch: error already emitted elsewhere
644 let arg_idx
= *idx_map
.get(*offset
).unwrap_or(&0);
649 self.ecx
.expr_usize(sp
, arg_idx
)
652 // should never be the case, because names are already
654 parse
::ArgumentNamed(_
) => panic
!("should never happen"),
658 let simple_arg
= parse
::Argument
{
660 // We don't have ArgumentNext any more, so we have to
661 // track the current argument ourselves.
666 format
: parse
::FormatSpec
{
667 fill
: arg
.format
.fill
,
668 align
: parse
::AlignUnknown
,
670 precision
: parse
::CountImplied
,
671 precision_span
: None
,
672 width
: parse
::CountImplied
,
675 ty_span
: arg
.format
.ty_span
,
679 let fill
= arg
.format
.fill
.unwrap_or(' '
);
681 let pos_simple
= arg
.position
.index() == simple_arg
.position
.index();
683 if arg
.format
.precision_span
.is_some() || arg
.format
.width_span
.is_some() {
684 self.arg_with_formatting
.push(arg
.format
);
686 if !pos_simple
|| arg
.format
!= simple_arg
.format
|| fill
!= ' '
{
687 self.all_pieces_simple
= false;
691 let fill
= self.ecx
.expr_lit(sp
, ast
::LitKind
::Char(fill
));
693 let mut p
= Context
::rtpath(self.ecx
, "Alignment");
694 p
.push(self.ecx
.ident_of(name
, sp
));
695 self.ecx
.path_global(sp
, p
)
697 let align
= match arg
.format
.align
{
698 parse
::AlignLeft
=> align("Left"),
699 parse
::AlignRight
=> align("Right"),
700 parse
::AlignCenter
=> align("Center"),
701 parse
::AlignUnknown
=> align("Unknown"),
703 let align
= self.ecx
.expr_path(align
);
704 let flags
= self.ecx
.expr_u32(sp
, arg
.format
.flags
);
705 let prec
= self.build_count(arg
.format
.precision
);
706 let width
= self.build_count(arg
.format
.width
);
707 let path
= self.ecx
.path_global(sp
, Context
::rtpath(self.ecx
, "FormatSpec"));
708 let fmt
= self.ecx
.expr_struct(
712 self.ecx
.field_imm(sp
, self.ecx
.ident_of("fill", sp
), fill
),
713 self.ecx
.field_imm(sp
, self.ecx
.ident_of("align", sp
), align
),
714 self.ecx
.field_imm(sp
, self.ecx
.ident_of("flags", sp
), flags
),
715 self.ecx
.field_imm(sp
, self.ecx
.ident_of("precision", sp
), prec
),
716 self.ecx
.field_imm(sp
, self.ecx
.ident_of("width", sp
), width
),
720 let path
= self.ecx
.path_global(sp
, Context
::rtpath(self.ecx
, "Argument"));
721 Some(self.ecx
.expr_struct(
725 self.ecx
.field_imm(sp
, self.ecx
.ident_of("position", sp
), pos
),
726 self.ecx
.field_imm(sp
, self.ecx
.ident_of("format", sp
), fmt
),
733 /// Actually builds the expression which the format_args! block will be
735 fn into_expr(self) -> P
<ast
::Expr
> {
737 Vec
::with_capacity((0..self.args
.len()).map(|i
| self.arg_unique_types
[i
].len()).sum());
738 let mut counts
= Vec
::with_capacity(self.count_args
.len());
739 let mut pats
= Vec
::with_capacity(self.args
.len());
740 let mut heads
= Vec
::with_capacity(self.args
.len());
742 let names_pos
: Vec
<_
> = (0..self.args
.len())
743 .map(|i
| self.ecx
.ident_of(&format
!("arg{}", i
), self.macsp
))
746 // First, build up the static array which will become our precompiled
748 let pieces
= self.ecx
.expr_vec_slice(self.fmtsp
, self.str_pieces
);
750 // Before consuming the expressions, we have to remember spans for
751 // count arguments as they are now generated separate from other
752 // arguments, hence have no access to the `P<ast::Expr>`'s.
753 let spans_pos
: Vec
<_
> = self.args
.iter().map(|e
| e
.span
).collect();
755 // Right now there is a bug such that for the expression:
757 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
758 // valid for the call to `foo`. To work around this all arguments to the
759 // format! string are shoved into locals. Furthermore, we shove the address
760 // of each variable because we don't want to move out of the arguments
761 // passed to this function.
762 for (i
, e
) in self.args
.into_iter().enumerate() {
763 let name
= names_pos
[i
];
764 let span
= self.ecx
.with_def_site_ctxt(e
.span
);
765 pats
.push(self.ecx
.pat_ident(span
, name
));
766 for arg_ty
in self.arg_unique_types
[i
].iter() {
767 locals
.push(Context
::format_arg(self.ecx
, self.macsp
, e
.span
, arg_ty
, name
));
769 heads
.push(self.ecx
.expr_addr_of(e
.span
, e
));
771 for pos
in self.count_args
{
772 let index
= match pos
{
774 _
=> panic
!("should never happen"),
776 let name
= names_pos
[index
];
777 let span
= spans_pos
[index
];
778 counts
.push(Context
::format_arg(self.ecx
, self.macsp
, span
, &Count
, name
));
781 // Now create a vector containing all the arguments
782 let args
= locals
.into_iter().chain(counts
.into_iter());
784 let args_array
= self.ecx
.expr_vec(self.macsp
, args
.collect());
786 // Constructs an AST equivalent to:
788 // match (&arg0, &arg1) {
789 // (tmp0, tmp1) => args_array
798 // Because of #11585 the new temporary lifetime rule, the enclosing
799 // statements for these temporaries become the let's themselves.
800 // If one or more of them are RefCell's, RefCell borrow() will also
801 // end there; they don't last long enough for args_array to use them.
802 // The match expression solves the scope problem.
804 // Note, it may also very well be transformed to:
809 // ref tmp1 => args_array } } }
811 // But the nested match expression is proved to perform not as well
812 // as series of let's; the first approach does.
813 let pat
= self.ecx
.pat_tuple(self.macsp
, pats
);
814 let arm
= self.ecx
.arm(self.macsp
, pat
, args_array
);
815 let head
= self.ecx
.expr(self.macsp
, ast
::ExprKind
::Tup(heads
));
816 let result
= self.ecx
.expr_match(self.macsp
, head
, vec
![arm
]);
818 let args_slice
= self.ecx
.expr_addr_of(self.macsp
, result
);
820 // Now create the fmt::Arguments struct with all our locals we created.
821 let (fn_name
, fn_args
) = if self.all_pieces_simple
{
822 ("new_v1", vec
![pieces
, args_slice
])
824 // Build up the static array which will store our precompiled
825 // nonstandard placeholders, if there are any.
826 let fmt
= self.ecx
.expr_vec_slice(self.macsp
, self.pieces
);
828 ("new_v1_formatted", vec
![pieces
, args_slice
, fmt
])
831 let path
= self.ecx
.std_path(&[sym
::fmt
, sym
::Arguments
, Symbol
::intern(fn_name
)]);
832 self.ecx
.expr_call_global(self.macsp
, path
, fn_args
)
842 sp
= ecx
.with_def_site_ctxt(sp
);
843 let arg
= ecx
.expr_ident(sp
, arg
);
844 let trait_
= match *ty
{
845 Placeholder(trait_
) if trait_
== "<invalid>" => return DummyResult
::raw_expr(sp
, true),
846 Placeholder(trait_
) => trait_
,
848 let path
= ecx
.std_path(&[sym
::fmt
, sym
::ArgumentV1
, sym
::from_usize
]);
849 return ecx
.expr_call_global(macsp
, path
, vec
![arg
]);
853 let path
= ecx
.std_path(&[sym
::fmt
, Symbol
::intern(trait_
), sym
::fmt
]);
854 let format_fn
= ecx
.path_global(sp
, path
);
855 let path
= ecx
.std_path(&[sym
::fmt
, sym
::ArgumentV1
, sym
::new
]);
856 ecx
.expr_call_global(macsp
, path
, vec
![arg
, ecx
.expr_path(format_fn
)])
860 fn expand_format_args_impl
<'cx
>(
861 ecx
: &'cx
mut ExtCtxt
<'_
>,
865 ) -> Box
<dyn base
::MacResult
+ 'cx
> {
866 sp
= ecx
.with_def_site_ctxt(sp
);
867 match parse_args(ecx
, sp
, tts
) {
868 Ok((efmt
, args
, names
)) => {
869 MacEager
::expr(expand_preparsed_format_args(ecx
, sp
, efmt
, args
, names
, nl
))
878 pub fn expand_format_args
<'cx
>(
879 ecx
: &'cx
mut ExtCtxt
<'_
>,
882 ) -> Box
<dyn base
::MacResult
+ 'cx
> {
883 expand_format_args_impl(ecx
, sp
, tts
, false)
886 pub fn expand_format_args_nl
<'cx
>(
887 ecx
: &'cx
mut ExtCtxt
<'_
>,
890 ) -> Box
<dyn base
::MacResult
+ 'cx
> {
891 expand_format_args_impl(ecx
, sp
, tts
, true)
894 /// Take the various parts of `format_args!(efmt, args..., name=names...)`
895 /// and construct the appropriate formatting expression.
896 pub fn expand_preparsed_format_args(
897 ecx
: &mut ExtCtxt
<'_
>,
900 args
: Vec
<P
<ast
::Expr
>>,
901 names
: FxHashMap
<Symbol
, usize>,
902 append_newline
: bool
,
904 // NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because
905 // `ArgumentType` does not derive `Clone`.
906 let arg_types
: Vec
<_
> = (0..args
.len()).map(|_
| Vec
::new()).collect();
907 let arg_unique_types
: Vec
<_
> = (0..args
.len()).map(|_
| Vec
::new()).collect();
909 let mut macsp
= ecx
.call_site();
910 macsp
= ecx
.with_def_site_ctxt(macsp
);
912 let msg
= "format argument must be a string literal";
913 let fmt_sp
= efmt
.span
;
914 let (fmt_str
, fmt_style
, fmt_span
) = match expr_to_spanned_string(ecx
, efmt
, msg
) {
915 Ok(mut fmt
) if append_newline
=> {
916 fmt
.0 = Symbol
::intern(&format
!("{}\n", fmt
.0));
921 if let Some(mut err
) = err
{
922 let sugg_fmt
= match args
.len() {
923 0 => "{}".to_string(),
924 _
=> format
!("{}{{}}", "{} ".repeat(args
.len())),
927 fmt_sp
.shrink_to_lo(),
928 "you might be missing a string literal to format with",
929 format
!("\"{}\", ", sugg_fmt
),
930 Applicability
::MaybeIncorrect
,
934 return DummyResult
::raw_expr(sp
, true);
938 let str_style
= match fmt_style
{
939 ast
::StrStyle
::Cooked
=> None
,
940 ast
::StrStyle
::Raw(raw
) => Some(raw
as usize),
943 let fmt_str
= &fmt_str
.as_str(); // for the suggestions below
944 let fmt_snippet
= ecx
.source_map().span_to_snippet(fmt_sp
).ok();
945 let mut parser
= parse
::Parser
::new(
950 parse
::ParseMode
::Format
,
953 let mut unverified_pieces
= Vec
::new();
954 while let Some(piece
) = parser
.next() {
955 if !parser
.errors
.is_empty() {
958 unverified_pieces
.push(piece
);
962 if !parser
.errors
.is_empty() {
963 let err
= parser
.errors
.remove(0);
964 let sp
= fmt_span
.from_inner(err
.span
);
965 let mut e
= ecx
.struct_span_err(sp
, &format
!("invalid format string: {}", err
.description
));
966 e
.span_label(sp
, err
.label
+ " in format string");
967 if let Some(note
) = err
.note
{
970 if let Some((label
, span
)) = err
.secondary_label
{
971 let sp
= fmt_span
.from_inner(span
);
972 e
.span_label(sp
, label
);
975 return DummyResult
::raw_expr(sp
, true);
978 let arg_spans
= parser
.arg_places
.iter().map(|span
| fmt_span
.from_inner(*span
)).collect();
980 let named_pos
: FxHashSet
<usize> = names
.values().cloned().collect();
982 let mut cx
= Context
{
990 arg_index_map
: Vec
::new(),
991 count_args
: Vec
::new(),
992 count_positions
: FxHashMap
::default(),
993 count_positions_count
: 0,
994 count_args_index_offset
: 0,
995 literal
: String
::new(),
996 pieces
: Vec
::with_capacity(unverified_pieces
.len()),
997 str_pieces
: Vec
::with_capacity(unverified_pieces
.len()),
998 all_pieces_simple
: true,
1001 invalid_refs
: Vec
::new(),
1003 arg_with_formatting
: Vec
::new(),
1004 is_literal
: parser
.is_literal
,
1007 // This needs to happen *after* the Parser has consumed all pieces to create all the spans
1008 let pieces
= unverified_pieces
1011 cx
.verify_piece(&piece
);
1012 cx
.resolve_name_inplace(&mut piece
);
1015 .collect
::<Vec
<_
>>();
1017 let numbered_position_args
= pieces
.iter().any(|arg
: &parse
::Piece
<'_
>| match *arg
{
1018 parse
::String(_
) => false,
1019 parse
::NextArgument(arg
) => match arg
.position
{
1020 parse
::Position
::ArgumentIs(_
) => true,
1025 cx
.build_index_map();
1027 let mut arg_index_consumed
= vec
![0usize
; cx
.arg_index_map
.len()];
1029 for piece
in pieces
{
1030 if let Some(piece
) = cx
.build_piece(&piece
, &mut arg_index_consumed
) {
1031 let s
= cx
.build_literal_string();
1032 cx
.str_pieces
.push(s
);
1033 cx
.pieces
.push(piece
);
1037 if !cx
.literal
.is_empty() {
1038 let s
= cx
.build_literal_string();
1039 cx
.str_pieces
.push(s
);
1042 if !cx
.invalid_refs
.is_empty() {
1043 cx
.report_invalid_references(numbered_position_args
);
1046 // Make sure that all arguments were used and all arguments have types.
1051 .filter(|(i
, ty
)| ty
.is_empty() && !cx
.count_positions
.contains_key(&i
))
1053 let msg
= if named_pos
.contains(&i
) {
1055 "named argument never used"
1057 // positional argument
1058 "argument never used"
1060 (cx
.args
[i
].span
, msg
)
1062 .collect
::<Vec
<_
>>();
1064 let errs_len
= errs
.len();
1065 if !errs
.is_empty() {
1066 let args_used
= cx
.arg_types
.len() - errs_len
;
1067 let args_unused
= errs_len
;
1071 let (sp
, msg
) = errs
.into_iter().next().unwrap();
1072 let mut diag
= cx
.ecx
.struct_span_err(sp
, msg
);
1073 diag
.span_label(sp
, msg
);
1076 let mut diag
= cx
.ecx
.struct_span_err(
1077 errs
.iter().map(|&(sp
, _
)| sp
).collect
::<Vec
<Span
>>(),
1078 "multiple unused formatting arguments",
1080 diag
.span_label(cx
.fmtsp
, "multiple missing formatting specifiers");
1081 for (sp
, msg
) in errs
{
1082 diag
.span_label(sp
, msg
);
1088 // Used to ensure we only report translations for *one* kind of foreign format.
1089 let mut found_foreign
= false;
1090 // Decide if we want to look for foreign formatting directives.
1091 if args_used
< args_unused
{
1092 use super::format_foreign
as foreign
;
1094 // The set of foreign substitutions we've explained. This prevents spamming the user
1095 // with `%d should be written as {}` over and over again.
1096 let mut explained
= FxHashSet
::default();
1098 macro_rules
! check_foreign
{
1100 let mut show_doc_note
= false;
1102 let mut suggestions
= vec
![];
1103 // account for `"` and account for raw strings `r#`
1104 let padding
= str_style
.map(|i
| i
+ 2).unwrap_or(1);
1105 for sub
in foreign
::$kind
::iter_subs(fmt_str
, padding
) {
1106 let trn
= match sub
.translate() {
1109 // If it has no translation, don't call it out specifically.
1113 let pos
= sub
.position();
1114 let sub
= String
::from(sub
.as_str());
1115 if explained
.contains(&sub
) {
1118 explained
.insert(sub
.clone());
1121 found_foreign
= true;
1122 show_doc_note
= true;
1125 if let Some(inner_sp
) = pos
{
1126 let sp
= fmt_sp
.from_inner(inner_sp
);
1127 suggestions
.push((sp
, trn
));
1129 diag
.help(&format
!("`{}` should be written as `{}`", sub
, trn
));
1136 " formatting not supported; see the documentation for `std::fmt`",
1139 if suggestions
.len() > 0 {
1140 diag
.multipart_suggestion(
1141 "format specifiers use curly braces",
1143 Applicability
::MachineApplicable
,
1149 check_foreign
!(printf
);
1151 check_foreign
!(shell
);
1154 if !found_foreign
&& errs_len
== 1 {
1155 diag
.span_label(cx
.fmtsp
, "formatting specifier missing");