1use std::borrow::Cow;
2
3use rustc_ast::*;
4use rustc_data_structures::fx::FxIndexMap;
5use rustc_hir as hir;
6use rustc_session::config::FmtDebug;
7use rustc_span::{DesugaringKind, Ident, Span, Symbol, sym};
8
9use super::LoweringContext;
10
11impl<'hir> LoweringContext<'_, 'hir> {
12 pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
13 let allow_const = fmt.arguments.all_args().is_empty();
16 let mut fmt = Cow::Borrowed(fmt);
17
18 let sp = self.mark_span_with_reason(
19 DesugaringKind::FormatLiteral { source: fmt.is_source_literal },
20 sp,
21 sp.ctxt().outer_expn_data().allow_internal_unstable,
22 );
23
24 if self.tcx.sess.opts.unstable_opts.flatten_format_args {
25 fmt = flatten_format_args(fmt);
26 fmt = self.inline_literals(fmt);
27 }
28 expand_format_args(self, sp, &fmt, allow_const)
29 }
30
31 fn try_inline_lit(&self, lit: token::Lit) -> Option<Symbol> {
33 match LitKind::from_token_lit(lit) {
34 Ok(LitKind::Str(s, _)) => Some(s),
35 Ok(LitKind::Int(n, ty)) => {
36 match ty {
37 LitIntType::Unsuffixed => {
39 (n <= i32::MAX as u128).then_some(Symbol::intern(&n.to_string()))
40 }
41 LitIntType::Signed(int_ty) => {
42 let max_literal = self.int_ty_max(int_ty);
43 (n <= max_literal).then_some(Symbol::intern(&n.to_string()))
44 }
45 LitIntType::Unsigned(uint_ty) => {
46 let max_literal = self.uint_ty_max(uint_ty);
47 (n <= max_literal).then_some(Symbol::intern(&n.to_string()))
48 }
49 }
50 }
51 _ => None,
52 }
53 }
54
55 fn int_ty_max(&self, int_ty: IntTy) -> u128 {
57 match int_ty {
58 IntTy::Isize => self.tcx.data_layout.pointer_size.signed_int_max() as u128,
59 IntTy::I8 => i8::MAX as u128,
60 IntTy::I16 => i16::MAX as u128,
61 IntTy::I32 => i32::MAX as u128,
62 IntTy::I64 => i64::MAX as u128,
63 IntTy::I128 => i128::MAX as u128,
64 }
65 }
66
67 fn uint_ty_max(&self, uint_ty: UintTy) -> u128 {
69 match uint_ty {
70 UintTy::Usize => self.tcx.data_layout.pointer_size.unsigned_int_max(),
71 UintTy::U8 => u8::MAX as u128,
72 UintTy::U16 => u16::MAX as u128,
73 UintTy::U32 => u32::MAX as u128,
74 UintTy::U64 => u64::MAX as u128,
75 UintTy::U128 => u128::MAX as u128,
76 }
77 }
78
79 fn inline_literals<'fmt>(&self, mut fmt: Cow<'fmt, FormatArgs>) -> Cow<'fmt, FormatArgs> {
89 let mut was_inlined = vec![false; fmt.arguments.all_args().len()];
90 let mut inlined_anything = false;
91
92 for i in 0..fmt.template.len() {
93 let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
94 let Ok(arg_index) = placeholder.argument.index else { continue };
95
96 let mut literal = None;
97
98 if let FormatTrait::Display = placeholder.format_trait
99 && placeholder.format_options == Default::default()
100 && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
101 && let ExprKind::Lit(lit) = arg.kind
102 {
103 literal = self.try_inline_lit(lit);
104 }
105
106 if let Some(literal) = literal {
107 let fmt = fmt.to_mut();
110 fmt.template[i] = FormatArgsPiece::Literal(literal);
112 was_inlined[arg_index] = true;
113 inlined_anything = true;
114 }
115 }
116
117 if inlined_anything {
119 let fmt = fmt.to_mut();
120
121 let mut remove = was_inlined;
122
123 for_all_argument_indexes(&mut fmt.template, |index| remove[*index] = false);
125
126 let mut remove_it = remove.iter();
128 fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&true));
129
130 let index_map: Vec<usize> = remove
132 .into_iter()
133 .scan(0, |i, remove| {
134 let mapped = *i;
135 *i += !remove as usize;
136 Some(mapped)
137 })
138 .collect();
139
140 for_all_argument_indexes(&mut fmt.template, |index| *index = index_map[*index]);
142 }
143
144 fmt
145 }
146}
147
148fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
158 let mut i = 0;
159 while i < fmt.template.len() {
160 if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i]
161 && let FormatTrait::Display | FormatTrait::Debug = &placeholder.format_trait
162 && let Ok(arg_index) = placeholder.argument.index
163 && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
164 && let ExprKind::FormatArgs(_) = &arg.kind
165 && fmt.template.iter().enumerate().all(|(j, p)|
167 i == j ||
168 !matches!(p, FormatArgsPiece::Placeholder(placeholder)
169 if placeholder.argument.index == Ok(arg_index))
170 )
171 {
172 let fmt = fmt.to_mut();
175
176 let args = fmt.arguments.all_args_mut();
181 let remaining_args = args.split_off(arg_index + 1);
182 let old_arg_offset = args.len();
183 let mut fmt2 = &mut args.pop().unwrap().expr; let fmt2 = loop {
185 match &mut fmt2.kind {
187 ExprKind::Paren(inner) | ExprKind::AddrOf(BorrowKind::Ref, _, inner) => {
188 fmt2 = inner
189 }
190 ExprKind::FormatArgs(fmt2) => break fmt2,
191 _ => unreachable!(),
192 }
193 };
194
195 args.append(fmt2.arguments.all_args_mut());
196 let new_arg_offset = args.len();
197 args.extend(remaining_args);
198
199 for_all_argument_indexes(&mut fmt.template, |index| {
201 if *index >= old_arg_offset {
202 *index -= old_arg_offset;
203 *index += new_arg_offset;
204 }
205 });
206
207 let rest = fmt.template.split_off(i + 1);
210 fmt.template.pop(); for_all_argument_indexes(&mut fmt2.template, |index| *index += arg_index);
214 fmt.template.append(&mut fmt2.template);
215 fmt.template.extend(rest);
216
217 } else {
219 i += 1;
220 }
221 }
222 fmt
223}
224
225#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
226enum ArgumentType {
227 Format(FormatTrait),
228 Usize,
229}
230
231fn make_argument<'hir>(
239 ctx: &mut LoweringContext<'_, 'hir>,
240 sp: Span,
241 arg: &'hir hir::Expr<'hir>,
242 ty: ArgumentType,
243) -> hir::Expr<'hir> {
244 use ArgumentType::*;
245 use FormatTrait::*;
246 let new_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
247 sp,
248 hir::LangItem::FormatArgument,
249 match ty {
250 Format(Display) => sym::new_display,
251 Format(Debug) => match ctx.tcx.sess.opts.unstable_opts.fmt_debug {
252 FmtDebug::Full | FmtDebug::Shallow => sym::new_debug,
253 FmtDebug::None => sym::new_debug_noop,
254 },
255 Format(LowerExp) => sym::new_lower_exp,
256 Format(UpperExp) => sym::new_upper_exp,
257 Format(Octal) => sym::new_octal,
258 Format(Pointer) => sym::new_pointer,
259 Format(Binary) => sym::new_binary,
260 Format(LowerHex) => sym::new_lower_hex,
261 Format(UpperHex) => sym::new_upper_hex,
262 Usize => sym::from_usize,
263 },
264 ));
265 ctx.expr_call_mut(sp, new_fn, std::slice::from_ref(arg))
266}
267
268fn make_count<'hir>(
288 ctx: &mut LoweringContext<'_, 'hir>,
289 sp: Span,
290 count: &Option<FormatCount>,
291 argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
292) -> hir::Expr<'hir> {
293 match count {
294 Some(FormatCount::Literal(n)) => {
295 let count_is = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
296 sp,
297 hir::LangItem::FormatCount,
298 sym::Is,
299 ));
300 let value = ctx.arena.alloc_from_iter([ctx.expr_u16(sp, *n)]);
301 ctx.expr_call_mut(sp, count_is, value)
302 }
303 Some(FormatCount::Argument(arg)) => {
304 if let Ok(arg_index) = arg.index {
305 let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize), arg.span);
306 let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
307 sp,
308 hir::LangItem::FormatCount,
309 sym::Param,
310 ));
311 let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, i)]);
312 ctx.expr_call_mut(sp, count_param, value)
313 } else {
314 ctx.expr(
315 sp,
316 hir::ExprKind::Err(
317 ctx.dcx().span_delayed_bug(sp, "lowered bad format_args count"),
318 ),
319 )
320 }
321 }
322 None => ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatCount, sym::Implied),
323 }
324}
325
326fn make_format_spec<'hir>(
339 ctx: &mut LoweringContext<'_, 'hir>,
340 sp: Span,
341 placeholder: &FormatPlaceholder,
342 argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
343) -> hir::Expr<'hir> {
344 let position = match placeholder.argument.index {
345 Ok(arg_index) => {
346 let (i, _) = argmap.insert_full(
347 (arg_index, ArgumentType::Format(placeholder.format_trait)),
348 placeholder.span,
349 );
350 ctx.expr_usize(sp, i)
351 }
352 Err(_) => ctx.expr(
353 sp,
354 hir::ExprKind::Err(ctx.dcx().span_delayed_bug(sp, "lowered bad format_args count")),
355 ),
356 };
357 let &FormatOptions {
358 ref width,
359 ref precision,
360 alignment,
361 fill,
362 sign,
363 alternate,
364 zero_pad,
365 debug_hex,
366 } = &placeholder.format_options;
367 let fill = fill.unwrap_or(' ');
368 let align = match alignment {
370 Some(FormatAlignment::Left) => 0,
371 Some(FormatAlignment::Right) => 1,
372 Some(FormatAlignment::Center) => 2,
373 None => 3,
374 };
375 let flags: u32 = fill as u32
377 | ((sign == Some(FormatSign::Plus)) as u32) << 21
378 | ((sign == Some(FormatSign::Minus)) as u32) << 22
379 | (alternate as u32) << 23
380 | (zero_pad as u32) << 24
381 | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
382 | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
383 | (width.is_some() as u32) << 27
384 | (precision.is_some() as u32) << 28
385 | align << 29
386 | 1 << 31; let flags = ctx.expr_u32(sp, flags);
388 let precision = make_count(ctx, sp, precision, argmap);
389 let width = make_count(ctx, sp, width, argmap);
390 let position = ctx.expr_field(Ident::new(sym::position, sp), ctx.arena.alloc(position), sp);
391 let flags = ctx.expr_field(Ident::new(sym::flags, sp), ctx.arena.alloc(flags), sp);
392 let precision = ctx.expr_field(Ident::new(sym::precision, sp), ctx.arena.alloc(precision), sp);
393 let width = ctx.expr_field(Ident::new(sym::width, sp), ctx.arena.alloc(width), sp);
394 let placeholder = ctx.arena.alloc(hir::QPath::LangItem(hir::LangItem::FormatPlaceholder, sp));
395 let fields = ctx.arena.alloc_from_iter([position, flags, precision, width]);
396 ctx.expr(sp, hir::ExprKind::Struct(placeholder, fields, hir::StructTailExpr::None))
397}
398
399fn expand_format_args<'hir>(
400 ctx: &mut LoweringContext<'_, 'hir>,
401 macsp: Span,
402 fmt: &FormatArgs,
403 allow_const: bool,
404) -> hir::ExprKind<'hir> {
405 let mut incomplete_lit = String::new();
406 let lit_pieces =
407 ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| {
408 match piece {
409 &FormatArgsPiece::Literal(s) => {
410 if let Some(FormatArgsPiece::Literal(_)) = fmt.template.get(i + 1) {
412 incomplete_lit.push_str(s.as_str());
413 None
414 } else if !incomplete_lit.is_empty() {
415 incomplete_lit.push_str(s.as_str());
416 let s = Symbol::intern(&incomplete_lit);
417 incomplete_lit.clear();
418 Some(ctx.expr_str(fmt.span, s))
419 } else {
420 Some(ctx.expr_str(fmt.span, s))
421 }
422 }
423 &FormatArgsPiece::Placeholder(_) => {
424 if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
426 Some(ctx.expr_str(fmt.span, sym::empty))
427 } else {
428 None
429 }
430 }
431 }
432 }));
433 let lit_pieces = ctx.expr_array_ref(fmt.span, lit_pieces);
434
435 let mut use_format_options = false;
438
439 let mut argmap = FxIndexMap::default();
442 for piece in &fmt.template {
443 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
444 if placeholder.format_options != Default::default() {
445 use_format_options = true;
447 }
448 if let Ok(index) = placeholder.argument.index {
449 if argmap
450 .insert((index, ArgumentType::Format(placeholder.format_trait)), placeholder.span)
451 .is_some()
452 {
453 use_format_options = true;
456 }
457 }
458 }
459
460 let format_options = use_format_options.then(|| {
461 let elements = ctx.arena.alloc_from_iter(fmt.template.iter().filter_map(|piece| {
464 let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
465 Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
466 }));
467 ctx.expr_array_ref(macsp, elements)
468 });
469
470 let arguments = fmt.arguments.all_args();
471
472 if allow_const && arguments.is_empty() && argmap.is_empty() {
473 let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
476 macsp,
477 hir::LangItem::FormatArguments,
478 sym::new_const,
479 ));
480 let new_args = ctx.arena.alloc_from_iter([lit_pieces]);
481 return hir::ExprKind::Call(new, new_args);
482 }
483
484 let (let_statements, args) = if arguments.is_empty() {
485 (vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
488 } else if argmap.len() == 1 && arguments.len() == 1 {
489 let args = ctx.arena.alloc_from_iter(argmap.iter().map(
494 |(&(arg_index, ty), &placeholder_span)| {
495 let arg = &arguments[arg_index];
496 let placeholder_span =
497 placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
498 let arg = ctx.lower_expr(&arg.expr);
499 let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
500 make_argument(ctx, placeholder_span, ref_arg, ty)
501 },
502 ));
503 let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
504 let args_ident = Ident::new(sym::args, macsp);
505 let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
506 let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
507 (vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
508 } else {
509 let args_ident = Ident::new(sym::args, macsp);
512 let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
513 let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
514 let arg_expr = ctx.lower_expr(&arg.expr);
515 ctx.expr(
516 arg.expr.span.with_ctxt(macsp.ctxt()),
517 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
518 )
519 }));
520 let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
521 let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));
522
523 let args = ctx.arena.alloc_from_iter(argmap.iter().map(
531 |(&(arg_index, ty), &placeholder_span)| {
532 let arg = &arguments[arg_index];
533 let placeholder_span =
534 placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
535 let arg_span = match arg.kind {
536 FormatArgumentKind::Captured(_) => placeholder_span,
537 _ => arg.expr.span.with_ctxt(macsp.ctxt()),
538 };
539 let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
540 let arg = ctx.arena.alloc(ctx.expr(
541 arg_span,
542 hir::ExprKind::Field(
543 args_ident_expr,
544 Ident::new(sym::integer(arg_index), macsp),
545 ),
546 ));
547 make_argument(ctx, placeholder_span, arg, ty)
548 },
549 ));
550 let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
551 let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
552 let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
553 (
554 vec![let_statement_1, let_statement_2],
555 ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
556 )
557 };
558
559 let args = ctx.expr_ref(macsp, args);
562
563 let call = if let Some(format_options) = format_options {
564 let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
573 macsp,
574 hir::LangItem::FormatArguments,
575 sym::new_v1_formatted,
576 ));
577 let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options]);
578 let call = ctx.expr_call(macsp, new_v1_formatted, args);
579 let hir_id = ctx.next_id();
580 hir::ExprKind::Block(
581 ctx.arena.alloc(hir::Block {
582 stmts: &[],
583 expr: Some(call),
584 hir_id,
585 rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
586 span: macsp,
587 targeted_by_break: false,
588 }),
589 None,
590 )
591 } else {
592 let new_v1 = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
598 macsp,
599 hir::LangItem::FormatArguments,
600 sym::new_v1,
601 ));
602 let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
603 hir::ExprKind::Call(new_v1, new_args)
604 };
605
606 if !let_statements.is_empty() {
607 let call = ctx.arena.alloc(ctx.expr(macsp, call));
614 let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
615 hir::ExprKind::Block(block, None)
616 } else {
617 call
618 }
619}
620
621fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
622 for piece in template {
623 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
624 if let Ok(index) = &mut placeholder.argument.index {
625 f(index);
626 }
627 if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) =
628 &mut placeholder.format_options.width
629 {
630 f(index);
631 }
632 if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) =
633 &mut placeholder.format_options.precision
634 {
635 f(index);
636 }
637 }
638}