@@ -13,7 +13,8 @@ use std::borrow::Cow;
13
13
14
14
impl < ' hir > LoweringContext < ' _ , ' hir > {
15
15
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) ;
17
18
expand_format_args ( self , sp, & fmt)
18
19
}
19
20
}
@@ -27,8 +28,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
27
28
/// into
28
29
///
29
30
/// `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 > {
32
32
let mut i = 0 ;
33
33
while i < fmt. template . len ( ) {
34
34
if let FormatArgsPiece :: Placeholder ( placeholder) = & fmt. template [ i]
@@ -100,6 +100,64 @@ fn flatten_format_args(fmt: &FormatArgs) -> Cow<'_, FormatArgs> {
100
100
fmt
101
101
}
102
102
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
+
103
161
#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
104
162
enum ArgumentType {
105
163
Format ( FormatTrait ) ,
0 commit comments