about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMara Bos <m-ou.se@m-ou.se>2023-01-13 17:32:15 +0100
committerMara Bos <m-ou.se@m-ou.se>2023-03-16 11:19:31 +0100
commit85ef2f0cfe7c176755d4ec542c38df868bca8bcb (patch)
tree478ab2f0c62536affe96a1dcf4c3b01ec847223a
parent94ad7e881dbf28784e44198dcf7dd865661b5424 (diff)
downloadrust-85ef2f0cfe7c176755d4ec542c38df868bca8bcb.tar.gz
rust-85ef2f0cfe7c176755d4ec542c38df868bca8bcb.zip
Inline string literals into format_args!().
-rw-r--r--compiler/rustc_ast_lowering/src/format.rs64
1 files changed, 61 insertions, 3 deletions
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 63e74261d7c..3db4d061f21 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -13,7 +13,8 @@ use std::borrow::Cow;
 
 impl<'hir> LoweringContext<'_, 'hir> {
     pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
-        let fmt = flatten_format_args(fmt);
+        let fmt = flatten_format_args(Cow::Borrowed(fmt));
+        let fmt = inline_literals(fmt);
         expand_format_args(self, sp, &fmt)
     }
 }
@@ -27,8 +28,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 /// into
 ///
 /// `format_args!("a {} b{}! {}.", 1, 2, 3)`.
-fn flatten_format_args(fmt: &FormatArgs) -> Cow<'_, FormatArgs> {
-    let mut fmt = Cow::Borrowed(fmt);
+fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
     let mut i = 0;
     while i < fmt.template.len() {
         if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i]
@@ -100,6 +100,64 @@ fn flatten_format_args(fmt: &FormatArgs) -> Cow<'_, FormatArgs> {
     fmt
 }
 
+/// Inline literals into the format string.
+///
+/// Turns
+///
+/// `format_args!("Hello, {}! {}", "World", 123)`
+///
+/// into
+///
+/// `format_args!("Hello, World! {}", 123)`.
+fn inline_literals(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
+    // None: Not sure yet.
+    // Some(true): Remove, because it was inlined. (Might be set to false later if it is used in another way.)
+    // Some(false): Do not remove, because some non-inlined placeholder uses it.
+    let mut remove = vec![None; fmt.arguments.all_args().len()];
+
+    for i in 0..fmt.template.len() {
+        let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
+        let Ok(arg_index) = placeholder.argument.index else { continue };
+        if let FormatTrait::Display = placeholder.format_trait
+            && let ExprKind::Lit(lit) = fmt.arguments.all_args()[arg_index].expr.kind
+            && let token::LitKind::Str | token::LitKind::StrRaw(_) = lit.kind
+            && let Ok(LitKind::Str(s, _)) = LitKind::from_token_lit(lit)
+        {
+            // Now we need to mutate the outer FormatArgs.
+            // If this is the first time, this clones the outer FormatArgs.
+            let fmt = fmt.to_mut();
+            // Replace the placeholder with the literal.
+            fmt.template[i] = FormatArgsPiece::Literal(s);
+            // Only remove it wasn't set to 'do not remove'.
+            remove[arg_index].get_or_insert(true);
+        } else {
+            // Never remove an argument that's used by a non-inlined placeholder,
+            // even if this argument is inlined in another place.
+            remove[arg_index] = Some(false);
+        }
+    }
+
+    // Remove the arguments that were inlined.
+    if remove.iter().any(|&x| x == Some(true)) {
+        let fmt = fmt.to_mut();
+        // Drop all the arguments that are marked for removal.
+        let mut remove_it = remove.iter();
+        fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&Some(true)));
+        // Correct the indexes that refer to arguments that have shifted position.
+        for piece in &mut fmt.template {
+            let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
+            let Ok(arg_index) = &mut placeholder.argument.index else { continue };
+            for i in 0..*arg_index {
+                if remove[i] == Some(true) {
+                    *arg_index -= 1;
+                }
+            }
+        }
+    }
+
+    fmt
+}
+
 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 enum ArgumentType {
     Format(FormatTrait),