@@ -20,10 +20,126 @@ impl<'hir> LoweringContext<'_, 'hir> {
20
20
let mut fmt = Cow :: Borrowed ( fmt) ;
21
21
if self . tcx . sess . opts . unstable_opts . flatten_format_args {
22
22
fmt = flatten_format_args ( fmt) ;
23
- fmt = inline_literals ( fmt) ;
23
+ fmt = self . inline_literals ( fmt) ;
24
24
}
25
25
expand_format_args ( self , sp, & fmt, allow_const)
26
26
}
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
+ }
27
143
}
28
144
29
145
/// Flattens nested `format_args!()` into one.
@@ -103,82 +219,6 @@ fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
103
219
fmt
104
220
}
105
221
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
-
182
222
#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
183
223
enum ArgumentType {
184
224
Format ( FormatTrait ) ,
0 commit comments