Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit a769b30

Browse files
committed
Flatten nested format_args!() into one.
1 parent 18e305d commit a769b30

File tree

2 files changed

+102
-4
lines changed

2 files changed

+102
-4
lines changed

compiler/rustc_ast/src/format.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ impl FormatArguments {
131131
&self.arguments[..]
132132
}
133133

134-
pub fn all_args_mut(&mut self) -> &mut [FormatArgument] {
135-
&mut self.arguments[..]
134+
pub fn all_args_mut(&mut self) -> &mut Vec<FormatArgument> {
135+
&mut self.arguments
136136
}
137137
}
138138

compiler/rustc_ast_lowering/src/format.rs

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,113 @@ use rustc_hir as hir;
77
use rustc_span::{
88
sym,
99
symbol::{kw, Ident},
10-
Span,
10+
Span, Symbol,
1111
};
12+
use std::borrow::Cow;
1213

1314
impl<'hir> LoweringContext<'_, 'hir> {
1415
pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
15-
expand_format_args(self, sp, fmt)
16+
let fmt = flatten_format_args(fmt);
17+
expand_format_args(self, sp, &fmt)
1618
}
1719
}
1820

21+
/// Flattens nested `format_args!()` into one.
22+
///
23+
/// Turns
24+
///
25+
/// `format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3)`
26+
///
27+
/// into
28+
///
29+
/// `format_args!("a {} b{}! {}.", 1, 2, 3)`.
30+
fn flatten_format_args(fmt: &FormatArgs) -> Cow<'_, FormatArgs> {
31+
let mut fmt = Cow::Borrowed(fmt);
32+
let mut i = 0;
33+
while i < fmt.template.len() {
34+
if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i]
35+
&& let FormatTrait::Display | FormatTrait::Debug = &placeholder.format_trait
36+
&& let Ok(arg_index) = placeholder.argument.index
37+
&& let arg = &fmt.arguments.all_args()[arg_index].expr
38+
&& let ExprKind::FormatArgs(_) = &arg.kind
39+
// Check that this argument is not used by any other placeholders.
40+
&& fmt.template.iter().enumerate().all(|(j, p)|
41+
i == j ||
42+
!matches!(p, FormatArgsPiece::Placeholder(placeholder)
43+
if placeholder.argument.index == Ok(arg_index))
44+
)
45+
{
46+
// Now we need to mutate the outer FormatArgs.
47+
// If this is the first time, this clones the outer FormatArgs.
48+
let fmt = fmt.to_mut();
49+
50+
// Take the inner FormatArgs out of the outer arguments, and
51+
// replace it by the inner arguments. (We can't just put those at
52+
// the end, because we need to preserve the order of evaluation.)
53+
54+
let args = fmt.arguments.all_args_mut();
55+
let remaining_args = args.split_off(arg_index + 1);
56+
let old_arg_offset = args.len();
57+
let fmt2 = args.pop().unwrap().expr.into_inner(); // The inner FormatArgs.
58+
let ExprKind::FormatArgs(fmt2) = fmt2.kind else { unreachable!() };
59+
let mut fmt2 = fmt2.into_inner();
60+
61+
args.append(fmt2.arguments.all_args_mut());
62+
let new_arg_offset = args.len();
63+
args.extend(remaining_args);
64+
65+
// Correct the indexes that refer to the arguments after the newly inserted arguments.
66+
for piece in &mut fmt.template {
67+
if let FormatArgsPiece::Placeholder(placeholder) = piece
68+
&& let Ok(index) = &mut placeholder.argument.index
69+
&& *index >= old_arg_offset
70+
{
71+
*index -= old_arg_offset;
72+
*index += new_arg_offset;
73+
}
74+
}
75+
76+
// Now merge the placeholders:
77+
78+
let mut rest = fmt.template.split_off(i + 1);
79+
fmt.template.pop(); // remove the placeholder for the nested fmt args.
80+
81+
// Coalesce adjacent literals.
82+
if let Some(FormatArgsPiece::Literal(s1)) = fmt.template.last() &&
83+
let Some(FormatArgsPiece::Literal(s2)) = fmt2.template.first_mut()
84+
{
85+
*s2 = Symbol::intern(&(s1.as_str().to_owned() + s2.as_str()));
86+
fmt.template.pop();
87+
}
88+
if let Some(FormatArgsPiece::Literal(s1)) = fmt2.template.last() &&
89+
let Some(FormatArgsPiece::Literal(s2)) = rest.first_mut()
90+
{
91+
*s2 = Symbol::intern(&(s1.as_str().to_owned() + s2.as_str()));
92+
fmt2.template.pop();
93+
}
94+
95+
for piece in fmt2.template {
96+
match piece {
97+
FormatArgsPiece::Literal(s) => fmt.template.push(FormatArgsPiece::Literal(s)),
98+
FormatArgsPiece::Placeholder(mut p) => {
99+
// Correct the index to refer to the right place into the outer argument list.
100+
if let Ok(n) = &mut p.argument.index {
101+
*n += arg_index;
102+
}
103+
fmt.template.push(FormatArgsPiece::Placeholder(p));
104+
}
105+
}
106+
}
107+
fmt.template.extend(rest);
108+
109+
// Don't increment `i` here, so we recurse into the newly added pieces.
110+
} else {
111+
i += 1;
112+
}
113+
}
114+
fmt
115+
}
116+
19117
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
20118
enum ArgumentType {
21119
Format(FormatTrait),

0 commit comments

Comments
 (0)