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