Skip to content

Commit 85ef2f0

Browse files
committed
Inline string literals into format_args!().
1 parent 94ad7e8 commit 85ef2f0

File tree

1 file changed

+61
-3
lines changed

1 file changed

+61
-3
lines changed

compiler/rustc_ast_lowering/src/format.rs

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use std::borrow::Cow;
1313

1414
impl<'hir> LoweringContext<'_, 'hir> {
1515
pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
16-
let fmt = flatten_format_args(fmt);
16+
let fmt = flatten_format_args(Cow::Borrowed(fmt));
17+
let fmt = inline_literals(fmt);
1718
expand_format_args(self, sp, &fmt)
1819
}
1920
}
@@ -27,8 +28,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
2728
/// into
2829
///
2930
/// `format_args!("a {} b{}! {}.", 1, 2, 3)`.
30-
fn flatten_format_args(fmt: &FormatArgs) -> Cow<'_, FormatArgs> {
31-
let mut fmt = Cow::Borrowed(fmt);
31+
fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
3232
let mut i = 0;
3333
while i < fmt.template.len() {
3434
if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i]
@@ -100,6 +100,64 @@ fn flatten_format_args(fmt: &FormatArgs) -> Cow<'_, FormatArgs> {
100100
fmt
101101
}
102102

103+
/// Inline literals into the format string.
104+
///
105+
/// Turns
106+
///
107+
/// `format_args!("Hello, {}! {}", "World", 123)`
108+
///
109+
/// into
110+
///
111+
/// `format_args!("Hello, World! {}", 123)`.
112+
fn inline_literals(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
113+
// None: Not sure yet.
114+
// Some(true): Remove, because it was inlined. (Might be set to false later if it is used in another way.)
115+
// Some(false): Do not remove, because some non-inlined placeholder uses it.
116+
let mut remove = vec![None; fmt.arguments.all_args().len()];
117+
118+
for i in 0..fmt.template.len() {
119+
let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
120+
let Ok(arg_index) = placeholder.argument.index else { continue };
121+
if let FormatTrait::Display = placeholder.format_trait
122+
&& let ExprKind::Lit(lit) = fmt.arguments.all_args()[arg_index].expr.kind
123+
&& let token::LitKind::Str | token::LitKind::StrRaw(_) = lit.kind
124+
&& let Ok(LitKind::Str(s, _)) = LitKind::from_token_lit(lit)
125+
{
126+
// Now we need to mutate the outer FormatArgs.
127+
// If this is the first time, this clones the outer FormatArgs.
128+
let fmt = fmt.to_mut();
129+
// Replace the placeholder with the literal.
130+
fmt.template[i] = FormatArgsPiece::Literal(s);
131+
// Only remove it wasn't set to 'do not remove'.
132+
remove[arg_index].get_or_insert(true);
133+
} else {
134+
// Never remove an argument that's used by a non-inlined placeholder,
135+
// even if this argument is inlined in another place.
136+
remove[arg_index] = Some(false);
137+
}
138+
}
139+
140+
// Remove the arguments that were inlined.
141+
if remove.iter().any(|&x| x == Some(true)) {
142+
let fmt = fmt.to_mut();
143+
// Drop all the arguments that are marked for removal.
144+
let mut remove_it = remove.iter();
145+
fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&Some(true)));
146+
// Correct the indexes that refer to arguments that have shifted position.
147+
for piece in &mut fmt.template {
148+
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
149+
let Ok(arg_index) = &mut placeholder.argument.index else { continue };
150+
for i in 0..*arg_index {
151+
if remove[i] == Some(true) {
152+
*arg_index -= 1;
153+
}
154+
}
155+
}
156+
}
157+
158+
fmt
159+
}
160+
103161
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
104162
enum ArgumentType {
105163
Format(FormatTrait),

0 commit comments

Comments
 (0)