@@ -13,7 +13,8 @@ use std::borrow::Cow;
1313
1414impl < ' 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 ) ]
104162enum ArgumentType {
105163 Format ( FormatTrait ) ,
0 commit comments