@@ -20,10 +20,126 @@ impl<'hir> LoweringContext<'_, 'hir> {
2020 let mut fmt = Cow :: Borrowed ( fmt) ;
2121 if self . tcx . sess . opts . unstable_opts . flatten_format_args {
2222 fmt = flatten_format_args ( fmt) ;
23- fmt = inline_literals ( fmt) ;
23+ fmt = self . inline_literals ( fmt) ;
2424 }
2525 expand_format_args ( self , sp, & fmt, allow_const)
2626 }
27+
28+ /// Try to convert a literal into an interned string
29+ fn try_inline_lit ( & self , lit : token:: Lit ) -> Option < Symbol > {
30+ match LitKind :: from_token_lit ( lit) {
31+ Ok ( LitKind :: Str ( s, _) ) => Some ( s) ,
32+ Ok ( LitKind :: Int ( n, ty) ) => {
33+ match ty {
34+ // unsuffixed integer literals are assumed to be i32's
35+ LitIntType :: Unsuffixed => {
36+ ( n <= i32:: MAX as u128 ) . then_some ( Symbol :: intern ( & n. to_string ( ) ) )
37+ }
38+ LitIntType :: Signed ( int_ty) => {
39+ let max_literal = self . int_ty_max ( int_ty) ;
40+ ( n <= max_literal) . then_some ( Symbol :: intern ( & n. to_string ( ) ) )
41+ }
42+ LitIntType :: Unsigned ( uint_ty) => {
43+ let max_literal = self . uint_ty_max ( uint_ty) ;
44+ ( n <= max_literal) . then_some ( Symbol :: intern ( & n. to_string ( ) ) )
45+ }
46+ }
47+ }
48+ _ => None ,
49+ }
50+ }
51+
52+ /// Get the maximum value of int_ty. It is platform-dependent due to the byte size of isize
53+ fn int_ty_max ( & self , int_ty : IntTy ) -> u128 {
54+ match int_ty {
55+ IntTy :: Isize => self . tcx . data_layout . pointer_size . signed_int_max ( ) as u128 ,
56+ IntTy :: I8 => i8:: MAX as u128 ,
57+ IntTy :: I16 => i16:: MAX as u128 ,
58+ IntTy :: I32 => i32:: MAX as u128 ,
59+ IntTy :: I64 => i64:: MAX as u128 ,
60+ IntTy :: I128 => i128:: MAX as u128 ,
61+ }
62+ }
63+
64+ /// Get the maximum value of uint_ty. It is platform-dependent due to the byte size of usize
65+ fn uint_ty_max ( & self , uint_ty : UintTy ) -> u128 {
66+ match uint_ty {
67+ UintTy :: Usize => self . tcx . data_layout . pointer_size . unsigned_int_max ( ) ,
68+ UintTy :: U8 => u8:: MAX as u128 ,
69+ UintTy :: U16 => u16:: MAX as u128 ,
70+ UintTy :: U32 => u32:: MAX as u128 ,
71+ UintTy :: U64 => u64:: MAX as u128 ,
72+ UintTy :: U128 => u128:: MAX as u128 ,
73+ }
74+ }
75+
76+ /// Inline literals into the format string.
77+ ///
78+ /// Turns
79+ ///
80+ /// `format_args!("Hello, {}! {} {}", "World", 123, x)`
81+ ///
82+ /// into
83+ ///
84+ /// `format_args!("Hello, World! 123 {}", x)`.
85+ fn inline_literals < ' fmt > ( & self , mut fmt : Cow < ' fmt , FormatArgs > ) -> Cow < ' fmt , FormatArgs > {
86+ let mut was_inlined = vec ! [ false ; fmt. arguments. all_args( ) . len( ) ] ;
87+ let mut inlined_anything = false ;
88+
89+ for i in 0 ..fmt. template . len ( ) {
90+ let FormatArgsPiece :: Placeholder ( placeholder) = & fmt. template [ i] else { continue } ;
91+ let Ok ( arg_index) = placeholder. argument . index else { continue } ;
92+
93+ let mut literal = None ;
94+
95+ if let FormatTrait :: Display = placeholder. format_trait
96+ && placeholder. format_options == Default :: default ( )
97+ && let arg = fmt. arguments . all_args ( ) [ arg_index] . expr . peel_parens_and_refs ( )
98+ && let ExprKind :: Lit ( lit) = arg. kind
99+ {
100+ literal = self . try_inline_lit ( lit) ;
101+ }
102+
103+ if let Some ( literal) = literal {
104+ // Now we need to mutate the outer FormatArgs.
105+ // If this is the first time, this clones the outer FormatArgs.
106+ let fmt = fmt. to_mut ( ) ;
107+ // Replace the placeholder with the literal.
108+ fmt. template [ i] = FormatArgsPiece :: Literal ( literal) ;
109+ was_inlined[ arg_index] = true ;
110+ inlined_anything = true ;
111+ }
112+ }
113+
114+ // Remove the arguments that were inlined.
115+ if inlined_anything {
116+ let fmt = fmt. to_mut ( ) ;
117+
118+ let mut remove = was_inlined;
119+
120+ // Don't remove anything that's still used.
121+ for_all_argument_indexes ( & mut fmt. template , |index| remove[ * index] = false ) ;
122+
123+ // Drop all the arguments that are marked for removal.
124+ let mut remove_it = remove. iter ( ) ;
125+ fmt. arguments . all_args_mut ( ) . retain ( |_| remove_it. next ( ) != Some ( & true ) ) ;
126+
127+ // Calculate the mapping of old to new indexes for the remaining arguments.
128+ let index_map: Vec < usize > = remove
129+ . into_iter ( )
130+ . scan ( 0 , |i, remove| {
131+ let mapped = * i;
132+ * i += !remove as usize ;
133+ Some ( mapped)
134+ } )
135+ . collect ( ) ;
136+
137+ // Correct the indexes that refer to arguments that have shifted position.
138+ for_all_argument_indexes ( & mut fmt. template , |index| * index = index_map[ * index] ) ;
139+ }
140+
141+ fmt
142+ }
27143}
28144
29145/// Flattens nested `format_args!()` into one.
@@ -103,82 +219,6 @@ fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
103219 fmt
104220}
105221
106- /// Inline literals into the format string.
107- ///
108- /// Turns
109- ///
110- /// `format_args!("Hello, {}! {} {}", "World", 123, x)`
111- ///
112- /// into
113- ///
114- /// `format_args!("Hello, World! 123 {}", x)`.
115- fn inline_literals ( mut fmt : Cow < ' _ , FormatArgs > ) -> Cow < ' _ , FormatArgs > {
116- let mut was_inlined = vec ! [ false ; fmt. arguments. all_args( ) . len( ) ] ;
117- let mut inlined_anything = false ;
118-
119- for i in 0 ..fmt. template . len ( ) {
120- let FormatArgsPiece :: Placeholder ( placeholder) = & fmt. template [ i] else { continue } ;
121- let Ok ( arg_index) = placeholder. argument . index else { continue } ;
122-
123- let mut literal = None ;
124-
125- if let FormatTrait :: Display = placeholder. format_trait
126- && placeholder. format_options == Default :: default ( )
127- && let arg = fmt. arguments . all_args ( ) [ arg_index] . expr . peel_parens_and_refs ( )
128- && let ExprKind :: Lit ( lit) = arg. kind
129- {
130- if let token:: LitKind :: Str | token:: LitKind :: StrRaw ( _) = lit. kind
131- && let Ok ( LitKind :: Str ( s, _) ) = LitKind :: from_token_lit ( lit)
132- {
133- literal = Some ( s) ;
134- } else if let token:: LitKind :: Integer = lit. kind
135- && let Ok ( LitKind :: Int ( n, _) ) = LitKind :: from_token_lit ( lit)
136- {
137- literal = Some ( Symbol :: intern ( & n. to_string ( ) ) ) ;
138- }
139- }
140-
141- if let Some ( literal) = literal {
142- // Now we need to mutate the outer FormatArgs.
143- // If this is the first time, this clones the outer FormatArgs.
144- let fmt = fmt. to_mut ( ) ;
145- // Replace the placeholder with the literal.
146- fmt. template [ i] = FormatArgsPiece :: Literal ( literal) ;
147- was_inlined[ arg_index] = true ;
148- inlined_anything = true ;
149- }
150- }
151-
152- // Remove the arguments that were inlined.
153- if inlined_anything {
154- let fmt = fmt. to_mut ( ) ;
155-
156- let mut remove = was_inlined;
157-
158- // Don't remove anything that's still used.
159- for_all_argument_indexes ( & mut fmt. template , |index| remove[ * index] = false ) ;
160-
161- // Drop all the arguments that are marked for removal.
162- let mut remove_it = remove. iter ( ) ;
163- fmt. arguments . all_args_mut ( ) . retain ( |_| remove_it. next ( ) != Some ( & true ) ) ;
164-
165- // Calculate the mapping of old to new indexes for the remaining arguments.
166- let index_map: Vec < usize > = remove
167- . into_iter ( )
168- . scan ( 0 , |i, remove| {
169- let mapped = * i;
170- * i += !remove as usize ;
171- Some ( mapped)
172- } )
173- . collect ( ) ;
174-
175- // Correct the indexes that refer to arguments that have shifted position.
176- for_all_argument_indexes ( & mut fmt. template , |index| * index = index_map[ * index] ) ;
177- }
178-
179- fmt
180- }
181-
182222#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
183223enum ArgumentType {
184224 Format ( FormatTrait ) ,
0 commit comments