@@ -166,6 +166,75 @@ module StringOps {
166
166
}
167
167
}
168
168
169
+ /** Provides predicates and classes for working with Printf-style formatters. */
170
+ module Formatting {
171
+ /**
172
+ * Gets a regular expression for matching simple format-string components, including flags,
173
+ * width and precision specifiers, not including explicit argument indices.
174
+ */
175
+ pragma [ noinline]
176
+ private string getFormatComponentRegex ( ) {
177
+ exists (
178
+ string literal , string opt_flag , string width , string prec , string opt_width_and_prec ,
179
+ string operator , string verb
180
+ |
181
+ literal = "([^%]|%%)+" and
182
+ opt_flag = "[-+ #0]?" and
183
+ width = "\\d+|\\*" and
184
+ prec = "\\.(\\d+|\\*)" and
185
+ opt_width_and_prec = "(" + width + ")?(" + prec + ")?" and
186
+ operator = "[bcdeEfFgGoOpqstTxXUv]" and
187
+ verb = "(%" + opt_flag + opt_width_and_prec + operator + ")"
188
+ |
189
+ result = "(" + literal + "|" + verb + ")"
190
+ )
191
+ }
192
+
193
+ /**
194
+ * A function that performs string formatting in the same manner as `fmt.Printf` etc.
195
+ */
196
+ abstract class Range extends Function {
197
+ /**
198
+ * Gets the parameter index of the format string.
199
+ */
200
+ abstract int getFormatStringIndex ( ) ;
201
+
202
+ /**
203
+ * Gets the parameter index of the first parameter to be formatted.
204
+ */
205
+ abstract int getFirstFormattedParameterIndex ( ) ;
206
+ }
207
+
208
+ /**
209
+ * A call to a `fmt.Printf`-style string formatting function.
210
+ */
211
+ class StringFormatCall extends DataFlow:: CallNode {
212
+ string fmt ;
213
+ Range f ;
214
+
215
+ StringFormatCall ( ) {
216
+ this = f .getACall ( ) and
217
+ fmt = this .getArgument ( f .getFormatStringIndex ( ) ) .getStringValue ( ) and
218
+ fmt .regexpMatch ( getFormatComponentRegex ( ) + "*" )
219
+ }
220
+
221
+ /**
222
+ * Gets the `n`th component of this format string.
223
+ */
224
+ string getComponent ( int n ) { result = fmt .regexpFind ( getFormatComponentRegex ( ) , n , _) }
225
+
226
+ /**
227
+ * Gets the `n`th argument formatted by this format call, where `formatDirective` specifies how it will be formatted.
228
+ */
229
+ DataFlow:: Node getOperand ( int n , string formatDirective ) {
230
+ formatDirective = this .getComponent ( n ) and
231
+ formatDirective .charAt ( 0 ) = "%" and
232
+ formatDirective .charAt ( 1 ) != "%" and
233
+ result = this .getArgument ( ( n / 2 ) + f .getFirstFormattedParameterIndex ( ) )
234
+ }
235
+ }
236
+ }
237
+
169
238
/**
170
239
* A data-flow node that performs string concatenation.
171
240
*
@@ -233,29 +302,6 @@ module StringOps {
233
302
}
234
303
}
235
304
236
- /**
237
- * Gets a regular expression for matching simple format-string components, including flags,
238
- * width and precision specifiers, but not including `*` specifiers or explicit argument
239
- * indices.
240
- */
241
- pragma [ noinline]
242
- private string getFormatComponentRegex ( ) {
243
- exists (
244
- string literal , string opt_flag , string width , string prec , string opt_width_and_prec ,
245
- string operator , string verb
246
- |
247
- literal = "([^%]|%%)+" and
248
- opt_flag = "[-+ #0]?" and
249
- width = "\\d+|\\*" and
250
- prec = "\\.(\\d+|\\*)" and
251
- opt_width_and_prec = "(" + width + ")?(" + prec + ")?" and
252
- operator = "[bcdeEfFgGoOpqstTxXUv]" and
253
- verb = "(%" + opt_flag + opt_width_and_prec + operator + ")"
254
- |
255
- result = "(" + literal + "|" + verb + ")"
256
- )
257
- }
258
-
259
305
/**
260
306
* A call to `fmt.Sprintf`, considered as a string concatenation.
261
307
*
@@ -272,42 +318,25 @@ module StringOps {
272
318
* node nor a string value. This is because verbs like `%q` perform additional string
273
319
* transformations that we cannot easily represent.
274
320
*/
275
- private class SprintfConcat extends Range , DataFlow:: CallNode {
276
- string fmt ;
277
-
278
- SprintfConcat ( ) {
279
- exists ( Function sprintf | sprintf .hasQualifiedName ( "fmt" , "Sprintf" ) |
280
- this = sprintf .getACall ( ) and
281
- fmt = this .getArgument ( 0 ) .getStringValue ( ) and
282
- fmt .regexpMatch ( getFormatComponentRegex ( ) + "*" )
283
- )
284
- }
285
-
286
- /**
287
- * Gets the `n`th component of this format string.
288
- */
289
- private string getComponent ( int n ) {
290
- result = fmt .regexpFind ( getFormatComponentRegex ( ) , n , _)
291
- }
321
+ private class SprintfConcat extends Range instanceof Formatting:: StringFormatCall {
322
+ SprintfConcat ( ) { this = any ( Function f | f .hasQualifiedName ( "fmt" , "Sprintf" ) ) .getACall ( ) }
292
323
293
324
override DataFlow:: Node getOperand ( int n ) {
294
- exists ( int i , string part | part = "%s" or part = "%v" |
295
- part = this .getComponent ( n ) and
296
- i = n / 2 and
297
- result = this .getArgument ( i + 1 )
298
- )
325
+ result = Formatting:: StringFormatCall .super .getOperand ( n , [ "%s" , "%v" ] )
299
326
}
300
327
301
328
override string getOperandStringValue ( int n ) {
302
329
result = Range .super .getOperandStringValue ( n )
303
330
or
304
- exists ( string cmp | cmp = this .getComponent ( n ) |
331
+ exists ( string cmp | cmp = Formatting :: StringFormatCall . super .getComponent ( n ) |
305
332
( cmp .charAt ( 0 ) != "%" or cmp .charAt ( 1 ) = "%" ) and
306
333
result = cmp .replaceAll ( "%%" , "%" )
307
334
)
308
335
}
309
336
310
- override int getNumOperand ( ) { result = max ( int i | exists ( this .getComponent ( i ) ) ) + 1 }
337
+ override int getNumOperand ( ) {
338
+ result = max ( int i | exists ( Formatting:: StringFormatCall .super .getComponent ( i ) ) ) + 1
339
+ }
311
340
}
312
341
313
342
/**
0 commit comments