• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::LoweringContext;
2 use rustc_ast as ast;
3 use rustc_ast::visit::{self, Visitor};
4 use rustc_ast::*;
5 use rustc_data_structures::fx::FxIndexMap;
6 use rustc_hir as hir;
7 use rustc_span::{
8     sym,
9     symbol::{kw, Ident},
10     Span, Symbol,
11 };
12 use std::borrow::Cow;
13 
14 impl<'hir> LoweringContext<'_, 'hir> {
lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir>15     pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
16         // Never call the const constructor of `fmt::Arguments` if the
17         // format_args!() had any arguments _before_ flattening/inlining.
18         let allow_const = fmt.arguments.all_args().is_empty();
19         let mut fmt = Cow::Borrowed(fmt);
20         if self.tcx.sess.opts.unstable_opts.flatten_format_args {
21             fmt = flatten_format_args(fmt);
22             fmt = inline_literals(fmt);
23         }
24         expand_format_args(self, sp, &fmt, allow_const)
25     }
26 }
27 
28 /// Flattens nested `format_args!()` into one.
29 ///
30 /// Turns
31 ///
32 /// `format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3)`
33 ///
34 /// into
35 ///
36 /// `format_args!("a {} b{}! {}.", 1, 2, 3)`.
flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs>37 fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
38     let mut i = 0;
39     while i < fmt.template.len() {
40         if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i]
41             && let FormatTrait::Display | FormatTrait::Debug = &placeholder.format_trait
42             && let Ok(arg_index) = placeholder.argument.index
43             && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
44             && let ExprKind::FormatArgs(_) = &arg.kind
45             // Check that this argument is not used by any other placeholders.
46             && fmt.template.iter().enumerate().all(|(j, p)|
47                 i == j ||
48                 !matches!(p, FormatArgsPiece::Placeholder(placeholder)
49                     if placeholder.argument.index == Ok(arg_index))
50             )
51         {
52             // Now we need to mutate the outer FormatArgs.
53             // If this is the first time, this clones the outer FormatArgs.
54             let fmt = fmt.to_mut();
55 
56             // Take the inner FormatArgs out of the outer arguments, and
57             // replace it by the inner arguments. (We can't just put those at
58             // the end, because we need to preserve the order of evaluation.)
59 
60             let args = fmt.arguments.all_args_mut();
61             let remaining_args = args.split_off(arg_index + 1);
62             let old_arg_offset = args.len();
63             let mut fmt2 = &mut args.pop().unwrap().expr; // The inner FormatArgs.
64             let fmt2 = loop { // Unwrap the Expr to get to the FormatArgs.
65                 match &mut fmt2.kind {
66                     ExprKind::Paren(inner) | ExprKind::AddrOf(BorrowKind::Ref, _, inner) => fmt2 = inner,
67                     ExprKind::FormatArgs(fmt2) => break fmt2,
68                     _ => unreachable!(),
69                 }
70             };
71 
72             args.append(fmt2.arguments.all_args_mut());
73             let new_arg_offset = args.len();
74             args.extend(remaining_args);
75 
76             // Correct the indexes that refer to the arguments after the newly inserted arguments.
77             for_all_argument_indexes(&mut fmt.template, |index| {
78                 if *index >= old_arg_offset {
79                     *index -= old_arg_offset;
80                     *index += new_arg_offset;
81                 }
82             });
83 
84             // Now merge the placeholders:
85 
86             let rest = fmt.template.split_off(i + 1);
87             fmt.template.pop(); // remove the placeholder for the nested fmt args.
88             // Insert the pieces from the nested format args, but correct any
89             // placeholders to point to the correct argument index.
90             for_all_argument_indexes(&mut fmt2.template, |index| *index += arg_index);
91             fmt.template.append(&mut fmt2.template);
92             fmt.template.extend(rest);
93 
94             // Don't increment `i` here, so we recurse into the newly added pieces.
95         } else {
96             i += 1;
97         }
98     }
99     fmt
100 }
101 
102 /// Inline literals into the format string.
103 ///
104 /// Turns
105 ///
106 /// `format_args!("Hello, {}! {} {}", "World", 123, x)`
107 ///
108 /// into
109 ///
110 /// `format_args!("Hello, World! 123 {}", x)`.
inline_literals(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs>111 fn inline_literals(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
112     let mut was_inlined = vec![false; fmt.arguments.all_args().len()];
113     let mut inlined_anything = false;
114 
115     for i in 0..fmt.template.len() {
116         let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
117         let Ok(arg_index) = placeholder.argument.index else { continue };
118 
119         let mut literal = None;
120 
121         if let FormatTrait::Display = placeholder.format_trait
122             && placeholder.format_options == Default::default()
123             && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
124             && let ExprKind::Lit(lit) = arg.kind
125         {
126             if let token::LitKind::Str | token::LitKind::StrRaw(_) = lit.kind
127                 && let Ok(LitKind::Str(s, _)) = LitKind::from_token_lit(lit)
128             {
129                 literal = Some(s);
130             } else if let token::LitKind::Integer = lit.kind
131                 && let Ok(LitKind::Int(n, _)) = LitKind::from_token_lit(lit)
132             {
133                 literal = Some(Symbol::intern(&n.to_string()));
134             }
135         }
136 
137         if let Some(literal) = literal {
138             // Now we need to mutate the outer FormatArgs.
139             // If this is the first time, this clones the outer FormatArgs.
140             let fmt = fmt.to_mut();
141             // Replace the placeholder with the literal.
142             fmt.template[i] = FormatArgsPiece::Literal(literal);
143             was_inlined[arg_index] = true;
144             inlined_anything = true;
145         }
146     }
147 
148     // Remove the arguments that were inlined.
149     if inlined_anything {
150         let fmt = fmt.to_mut();
151 
152         let mut remove = was_inlined;
153 
154         // Don't remove anything that's still used.
155         for_all_argument_indexes(&mut fmt.template, |index| remove[*index] = false);
156 
157         // Drop all the arguments that are marked for removal.
158         let mut remove_it = remove.iter();
159         fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&true));
160 
161         // Calculate the mapping of old to new indexes for the remaining arguments.
162         let index_map: Vec<usize> = remove
163             .into_iter()
164             .scan(0, |i, remove| {
165                 let mapped = *i;
166                 *i += !remove as usize;
167                 Some(mapped)
168             })
169             .collect();
170 
171         // Correct the indexes that refer to arguments that have shifted position.
172         for_all_argument_indexes(&mut fmt.template, |index| *index = index_map[*index]);
173     }
174 
175     fmt
176 }
177 
178 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
179 enum ArgumentType {
180     Format(FormatTrait),
181     Usize,
182 }
183 
184 /// Generate a hir expression representing an argument to a format_args invocation.
185 ///
186 /// Generates:
187 ///
188 /// ```text
189 ///     <core::fmt::Argument>::new_…(arg)
190 /// ```
make_argument<'hir>( ctx: &mut LoweringContext<'_, 'hir>, sp: Span, arg: &'hir hir::Expr<'hir>, ty: ArgumentType, ) -> hir::Expr<'hir>191 fn make_argument<'hir>(
192     ctx: &mut LoweringContext<'_, 'hir>,
193     sp: Span,
194     arg: &'hir hir::Expr<'hir>,
195     ty: ArgumentType,
196 ) -> hir::Expr<'hir> {
197     use ArgumentType::*;
198     use FormatTrait::*;
199     let new_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
200         sp,
201         hir::LangItem::FormatArgument,
202         match ty {
203             Format(Display) => sym::new_display,
204             Format(Debug) => sym::new_debug,
205             Format(LowerExp) => sym::new_lower_exp,
206             Format(UpperExp) => sym::new_upper_exp,
207             Format(Octal) => sym::new_octal,
208             Format(Pointer) => sym::new_pointer,
209             Format(Binary) => sym::new_binary,
210             Format(LowerHex) => sym::new_lower_hex,
211             Format(UpperHex) => sym::new_upper_hex,
212             Usize => sym::from_usize,
213         },
214     ));
215     ctx.expr_call_mut(sp, new_fn, std::slice::from_ref(arg))
216 }
217 
218 /// Generate a hir expression for a format_args Count.
219 ///
220 /// Generates:
221 ///
222 /// ```text
223 ///     <core::fmt::rt::Count>::Is(…)
224 /// ```
225 ///
226 /// or
227 ///
228 /// ```text
229 ///     <core::fmt::rt::Count>::Param(…)
230 /// ```
231 ///
232 /// or
233 ///
234 /// ```text
235 ///     <core::fmt::rt::Count>::Implied
236 /// ```
make_count<'hir>( ctx: &mut LoweringContext<'_, 'hir>, sp: Span, count: &Option<FormatCount>, argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>, ) -> hir::Expr<'hir>237 fn make_count<'hir>(
238     ctx: &mut LoweringContext<'_, 'hir>,
239     sp: Span,
240     count: &Option<FormatCount>,
241     argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
242 ) -> hir::Expr<'hir> {
243     match count {
244         Some(FormatCount::Literal(n)) => {
245             let count_is = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
246                 sp,
247                 hir::LangItem::FormatCount,
248                 sym::Is,
249             ));
250             let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]);
251             ctx.expr_call_mut(sp, count_is, value)
252         }
253         Some(FormatCount::Argument(arg)) => {
254             if let Ok(arg_index) = arg.index {
255                 let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize), arg.span);
256                 let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
257                     sp,
258                     hir::LangItem::FormatCount,
259                     sym::Param,
260                 ));
261                 let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, i)]);
262                 ctx.expr_call_mut(sp, count_param, value)
263             } else {
264                 ctx.expr(
265                     sp,
266                     hir::ExprKind::Err(
267                         ctx.tcx.sess.delay_span_bug(sp, "lowered bad format_args count"),
268                     ),
269                 )
270             }
271         }
272         None => ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatCount, sym::Implied),
273     }
274 }
275 
276 /// Generate a hir expression for a format_args placeholder specification.
277 ///
278 /// Generates
279 ///
280 /// ```text
281 ///     <core::fmt::rt::Placeholder::new(
282 ///         …usize, // position
283 ///         '…', // fill
284 ///         <core::fmt::rt::Alignment>::…, // alignment
285 ///         …u32, // flags
286 ///         <core::fmt::rt::Count::…>, // width
287 ///         <core::fmt::rt::Count::…>, // precision
288 ///     )
289 /// ```
make_format_spec<'hir>( ctx: &mut LoweringContext<'_, 'hir>, sp: Span, placeholder: &FormatPlaceholder, argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>, ) -> hir::Expr<'hir>290 fn make_format_spec<'hir>(
291     ctx: &mut LoweringContext<'_, 'hir>,
292     sp: Span,
293     placeholder: &FormatPlaceholder,
294     argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
295 ) -> hir::Expr<'hir> {
296     let position = match placeholder.argument.index {
297         Ok(arg_index) => {
298             let (i, _) = argmap.insert_full(
299                 (arg_index, ArgumentType::Format(placeholder.format_trait)),
300                 placeholder.span,
301             );
302             ctx.expr_usize(sp, i)
303         }
304         Err(_) => ctx.expr(
305             sp,
306             hir::ExprKind::Err(ctx.tcx.sess.delay_span_bug(sp, "lowered bad format_args count")),
307         ),
308     };
309     let &FormatOptions {
310         ref width,
311         ref precision,
312         alignment,
313         fill,
314         sign,
315         alternate,
316         zero_pad,
317         debug_hex,
318     } = &placeholder.format_options;
319     let fill = ctx.expr_char(sp, fill.unwrap_or(' '));
320     let align = ctx.expr_lang_item_type_relative(
321         sp,
322         hir::LangItem::FormatAlignment,
323         match alignment {
324             Some(FormatAlignment::Left) => sym::Left,
325             Some(FormatAlignment::Right) => sym::Right,
326             Some(FormatAlignment::Center) => sym::Center,
327             None => sym::Unknown,
328         },
329     );
330     // This needs to match `Flag` in library/core/src/fmt/rt.rs.
331     let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
332         | ((sign == Some(FormatSign::Minus)) as u32) << 1
333         | (alternate as u32) << 2
334         | (zero_pad as u32) << 3
335         | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
336         | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
337     let flags = ctx.expr_u32(sp, flags);
338     let precision = make_count(ctx, sp, &precision, argmap);
339     let width = make_count(ctx, sp, &width, argmap);
340     let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
341         sp,
342         hir::LangItem::FormatPlaceholder,
343         sym::new,
344     ));
345     let args = ctx.arena.alloc_from_iter([position, fill, align, flags, precision, width]);
346     ctx.expr_call_mut(sp, format_placeholder_new, args)
347 }
348 
expand_format_args<'hir>( ctx: &mut LoweringContext<'_, 'hir>, macsp: Span, fmt: &FormatArgs, allow_const: bool, ) -> hir::ExprKind<'hir>349 fn expand_format_args<'hir>(
350     ctx: &mut LoweringContext<'_, 'hir>,
351     macsp: Span,
352     fmt: &FormatArgs,
353     allow_const: bool,
354 ) -> hir::ExprKind<'hir> {
355     let mut incomplete_lit = String::new();
356     let lit_pieces =
357         ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| {
358             match piece {
359                 &FormatArgsPiece::Literal(s) => {
360                     // Coalesce adjacent literal pieces.
361                     if let Some(FormatArgsPiece::Literal(_)) = fmt.template.get(i + 1) {
362                         incomplete_lit.push_str(s.as_str());
363                         None
364                     } else if !incomplete_lit.is_empty() {
365                         incomplete_lit.push_str(s.as_str());
366                         let s = Symbol::intern(&incomplete_lit);
367                         incomplete_lit.clear();
368                         Some(ctx.expr_str(fmt.span, s))
369                     } else {
370                         Some(ctx.expr_str(fmt.span, s))
371                     }
372                 }
373                 &FormatArgsPiece::Placeholder(_) => {
374                     // Inject empty string before placeholders when not already preceded by a literal piece.
375                     if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
376                         Some(ctx.expr_str(fmt.span, kw::Empty))
377                     } else {
378                         None
379                     }
380                 }
381             }
382         }));
383     let lit_pieces = ctx.expr_array_ref(fmt.span, lit_pieces);
384 
385     // Whether we'll use the `Arguments::new_v1_formatted` form (true),
386     // or the `Arguments::new_v1` form (false).
387     let mut use_format_options = false;
388 
389     // Create a list of all _unique_ (argument, format trait) combinations.
390     // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
391     let mut argmap = FxIndexMap::default();
392     for piece in &fmt.template {
393         let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
394         if placeholder.format_options != Default::default() {
395             // Can't use basic form if there's any formatting options.
396             use_format_options = true;
397         }
398         if let Ok(index) = placeholder.argument.index {
399             if argmap
400                 .insert((index, ArgumentType::Format(placeholder.format_trait)), placeholder.span)
401                 .is_some()
402             {
403                 // Duplicate (argument, format trait) combination,
404                 // which we'll only put once in the args array.
405                 use_format_options = true;
406             }
407         }
408     }
409 
410     let format_options = use_format_options.then(|| {
411         // Generate:
412         //     &[format_spec_0, format_spec_1, format_spec_2]
413         let elements: Vec<_> = fmt
414             .template
415             .iter()
416             .filter_map(|piece| {
417                 let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
418                 Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
419             })
420             .collect();
421         ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
422     });
423 
424     let arguments = fmt.arguments.all_args();
425 
426     if allow_const && arguments.is_empty() && argmap.is_empty() {
427         // Generate:
428         //     <core::fmt::Arguments>::new_const(lit_pieces)
429         let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
430             macsp,
431             hir::LangItem::FormatArguments,
432             sym::new_const,
433         ));
434         let new_args = ctx.arena.alloc_from_iter([lit_pieces]);
435         return hir::ExprKind::Call(new, new_args);
436     }
437 
438     // If the args array contains exactly all the original arguments once,
439     // in order, we can use a simple array instead of a `match` construction.
440     // However, if there's a yield point in any argument except the first one,
441     // we don't do this, because an Argument cannot be kept across yield points.
442     //
443     // This is an optimization, speeding up compilation about 1-2% in some cases.
444     // See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
445     let use_simple_array = argmap.len() == arguments.len()
446         && argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
447         && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
448 
449     let args = if arguments.is_empty() {
450         // Generate:
451         //    &<core::fmt::Argument>::none()
452         //
453         // Note:
454         //     `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
455         //
456         //     This makes sure that this still fails to compile, even when the argument is inlined:
457         //
458         //     ```
459         //     let f = format_args!("{}", "a");
460         //     println!("{f}"); // error E0716
461         //     ```
462         //
463         //     Cases where keeping the object around is allowed, such as `format_args!("a")`,
464         //     are handled above by the `allow_const` case.
465         let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
466             macsp,
467             hir::LangItem::FormatArgument,
468             sym::none,
469         ));
470         let none = ctx.expr_call(macsp, none_fn, &[]);
471         ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
472     } else if use_simple_array {
473         // Generate:
474         //     &[
475         //         <core::fmt::Argument>::new_display(&arg0),
476         //         <core::fmt::Argument>::new_lower_hex(&arg1),
477         //         <core::fmt::Argument>::new_debug(&arg2),
478         //         …
479         //     ]
480         let elements: Vec<_> = arguments
481             .iter()
482             .zip(argmap)
483             .map(|(arg, ((_, ty), placeholder_span))| {
484                 let placeholder_span =
485                     placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
486                 let arg_span = match arg.kind {
487                     FormatArgumentKind::Captured(_) => placeholder_span,
488                     _ => arg.expr.span.with_ctxt(macsp.ctxt()),
489                 };
490                 let arg = ctx.lower_expr(&arg.expr);
491                 let ref_arg = ctx.arena.alloc(ctx.expr(
492                     arg_span,
493                     hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
494                 ));
495                 make_argument(ctx, placeholder_span, ref_arg, ty)
496             })
497             .collect();
498         ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
499     } else {
500         // Generate:
501         //     &match (&arg0, &arg1, &…) {
502         //         args => [
503         //             <core::fmt::Argument>::new_display(args.0),
504         //             <core::fmt::Argument>::new_lower_hex(args.1),
505         //             <core::fmt::Argument>::new_debug(args.0),
506         //             …
507         //         ]
508         //     }
509         let args_ident = Ident::new(sym::args, macsp);
510         let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
511         let args = ctx.arena.alloc_from_iter(argmap.iter().map(
512             |(&(arg_index, ty), &placeholder_span)| {
513                 let arg = &arguments[arg_index];
514                 let placeholder_span =
515                     placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
516                 let arg_span = match arg.kind {
517                     FormatArgumentKind::Captured(_) => placeholder_span,
518                     _ => arg.expr.span.with_ctxt(macsp.ctxt()),
519                 };
520                 let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
521                 let arg = ctx.arena.alloc(ctx.expr(
522                     arg_span,
523                     hir::ExprKind::Field(
524                         args_ident_expr,
525                         Ident::new(sym::integer(arg_index), macsp),
526                     ),
527                 ));
528                 make_argument(ctx, placeholder_span, arg, ty)
529             },
530         ));
531         let elements: Vec<_> = arguments
532             .iter()
533             .map(|arg| {
534                 let arg_expr = ctx.lower_expr(&arg.expr);
535                 ctx.expr(
536                     arg.expr.span.with_ctxt(macsp.ctxt()),
537                     hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
538                 )
539             })
540             .collect();
541         let args_tuple = ctx
542             .arena
543             .alloc(ctx.expr(macsp, hir::ExprKind::Tup(ctx.arena.alloc_from_iter(elements))));
544         let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
545         let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
546         let match_expr = ctx.arena.alloc(ctx.expr_match(
547             macsp,
548             args_tuple,
549             match_arms,
550             hir::MatchSource::FormatArgs,
551         ));
552         ctx.expr(
553             macsp,
554             hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
555         )
556     };
557 
558     if let Some(format_options) = format_options {
559         // Generate:
560         //     <core::fmt::Arguments>::new_v1_formatted(
561         //         lit_pieces,
562         //         args,
563         //         format_options,
564         //         unsafe { ::core::fmt::UnsafeArg::new() }
565         //     )
566         let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
567             macsp,
568             hir::LangItem::FormatArguments,
569             sym::new_v1_formatted,
570         ));
571         let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
572             macsp,
573             hir::LangItem::FormatUnsafeArg,
574             sym::new,
575         ));
576         let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
577         let hir_id = ctx.next_id();
578         let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
579             stmts: &[],
580             expr: Some(unsafe_arg_new_call),
581             hir_id,
582             rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
583             span: macsp,
584             targeted_by_break: false,
585         }));
586         let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
587         hir::ExprKind::Call(new_v1_formatted, args)
588     } else {
589         // Generate:
590         //     <core::fmt::Arguments>::new_v1(
591         //         lit_pieces,
592         //         args,
593         //     )
594         let new_v1 = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
595             macsp,
596             hir::LangItem::FormatArguments,
597             sym::new_v1,
598         ));
599         let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
600         hir::ExprKind::Call(new_v1, new_args)
601     }
602 }
603 
may_contain_yield_point(e: &ast::Expr) -> bool604 fn may_contain_yield_point(e: &ast::Expr) -> bool {
605     struct MayContainYieldPoint(bool);
606 
607     impl Visitor<'_> for MayContainYieldPoint {
608         fn visit_expr(&mut self, e: &ast::Expr) {
609             if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
610                 self.0 = true;
611             } else {
612                 visit::walk_expr(self, e);
613             }
614         }
615 
616         fn visit_mac_call(&mut self, _: &ast::MacCall) {
617             // Macros should be expanded at this point.
618             unreachable!("unexpanded macro in ast lowering");
619         }
620 
621         fn visit_item(&mut self, _: &ast::Item) {
622             // Do not recurse into nested items.
623         }
624     }
625 
626     let mut visitor = MayContainYieldPoint(false);
627     visitor.visit_expr(e);
628     visitor.0
629 }
630 
for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize))631 fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
632     for piece in template {
633         let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
634         if let Ok(index) = &mut placeholder.argument.index {
635             f(index);
636         }
637         if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) =
638             &mut placeholder.format_options.width
639         {
640             f(index);
641         }
642         if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) =
643             &mut placeholder.format_options.precision
644         {
645             f(index);
646         }
647     }
648 }
649