3 use rustc_ast
::tokenstream
::TokenStream
;
5 use rustc_data_structures
::fx
::FxHashSet
;
6 use rustc_errors
::{pluralize, Applicability, MultiSpan, PResult}
;
7 use rustc_expand
::base
::{self, *}
;
8 use rustc_parse_format
as parse
;
9 use rustc_span
::symbol
::{Ident, Symbol}
;
10 use rustc_span
::{BytePos, InnerSpan, Span}
;
12 use rustc_lint_defs
::builtin
::NAMED_ARGUMENTS_USED_POSITIONALLY
;
13 use rustc_lint_defs
::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}
;
19 use expand
::expand_parsed_format_args
;
21 // The format_args!() macro is expanded in three steps:
22 // 1. First, `parse_args` will parse the `(literal, arg, arg, name=arg, name=arg)` syntax,
23 // but doesn't parse the template (the literal) itself.
24 // 2. Second, `make_format_args` will parse the template, the format options, resolve argument references,
25 // produce diagnostics, and turn the whole thing into a `FormatArgs` structure.
26 // 3. Finally, `expand_parsed_format_args` will turn that `FormatArgs` structure
27 // into the expression that the macro expands to.
29 // See format/ast.rs for the FormatArgs structure and glossary.
31 // Only used in parse_args and report_invalid_references,
32 // to indicate how a referred argument was used.
33 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
35 Placeholder(Option
<Span
>),
39 use PositionUsedAs
::*;
41 /// Parses the arguments from the given list of tokens, returning the diagnostic
42 /// if there's a parse error so we can continue parsing other format!
45 /// If parsing succeeds, the return value is:
48 /// Ok((fmtstr, parsed arguments))
51 ecx
: &mut ExtCtxt
<'a
>,
54 ) -> PResult
<'a
, (P
<Expr
>, FormatArguments
)> {
55 let mut args
= FormatArguments
::new();
57 let mut p
= ecx
.new_parser_from_tts(tts
);
59 if p
.token
== token
::Eof
{
60 return Err(ecx
.struct_span_err(sp
, "requires at least a format string argument"));
63 let first_token
= &p
.token
;
64 let fmtstr
= match first_token
.kind
{
65 token
::TokenKind
::Literal(token
::Lit
{
66 kind
: token
::LitKind
::Str
| token
::LitKind
::StrRaw(_
),
69 // If the first token is a string literal, then a format expression
70 // is constructed from it.
72 // This allows us to properly handle cases when the first comma
73 // after the format string is mistakenly replaced with any operator,
74 // which cause the expression parser to eat too much tokens.
75 p
.parse_literal_maybe_minus()?
78 // Otherwise, we fall back to the expression parser.
85 while p
.token
!= token
::Eof
{
86 if !p
.eat(&token
::Comma
) {
88 p
.clear_expected_tokens();
91 match p
.expect(&token
::Comma
) {
93 match token
::TokenKind
::Comma
.similar_tokens() {
94 Some(tks
) if tks
.contains(&p
.token
.kind
) => {
95 // If a similar token is found, then it may be a typo. We
96 // consider it as a comma, and continue parsing.
100 // Otherwise stop the parsing and return the error.
101 _
=> return Err(err
),
110 if p
.token
== token
::Eof
{
112 } // accept trailing commas
113 match p
.token
.ident() {
114 Some((ident
, _
)) if p
.look_ahead(1, |t
| *t
== token
::Eq
) => {
116 p
.expect(&token
::Eq
)?
;
117 let expr
= p
.parse_expr()?
;
118 if let Some((_
, prev
)) = args
.by_name(ident
.name
) {
121 &format
!("duplicate argument named `{}`", ident
),
123 .span_label(prev
.kind
.ident().unwrap().span
, "previously here")
124 .span_label(ident
.span
, "duplicate argument")
128 args
.add(FormatArgument { kind: FormatArgumentKind::Named(ident), expr }
);
131 let expr
= p
.parse_expr()?
;
132 if !args
.named_args().is_empty() {
133 let mut err
= ecx
.struct_span_err(
135 "positional arguments cannot follow named arguments",
139 "positional arguments must be before named arguments",
141 for arg
in args
.named_args() {
142 if let Some(name
) = arg
.kind
.ident() {
143 err
.span_label(name
.span
.to(arg
.expr
.span
), "named argument");
148 args
.add(FormatArgument { kind: FormatArgumentKind::Normal, expr }
);
155 pub fn make_format_args(
156 ecx
: &mut ExtCtxt
<'_
>,
158 mut args
: FormatArguments
,
159 append_newline
: bool
,
160 ) -> Result
<FormatArgs
, ()> {
161 let msg
= "format argument must be a string literal";
162 let unexpanded_fmt_span
= efmt
.span
;
163 let (fmt_str
, fmt_style
, fmt_span
) = match expr_to_spanned_string(ecx
, efmt
, msg
) {
164 Ok(mut fmt
) if append_newline
=> {
165 fmt
.0 = Symbol
::intern(&format
!("{}\n", fmt
.0));
170 if let Some((mut err
, suggested
)) = err
{
171 let sugg_fmt
= match args
.explicit_args().len() {
172 0 => "{}".to_string(),
173 _
=> format
!("{}{{}}", "{} ".repeat(args
.explicit_args().len())),
177 unexpanded_fmt_span
.shrink_to_lo(),
178 "you might be missing a string literal to format with",
179 format
!("\"{}\", ", sugg_fmt
),
180 Applicability
::MaybeIncorrect
,
189 let str_style
= match fmt_style
{
190 rustc_ast
::StrStyle
::Cooked
=> None
,
191 rustc_ast
::StrStyle
::Raw(raw
) => Some(raw
as usize),
194 let fmt_str
= fmt_str
.as_str(); // for the suggestions below
195 let fmt_snippet
= ecx
.source_map().span_to_snippet(unexpanded_fmt_span
).ok();
196 let mut parser
= parse
::Parser
::new(
201 parse
::ParseMode
::Format
,
204 let mut pieces
= Vec
::new();
205 while let Some(piece
) = parser
.next() {
206 if !parser
.errors
.is_empty() {
213 let is_literal
= parser
.is_literal
;
215 if !parser
.errors
.is_empty() {
216 let err
= parser
.errors
.remove(0);
217 let sp
= if is_literal
{
218 fmt_span
.from_inner(InnerSpan
::new(err
.span
.start
, err
.span
.end
))
220 // The format string could be another macro invocation, e.g.:
221 // format!(concat!("abc", "{}"), 4);
222 // However, `err.span` is an inner span relative to the *result* of
223 // the macro invocation, which is why we would get a nonsensical
224 // result calling `fmt_span.from_inner(err.span)` as above, and
225 // might even end up inside a multibyte character (issue #86085).
226 // Therefore, we conservatively report the error for the entire
227 // argument span here.
230 let mut e
= ecx
.struct_span_err(sp
, &format
!("invalid format string: {}", err
.description
));
231 e
.span_label(sp
, err
.label
+ " in format string");
232 if let Some(note
) = err
.note
{
235 if let Some((label
, span
)) = err
.secondary_label
&& is_literal
{
236 e
.span_label(fmt_span
.from_inner(InnerSpan
::new(span
.start
, span
.end
)), label
);
238 if err
.should_be_replaced_with_positional_argument
{
239 let captured_arg_span
=
240 fmt_span
.from_inner(InnerSpan
::new(err
.span
.start
, err
.span
.end
));
241 if let Ok(arg
) = ecx
.source_map().span_to_snippet(captured_arg_span
) {
242 let span
= match args
.unnamed_args().last() {
243 Some(arg
) => arg
.expr
.span
,
246 e
.multipart_suggestion_verbose(
247 "consider using a positional formatting argument instead",
249 (captured_arg_span
, args
.unnamed_args().len().to_string()),
250 (span
.shrink_to_hi(), format
!(", {}", arg
)),
252 Applicability
::MachineApplicable
,
260 let to_span
= |inner_span
: rustc_parse_format
::InnerSpan
| {
262 fmt_span
.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }
)
266 let mut used
= vec
![false; args
.explicit_args().len()];
267 let mut invalid_refs
= Vec
::new();
268 let mut numeric_refences_to_named_arg
= Vec
::new();
272 Name(&'a
str, Option
<Span
>),
276 let mut lookup_arg
= |arg
: ArgRef
<'_
>,
278 used_as
: PositionUsedAs
,
279 kind
: FormatArgPositionKind
|
280 -> FormatArgPosition
{
281 let index
= match arg
{
283 if let Some(arg
) = args
.by_index(index
) {
285 if arg
.kind
.ident().is_some() {
286 // This was a named argument, but it was used as a positional argument.
287 numeric_refences_to_named_arg
.push((index
, span
, used_as
));
291 // Doesn't exist as an explicit argument.
292 invalid_refs
.push((index
, span
, used_as
, kind
));
296 Name(name
, span
) => {
297 let name
= Symbol
::intern(name
);
298 if let Some((index
, _
)) = args
.by_name(name
) {
299 // Name found in `args`, so we resolve it to its index.
300 if index
< args
.explicit_args().len() {
301 // Mark it as used, if it was an explicit argument.
306 // Name not found in `args`, so we add it as an implicitly captured argument.
307 let span
= span
.unwrap_or(fmt_span
);
308 let ident
= Ident
::new(name
, span
);
309 let expr
= if is_literal
{
310 ecx
.expr_ident(span
, ident
)
312 // For the moment capturing variables from format strings expanded from macros is
313 // disabled (see RFC #2795)
314 ecx
.struct_span_err(span
, &format
!("there is no argument named `{name}`"))
315 .note(format
!("did you intend to capture a variable `{name}` from the surrounding scope?"))
316 .note("to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro")
318 DummyResult
::raw_expr(span
, true)
320 Ok(args
.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }
))
324 FormatArgPosition { index, kind, span }
327 let mut template
= Vec
::new();
328 let mut unfinished_literal
= String
::new();
329 let mut placeholder_index
= 0;
331 for piece
in pieces
{
333 parse
::Piece
::String(s
) => {
334 unfinished_literal
.push_str(s
);
336 parse
::Piece
::NextArgument(box parse
::Argument { position, position_span, format }
) => {
337 if !unfinished_literal
.is_empty() {
338 template
.push(FormatArgsPiece
::Literal(Symbol
::intern(&unfinished_literal
)));
339 unfinished_literal
.clear();
342 let span
= parser
.arg_places
.get(placeholder_index
).and_then(|&s
| to_span(s
));
343 placeholder_index
+= 1;
345 let position_span
= to_span(position_span
);
346 let argument
= match position
{
347 parse
::ArgumentImplicitlyIs(i
) => lookup_arg(
351 FormatArgPositionKind
::Implicit
,
353 parse
::ArgumentIs(i
) => lookup_arg(
357 FormatArgPositionKind
::Number
,
359 parse
::ArgumentNamed(name
) => lookup_arg(
360 Name(name
, position_span
),
363 FormatArgPositionKind
::Named
,
367 let alignment
= match format
.align
{
368 parse
::AlignUnknown
=> None
,
369 parse
::AlignLeft
=> Some(FormatAlignment
::Left
),
370 parse
::AlignRight
=> Some(FormatAlignment
::Right
),
371 parse
::AlignCenter
=> Some(FormatAlignment
::Center
),
374 let format_trait
= match format
.ty
{
375 "" => FormatTrait
::Display
,
376 "?" => FormatTrait
::Debug
,
377 "e" => FormatTrait
::LowerExp
,
378 "E" => FormatTrait
::UpperExp
,
379 "o" => FormatTrait
::Octal
,
380 "p" => FormatTrait
::Pointer
,
381 "b" => FormatTrait
::Binary
,
382 "x" => FormatTrait
::LowerHex
,
383 "X" => FormatTrait
::UpperHex
,
385 invalid_placeholder_type_error(ecx
, format
.ty
, format
.ty_span
, fmt_span
);
390 let precision_span
= format
.precision_span
.and_then(to_span
);
391 let precision
= match format
.precision
{
392 parse
::CountIs(n
) => Some(FormatCount
::Literal(n
)),
393 parse
::CountIsName(name
, name_span
) => Some(FormatCount
::Argument(lookup_arg(
394 Name(name
, to_span(name_span
)),
397 FormatArgPositionKind
::Named
,
399 parse
::CountIsParam(i
) => Some(FormatCount
::Argument(lookup_arg(
403 FormatArgPositionKind
::Number
,
405 parse
::CountIsStar(i
) => Some(FormatCount
::Argument(lookup_arg(
409 FormatArgPositionKind
::Implicit
,
411 parse
::CountImplied
=> None
,
414 let width_span
= format
.width_span
.and_then(to_span
);
415 let width
= match format
.width
{
416 parse
::CountIs(n
) => Some(FormatCount
::Literal(n
)),
417 parse
::CountIsName(name
, name_span
) => Some(FormatCount
::Argument(lookup_arg(
418 Name(name
, to_span(name_span
)),
421 FormatArgPositionKind
::Named
,
423 parse
::CountIsParam(i
) => Some(FormatCount
::Argument(lookup_arg(
427 FormatArgPositionKind
::Number
,
429 parse
::CountIsStar(_
) => unreachable
!(),
430 parse
::CountImplied
=> None
,
433 template
.push(FormatArgsPiece
::Placeholder(FormatPlaceholder
{
437 format_options
: FormatOptions
{
449 if !unfinished_literal
.is_empty() {
450 template
.push(FormatArgsPiece
::Literal(Symbol
::intern(&unfinished_literal
)));
453 if !invalid_refs
.is_empty() {
454 report_invalid_references(ecx
, &invalid_refs
, &template
, fmt_span
, &args
, parser
);
460 .filter(|&(_
, used
)| !used
)
462 let msg
= if let FormatArgumentKind
::Named(_
) = args
.explicit_args()[i
].kind
{
463 "named argument never used"
465 "argument never used"
467 (args
.explicit_args()[i
].expr
.span
, msg
)
469 .collect
::<Vec
<_
>>();
471 if !unused
.is_empty() {
472 // If there's a lot of unused arguments,
473 // let's check if this format arguments looks like another syntax (printf / shell).
474 let detect_foreign_fmt
= unused
.len() > args
.explicit_args().len() / 2;
475 report_missing_placeholders(ecx
, unused
, detect_foreign_fmt
, str_style
, fmt_str
, fmt_span
);
478 // Only check for unused named argument names if there are no other errors to avoid causing
479 // too much noise in output errors, such as when a named argument is entirely unused.
480 if invalid_refs
.is_empty() && ecx
.sess
.err_count() == 0 {
481 for &(index
, span
, used_as
) in &numeric_refences_to_named_arg
{
482 let (position_sp_to_replace
, position_sp_for_msg
) = match used_as
{
483 Placeholder(pspan
) => (span
, pspan
),
485 // Strip the leading `.` for precision.
486 let span
= span
.map(|span
| span
.with_lo(span
.lo() + BytePos(1)));
489 Width
=> (span
, span
),
491 let arg_name
= args
.explicit_args()[index
].kind
.ident().unwrap();
492 ecx
.buffered_early_lint
.push(BufferedEarlyLint
{
493 span
: arg_name
.span
.into(),
494 msg
: format
!("named argument `{}` is not used by name", arg_name
.name
).into(),
495 node_id
: rustc_ast
::CRATE_NODE_ID
,
496 lint_id
: LintId
::of(&NAMED_ARGUMENTS_USED_POSITIONALLY
),
497 diagnostic
: BuiltinLintDiagnostics
::NamedArgumentUsedPositionally
{
498 position_sp_to_replace
,
500 named_arg_sp
: arg_name
.span
,
501 named_arg_name
: arg_name
.name
.to_string(),
502 is_formatting_arg
: matches
!(used_as
, Width
| Precision
),
508 Ok(FormatArgs { span: fmt_span, template, arguments: args }
)
511 fn invalid_placeholder_type_error(
514 ty_span
: Option
<rustc_parse_format
::InnerSpan
>,
517 let sp
= ty_span
.map(|sp
| fmt_span
.from_inner(InnerSpan
::new(sp
.start
, sp
.end
)));
519 ecx
.struct_span_err(sp
.unwrap_or(fmt_span
), &format
!("unknown format trait `{}`", ty
));
521 "the only appropriate formatting traits are:\n\
522 - ``, which uses the `Display` trait\n\
523 - `?`, which uses the `Debug` trait\n\
524 - `e`, which uses the `LowerExp` trait\n\
525 - `E`, which uses the `UpperExp` trait\n\
526 - `o`, which uses the `Octal` trait\n\
527 - `p`, which uses the `Pointer` trait\n\
528 - `b`, which uses the `Binary` trait\n\
529 - `x`, which uses the `LowerHex` trait\n\
530 - `X`, which uses the `UpperHex` trait",
532 if let Some(sp
) = sp
{
533 for (fmt
, name
) in &[
544 err
.tool_only_span_suggestion(
546 &format
!("use the `{}` trait", name
),
548 Applicability
::MaybeIncorrect
,
555 fn report_missing_placeholders(
556 ecx
: &mut ExtCtxt
<'_
>,
557 unused
: Vec
<(Span
, &str)>,
558 detect_foreign_fmt
: bool
,
559 str_style
: Option
<usize>,
563 let mut diag
= if let &[(span
, msg
)] = &unused
[..] {
564 let mut diag
= ecx
.struct_span_err(span
, msg
);
565 diag
.span_label(span
, msg
);
568 let mut diag
= ecx
.struct_span_err(
569 unused
.iter().map(|&(sp
, _
)| sp
).collect
::<Vec
<Span
>>(),
570 "multiple unused formatting arguments",
572 diag
.span_label(fmt_span
, "multiple missing formatting specifiers");
573 for &(span
, msg
) in &unused
{
574 diag
.span_label(span
, msg
);
579 // Used to ensure we only report translations for *one* kind of foreign format.
580 let mut found_foreign
= false;
582 // Decide if we want to look for foreign formatting directives.
583 if detect_foreign_fmt
{
584 use super::format_foreign
as foreign
;
586 // The set of foreign substitutions we've explained. This prevents spamming the user
587 // with `%d should be written as {}` over and over again.
588 let mut explained
= FxHashSet
::default();
590 macro_rules
! check_foreign
{
592 let mut show_doc_note
= false;
594 let mut suggestions
= vec
![];
595 // account for `"` and account for raw strings `r#`
596 let padding
= str_style
.map(|i
| i
+ 2).unwrap_or(1);
597 for sub
in foreign
::$kind
::iter_subs(fmt_str
, padding
) {
598 let (trn
, success
) = match sub
.translate() {
599 Ok(trn
) => (trn
, true),
600 Err(Some(msg
)) => (msg
, false),
602 // If it has no translation, don't call it out specifically.
606 let pos
= sub
.position();
607 let sub
= String
::from(sub
.as_str());
608 if explained
.contains(&sub
) {
611 explained
.insert(sub
.clone());
614 found_foreign
= true;
615 show_doc_note
= true;
618 if let Some(inner_sp
) = pos
{
619 let sp
= fmt_span
.from_inner(inner_sp
);
622 suggestions
.push((sp
, trn
));
626 &format
!("format specifiers use curly braces, and {}", trn
),
631 diag
.help(&format
!("`{}` should be written as `{}`", sub
, trn
));
633 diag
.note(&format
!("`{}` should use curly braces, and {}", sub
, trn
));
641 " formatting is not supported; see the documentation for `std::fmt`",
644 if suggestions
.len() > 0 {
645 diag
.multipart_suggestion(
646 "format specifiers use curly braces",
648 Applicability
::MachineApplicable
,
654 check_foreign
!(printf
);
656 check_foreign
!(shell
);
659 if !found_foreign
&& unused
.len() == 1 {
660 diag
.span_label(fmt_span
, "formatting specifier missing");
666 /// Handle invalid references to positional arguments. Output different
667 /// errors for the case where all arguments are positional and for when
668 /// there are named arguments or numbered positional arguments in the
670 fn report_invalid_references(
671 ecx
: &mut ExtCtxt
<'_
>,
672 invalid_refs
: &[(usize, Option
<Span
>, PositionUsedAs
, FormatArgPositionKind
)],
673 template
: &[FormatArgsPiece
],
675 args
: &FormatArguments
,
676 parser
: parse
::Parser
<'_
>,
678 let num_args_desc
= match args
.explicit_args().len() {
679 0 => "no arguments were given".to_string(),
680 1 => "there is 1 argument".to_string(),
681 n
=> format
!("there are {} arguments", n
),
686 if template
.iter().all(|piece
| match piece
{
687 FormatArgsPiece
::Placeholder(FormatPlaceholder
{
688 argument
: FormatArgPosition { kind: FormatArgPositionKind::Number, .. }
,
691 FormatArgsPiece
::Placeholder(FormatPlaceholder
{
695 Some(FormatCount
::Argument(FormatArgPosition
{
696 kind
: FormatArgPositionKind
::Number
,
703 Some(FormatCount
::Argument(FormatArgPosition
{
704 kind
: FormatArgPositionKind
::Number
,
713 // There are no numeric positions.
714 // Collect all the implicit positions:
715 let mut spans
= Vec
::new();
716 let mut num_placeholders
= 0;
717 for piece
in template
{
718 let mut placeholder
= None
;
720 if let FormatArgsPiece
::Placeholder(FormatPlaceholder
{
724 Some(FormatCount
::Argument(FormatArgPosition
{
726 kind
: FormatArgPositionKind
::Implicit
,
735 num_placeholders
+= 1;
738 if let FormatArgsPiece
::Placeholder(FormatPlaceholder
{
739 argument
: FormatArgPosition { kind: FormatArgPositionKind::Implicit, .. }
,
745 num_placeholders
+= 1;
747 // For `{:.*}`, we only push one span.
748 spans
.extend(placeholder
);
750 let span
= if spans
.is_empty() {
751 MultiSpan
::from_span(fmt_span
)
753 MultiSpan
::from_spans(spans
)
755 e
= ecx
.struct_span_err(
758 "{} positional argument{} in format string, but {}",
760 pluralize
!(num_placeholders
),
764 for arg
in args
.explicit_args() {
765 e
.span_label(arg
.expr
.span
, "");
767 // Point out `{:.*}` placeholders: those take an extra argument.
768 let mut has_precision_star
= false;
769 for piece
in template
{
770 if let FormatArgsPiece
::Placeholder(FormatPlaceholder
{
774 Some(FormatCount
::Argument(FormatArgPosition
{
777 kind
: FormatArgPositionKind
::Implicit
,
785 let (Ok(index
) | Err(index
)) = index
;
786 has_precision_star
= true;
790 "this precision flag adds an extra required argument at position {}, which is why there {} expected",
792 if num_placeholders
== 1 {
793 "is 1 argument".to_string()
795 format
!("are {} arguments", num_placeholders
)
801 if has_precision_star
{
802 e
.note("positional arguments are zero-based");
805 let mut indexes
: Vec
<_
> = invalid_refs
.iter().map(|&(index
, _
, _
, _
)| index
).collect();
806 // Avoid `invalid reference to positional arguments 7 and 7 (there is 1 argument)`
807 // for `println!("{7:7$}", 1);`
810 let span
: MultiSpan
= if !parser
.is_literal
|| parser
.arg_places
.is_empty() {
811 MultiSpan
::from_span(fmt_span
)
813 MultiSpan
::from_spans(invalid_refs
.iter().filter_map(|&(_
, span
, _
, _
)| span
).collect())
815 let arg_list
= if let &[index
] = &indexes
[..] {
816 format
!("argument {index}")
818 let tail
= indexes
.pop().unwrap();
820 "arguments {head} and {tail}",
821 head
= indexes
.into_iter().map(|i
| i
.to_string()).collect
::<Vec
<_
>>().join(", ")
824 e
= ecx
.struct_span_err(
826 &format
!("invalid reference to positional {} ({})", arg_list
, num_args_desc
),
828 e
.note("positional arguments are zero-based");
831 if template
.iter().any(|piece
| match piece
{
832 FormatArgsPiece
::Placeholder(FormatPlaceholder { format_options: f, .. }
) => {
833 *f
!= FormatOptions
::default()
837 e
.note("for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html");
843 fn expand_format_args_impl
<'cx
>(
844 ecx
: &'cx
mut ExtCtxt
<'_
>,
848 ) -> Box
<dyn base
::MacResult
+ 'cx
> {
849 sp
= ecx
.with_def_site_ctxt(sp
);
850 match parse_args(ecx
, sp
, tts
) {
851 Ok((efmt
, args
)) => {
852 if let Ok(format_args
) = make_format_args(ecx
, efmt
, args
, nl
) {
853 MacEager
::expr(expand_parsed_format_args(ecx
, format_args
))
855 MacEager
::expr(DummyResult
::raw_expr(sp
, true))
865 pub fn expand_format_args
<'cx
>(
866 ecx
: &'cx
mut ExtCtxt
<'_
>,
869 ) -> Box
<dyn base
::MacResult
+ 'cx
> {
870 expand_format_args_impl(ecx
, sp
, tts
, false)
873 pub fn expand_format_args_nl
<'cx
>(
874 ecx
: &'cx
mut ExtCtxt
<'_
>,
877 ) -> Box
<dyn base
::MacResult
+ 'cx
> {
878 expand_format_args_impl(ecx
, sp
, tts
, true)