/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/include/clang/AST/FormatString.h
Line | Count | Source (jump to first uncovered line) |
1 | | //= FormatString.h - Analysis of printf/fprintf format strings --*- C++ -*-===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defines APIs for analyzing the format strings of printf, fscanf, |
10 | | // and friends. |
11 | | // |
12 | | // The structure of format strings for fprintf are described in C99 7.19.6.1. |
13 | | // |
14 | | // The structure of format strings for fscanf are described in C99 7.19.6.2. |
15 | | // |
16 | | //===----------------------------------------------------------------------===// |
17 | | |
18 | | #ifndef LLVM_CLANG_AST_FORMATSTRING_H |
19 | | #define LLVM_CLANG_AST_FORMATSTRING_H |
20 | | |
21 | | #include "clang/AST/CanonicalType.h" |
22 | | |
23 | | namespace clang { |
24 | | |
25 | | class TargetInfo; |
26 | | |
27 | | //===----------------------------------------------------------------------===// |
28 | | /// Common components of both fprintf and fscanf format strings. |
29 | | namespace analyze_format_string { |
30 | | |
31 | | /// Class representing optional flags with location and representation |
32 | | /// information. |
33 | | class OptionalFlag { |
34 | | public: |
35 | | OptionalFlag(const char *Representation) |
36 | 500k | : representation(Representation), flag(false) {} |
37 | 46.9k | bool isSet() const { return flag; } |
38 | 0 | void set() { flag = true; } |
39 | 0 | void clear() { flag = false; } |
40 | 430 | void setPosition(const char *position) { |
41 | 430 | assert(position); |
42 | 0 | flag = true; |
43 | 430 | this->position = position; |
44 | 430 | } |
45 | 165 | const char *getPosition() const { |
46 | 165 | assert(position); |
47 | 0 | return position; |
48 | 165 | } |
49 | 111 | const char *toString() const { return representation; } |
50 | | |
51 | | // Overloaded operators for bool like qualities |
52 | 231k | explicit operator bool() const { return flag; } |
53 | 1.11k | OptionalFlag& operator=(const bool &rhs) { |
54 | 1.11k | flag = rhs; |
55 | 1.11k | return *this; // Return a reference to myself. |
56 | 1.11k | } |
57 | | private: |
58 | | const char *representation; |
59 | | const char *position; |
60 | | bool flag; |
61 | | }; |
62 | | |
63 | | /// Represents the length modifier in a format string in scanf/printf. |
64 | | class LengthModifier { |
65 | | public: |
66 | | enum Kind { |
67 | | None, |
68 | | AsChar, // 'hh' |
69 | | AsShort, // 'h' |
70 | | AsShortLong, // 'hl' (OpenCL float/int vector element) |
71 | | AsLong, // 'l' |
72 | | AsLongLong, // 'll' |
73 | | AsQuad, // 'q' (BSD, deprecated, for 64-bit integer types) |
74 | | AsIntMax, // 'j' |
75 | | AsSizeT, // 'z' |
76 | | AsPtrDiff, // 't' |
77 | | AsInt32, // 'I32' (MSVCRT, like __int32) |
78 | | AsInt3264, // 'I' (MSVCRT, like __int3264 from MIDL) |
79 | | AsInt64, // 'I64' (MSVCRT, like __int64) |
80 | | AsLongDouble, // 'L' |
81 | | AsAllocate, // for '%as', GNU extension to C90 scanf |
82 | | AsMAllocate, // for '%ms', GNU extension to scanf |
83 | | AsWide, // 'w' (MSVCRT, like l but only for c, C, s, S, or Z |
84 | | AsWideChar = AsLong // for '%ls', only makes sense for printf |
85 | | }; |
86 | | |
87 | | LengthModifier() |
88 | 51.9k | : Position(nullptr), kind(None) {} |
89 | | LengthModifier(const char *pos, Kind k) |
90 | 6.97k | : Position(pos), kind(k) {} |
91 | | |
92 | 523 | const char *getStart() const { |
93 | 523 | return Position; |
94 | 523 | } |
95 | | |
96 | 225 | unsigned getLength() const { |
97 | 225 | switch (kind) { |
98 | 199 | default: |
99 | 199 | return 1; |
100 | 2 | case AsLongLong: |
101 | 20 | case AsChar: |
102 | 20 | return 2; |
103 | 3 | case AsInt32: |
104 | 6 | case AsInt64: |
105 | 6 | return 3; |
106 | 0 | case None: |
107 | 0 | return 0; |
108 | 225 | } |
109 | 225 | } |
110 | | |
111 | 110k | Kind getKind() const { return kind; } |
112 | 1.17k | void setKind(Kind k) { kind = k; } |
113 | | |
114 | | const char *toString() const; |
115 | | |
116 | | private: |
117 | | const char *Position; |
118 | | Kind kind; |
119 | | }; |
120 | | |
121 | | class ConversionSpecifier { |
122 | | public: |
123 | | enum Kind { |
124 | | InvalidSpecifier = 0, |
125 | | // C99 conversion specifiers. |
126 | | cArg, |
127 | | dArg, |
128 | | DArg, // Apple extension |
129 | | iArg, |
130 | | IntArgBeg = dArg, |
131 | | IntArgEnd = iArg, |
132 | | |
133 | | oArg, |
134 | | OArg, // Apple extension |
135 | | uArg, |
136 | | UArg, // Apple extension |
137 | | xArg, |
138 | | XArg, |
139 | | UIntArgBeg = oArg, |
140 | | UIntArgEnd = XArg, |
141 | | |
142 | | fArg, |
143 | | FArg, |
144 | | eArg, |
145 | | EArg, |
146 | | gArg, |
147 | | GArg, |
148 | | aArg, |
149 | | AArg, |
150 | | DoubleArgBeg = fArg, |
151 | | DoubleArgEnd = AArg, |
152 | | |
153 | | sArg, |
154 | | pArg, |
155 | | nArg, |
156 | | PercentArg, |
157 | | CArg, |
158 | | SArg, |
159 | | |
160 | | // Apple extension: P specifies to os_log that the data being pointed to is |
161 | | // to be copied by os_log. The precision indicates the number of bytes to |
162 | | // copy. |
163 | | PArg, |
164 | | |
165 | | // ** Printf-specific ** |
166 | | |
167 | | ZArg, // MS extension |
168 | | |
169 | | // Objective-C specific specifiers. |
170 | | ObjCObjArg, // '@' |
171 | | ObjCBeg = ObjCObjArg, |
172 | | ObjCEnd = ObjCObjArg, |
173 | | |
174 | | // FreeBSD kernel specific specifiers. |
175 | | FreeBSDbArg, |
176 | | FreeBSDDArg, |
177 | | FreeBSDrArg, |
178 | | FreeBSDyArg, |
179 | | |
180 | | // GlibC specific specifiers. |
181 | | PrintErrno, // 'm' |
182 | | |
183 | | PrintfConvBeg = ObjCObjArg, |
184 | | PrintfConvEnd = PrintErrno, |
185 | | |
186 | | // ** Scanf-specific ** |
187 | | ScanListArg, // '[' |
188 | | ScanfConvBeg = ScanListArg, |
189 | | ScanfConvEnd = ScanListArg |
190 | | }; |
191 | | |
192 | | ConversionSpecifier(bool isPrintf = true) |
193 | | : IsPrintf(isPrintf), Position(nullptr), EndScanList(nullptr), |
194 | 51.9k | kind(InvalidSpecifier) {} |
195 | | |
196 | | ConversionSpecifier(bool isPrintf, const char *pos, Kind k) |
197 | 33.8k | : IsPrintf(isPrintf), Position(pos), EndScanList(nullptr), kind(k) {} |
198 | | |
199 | 698 | const char *getStart() const { |
200 | 698 | return Position; |
201 | 698 | } |
202 | | |
203 | 0 | StringRef getCharacters() const { |
204 | 0 | return StringRef(getStart(), getLength()); |
205 | 0 | } |
206 | | |
207 | 120k | bool consumesDataArgument() const { |
208 | 120k | switch (kind) { |
209 | 39 | case PrintErrno: |
210 | 39 | assert(IsPrintf); |
211 | 0 | return false; |
212 | 236 | case PercentArg: |
213 | 236 | return false; |
214 | 163 | case InvalidSpecifier: |
215 | 163 | return false; |
216 | 120k | default: |
217 | 120k | return true; |
218 | 120k | } |
219 | 120k | } |
220 | | |
221 | 255k | Kind getKind() const { return kind; } |
222 | 843 | void setKind(Kind k) { kind = k; } |
223 | 181 | unsigned getLength() const { |
224 | 181 | return EndScanList ? EndScanList - Position12 : 1169 ; |
225 | 181 | } |
226 | 102 | void setEndScanList(const char *pos) { EndScanList = pos; } |
227 | | |
228 | 25.6k | bool isIntArg() const { return (kind >= IntArgBeg && kind <= IntArgEnd) || |
229 | 25.6k | kind == FreeBSDrArg15.1k || kind == FreeBSDyArg15.0k ; } |
230 | 15.0k | bool isUIntArg() const { return kind >= UIntArgBeg && kind <= UIntArgEnd; } |
231 | 225 | bool isAnyIntArg() const { return kind >= IntArgBeg && kind <= UIntArgEnd210 ; } |
232 | 14.2k | bool isDoubleArg() const { |
233 | 14.2k | return kind >= DoubleArgBeg && kind <= DoubleArgEnd12.3k ; |
234 | 14.2k | } |
235 | | |
236 | | const char *toString() const; |
237 | | |
238 | 121k | bool isPrintfKind() const { return IsPrintf; } |
239 | | |
240 | | Optional<ConversionSpecifier> getStandardSpecifier() const; |
241 | | |
242 | | protected: |
243 | | bool IsPrintf; |
244 | | const char *Position; |
245 | | const char *EndScanList; |
246 | | Kind kind; |
247 | | }; |
248 | | |
249 | | class ArgType { |
250 | | public: |
251 | | enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy, |
252 | | AnyCharTy, CStrTy, WCStrTy, WIntTy }; |
253 | | |
254 | | /// How well a given conversion specifier matches its argument. |
255 | | enum MatchKind { |
256 | | /// The conversion specifier and the argument types are incompatible. For |
257 | | /// instance, "%d" and float. |
258 | | NoMatch = 0, |
259 | | /// The conversion specifier and the argument type are compatible. For |
260 | | /// instance, "%d" and _Bool. |
261 | | Match = 1, |
262 | | /// The conversion specifier and the argument type are disallowed by the C |
263 | | /// standard, but are in practice harmless. For instance, "%p" and int*. |
264 | | NoMatchPedantic, |
265 | | /// The conversion specifier and the argument type are compatible, but still |
266 | | /// seems likely to be an error. For instance, "%hd" and _Bool. |
267 | | NoMatchTypeConfusion, |
268 | | }; |
269 | | |
270 | | private: |
271 | | const Kind K; |
272 | | QualType T; |
273 | | const char *Name = nullptr; |
274 | | bool Ptr = false; |
275 | | |
276 | | /// The TypeKind identifies certain well-known types like size_t and |
277 | | /// ptrdiff_t. |
278 | | enum class TypeKind { DontCare, SizeT, PtrdiffT }; |
279 | | TypeKind TK = TypeKind::DontCare; |
280 | | |
281 | | public: |
282 | 10.2k | ArgType(Kind K = UnknownTy, const char *N = nullptr) : K(K), Name(N) {} |
283 | 805 | ArgType(QualType T, const char *N = nullptr) : K(SpecificTy), T(T), Name(N) {} |
284 | 17.3k | ArgType(CanQualType T) : K(SpecificTy), T(T) {} |
285 | | |
286 | 62 | static ArgType Invalid() { return ArgType(InvalidTy); } |
287 | 55.1k | bool isValid() const { return K != InvalidTy; } |
288 | | |
289 | 134 | bool isSizeT() const { return TK == TypeKind::SizeT; } |
290 | | |
291 | 114 | bool isPtrdiffT() const { return TK == TypeKind::PtrdiffT; } |
292 | | |
293 | | /// Create an ArgType which corresponds to the type pointer to A. |
294 | 1.10k | static ArgType PtrTo(const ArgType& A) { |
295 | 1.10k | assert(A.K >= InvalidTy && "ArgType cannot be pointer to invalid/unknown"); |
296 | 0 | ArgType Res = A; |
297 | 1.10k | Res.Ptr = true; |
298 | 1.10k | return Res; |
299 | 1.10k | } |
300 | | |
301 | | /// Create an ArgType which corresponds to the size_t/ssize_t type. |
302 | 205 | static ArgType makeSizeT(const ArgType &A) { |
303 | 205 | ArgType Res = A; |
304 | 205 | Res.TK = TypeKind::SizeT; |
305 | 205 | return Res; |
306 | 205 | } |
307 | | |
308 | | /// Create an ArgType which corresponds to the ptrdiff_t/unsigned ptrdiff_t |
309 | | /// type. |
310 | 29 | static ArgType makePtrdiffT(const ArgType &A) { |
311 | 29 | ArgType Res = A; |
312 | 29 | Res.TK = TypeKind::PtrdiffT; |
313 | 29 | return Res; |
314 | 29 | } |
315 | | |
316 | | MatchKind matchesType(ASTContext &C, QualType argTy) const; |
317 | | |
318 | | QualType getRepresentativeType(ASTContext &C) const; |
319 | | |
320 | | ArgType makeVectorType(ASTContext &C, unsigned NumElts) const; |
321 | | |
322 | | std::string getRepresentativeTypeName(ASTContext &C) const; |
323 | | }; |
324 | | |
325 | | class OptionalAmount { |
326 | | public: |
327 | | enum HowSpecified { NotSpecified, Constant, Arg, Invalid }; |
328 | | |
329 | | OptionalAmount(HowSpecified howSpecified, |
330 | | unsigned amount, |
331 | | const char *amountStart, |
332 | | unsigned amountLength, |
333 | | bool usesPositionalArg) |
334 | | : start(amountStart), length(amountLength), hs(howSpecified), amt(amount), |
335 | 2.35k | UsesPositionalArg(usesPositionalArg), UsesDotPrefix(false) {} |
336 | | |
337 | | OptionalAmount(bool valid = true) |
338 | | : start(nullptr),length(0), hs(valid ? NotSpecified : Invalid), amt(0), |
339 | 219k | UsesPositionalArg(false), UsesDotPrefix(false) {} |
340 | | |
341 | | explicit OptionalAmount(unsigned Amount) |
342 | | : start(nullptr), length(0), hs(Constant), amt(Amount), |
343 | 87 | UsesPositionalArg(false), UsesDotPrefix(false) {} |
344 | | |
345 | 30.0k | bool isInvalid() const { |
346 | 30.0k | return hs == Invalid; |
347 | 30.0k | } |
348 | | |
349 | 92.6k | HowSpecified getHowSpecified() const { return hs; } |
350 | 84 | void setHowSpecified(HowSpecified h) { hs = h; } |
351 | | |
352 | 58.6k | bool hasDataArgument() const { return hs == Arg; } |
353 | | |
354 | 137 | unsigned getArgIndex() const { |
355 | 137 | assert(hasDataArgument()); |
356 | 0 | return amt; |
357 | 137 | } |
358 | | |
359 | 1.27k | unsigned getConstantAmount() const { |
360 | 1.27k | assert(hs == Constant); |
361 | 0 | return amt; |
362 | 1.27k | } |
363 | | |
364 | 128 | const char *getStart() const { |
365 | | // We include the . character if it is given. |
366 | 128 | return start - UsesDotPrefix; |
367 | 128 | } |
368 | | |
369 | 37 | unsigned getConstantLength() const { |
370 | 37 | assert(hs == Constant); |
371 | 0 | return length + UsesDotPrefix; |
372 | 37 | } |
373 | | |
374 | | ArgType getArgType(ASTContext &Ctx) const; |
375 | | |
376 | | void toString(raw_ostream &os) const; |
377 | | |
378 | 4 | bool usesPositionalArg() const { return (bool) UsesPositionalArg; } |
379 | 2 | unsigned getPositionalArgIndex() const { |
380 | 2 | assert(hasDataArgument()); |
381 | 0 | return amt + 1; |
382 | 2 | } |
383 | | |
384 | 0 | bool usesDotPrefix() const { return UsesDotPrefix; } |
385 | 256 | void setUsesDotPrefix() { UsesDotPrefix = true; } |
386 | | |
387 | | private: |
388 | | const char *start; |
389 | | unsigned length; |
390 | | HowSpecified hs; |
391 | | unsigned amt; |
392 | | bool UsesPositionalArg : 1; |
393 | | bool UsesDotPrefix; |
394 | | }; |
395 | | |
396 | | |
397 | | class FormatSpecifier { |
398 | | protected: |
399 | | LengthModifier LM; |
400 | | OptionalAmount FieldWidth; |
401 | | ConversionSpecifier CS; |
402 | | OptionalAmount VectorNumElts; |
403 | | |
404 | | /// Positional arguments, an IEEE extension: |
405 | | /// IEEE Std 1003.1, 2004 Edition |
406 | | /// http://www.opengroup.org/onlinepubs/009695399/functions/printf.html |
407 | | bool UsesPositionalArg; |
408 | | unsigned argIndex; |
409 | | public: |
410 | | FormatSpecifier(bool isPrintf) |
411 | | : CS(isPrintf), VectorNumElts(false), |
412 | 51.9k | UsesPositionalArg(false), argIndex(0) {} |
413 | | |
414 | 6.97k | void setLengthModifier(LengthModifier lm) { |
415 | 6.97k | LM = lm; |
416 | 6.97k | } |
417 | | |
418 | 109 | void setUsesPositionalArg() { UsesPositionalArg = true; } |
419 | | |
420 | 33.5k | void setArgIndex(unsigned i) { |
421 | 33.5k | argIndex = i; |
422 | 33.5k | } |
423 | | |
424 | 38.1k | unsigned getArgIndex() const { |
425 | 38.1k | return argIndex; |
426 | 38.1k | } |
427 | | |
428 | 11 | unsigned getPositionalArgIndex() const { |
429 | 11 | return argIndex + 1; |
430 | 11 | } |
431 | | |
432 | 243 | const LengthModifier &getLengthModifier() const { |
433 | 243 | return LM; |
434 | 243 | } |
435 | | |
436 | 31.3k | const OptionalAmount &getFieldWidth() const { |
437 | 31.3k | return FieldWidth; |
438 | 31.3k | } |
439 | | |
440 | 166 | void setVectorNumElts(const OptionalAmount &Amt) { |
441 | 166 | VectorNumElts = Amt; |
442 | 166 | } |
443 | | |
444 | 0 | const OptionalAmount &getVectorNumElts() const { |
445 | 0 | return VectorNumElts; |
446 | 0 | } |
447 | | |
448 | 32.6k | void setFieldWidth(const OptionalAmount &Amt) { |
449 | 32.6k | FieldWidth = Amt; |
450 | 32.6k | } |
451 | | |
452 | 3.01k | bool usesPositionalArg() const { return UsesPositionalArg; } |
453 | | |
454 | | bool hasValidLengthModifier(const TargetInfo &Target, |
455 | | const LangOptions &LO) const; |
456 | | |
457 | | bool hasStandardLengthModifier() const; |
458 | | |
459 | | Optional<LengthModifier> getCorrectedLengthModifier() const; |
460 | | |
461 | | bool hasStandardConversionSpecifier(const LangOptions &LangOpt) const; |
462 | | |
463 | | bool hasStandardLengthConversionCombination() const; |
464 | | |
465 | | /// For a TypedefType QT, if it is a named integer type such as size_t, |
466 | | /// assign the appropriate value to LM and return true. |
467 | | static bool namedTypeToLengthModifier(QualType QT, LengthModifier &LM); |
468 | | }; |
469 | | |
470 | | } // end analyze_format_string namespace |
471 | | |
472 | | //===----------------------------------------------------------------------===// |
473 | | /// Pieces specific to fprintf format strings. |
474 | | |
475 | | namespace analyze_printf { |
476 | | |
477 | | class PrintfConversionSpecifier : |
478 | | public analyze_format_string::ConversionSpecifier { |
479 | | public: |
480 | | PrintfConversionSpecifier() |
481 | 0 | : ConversionSpecifier(true, nullptr, InvalidSpecifier) {} |
482 | | |
483 | | PrintfConversionSpecifier(const char *pos, Kind k) |
484 | 31.9k | : ConversionSpecifier(true, pos, k) {} |
485 | | |
486 | 21.7k | bool isObjCArg() const { return kind >= ObjCBeg && kind <= ObjCEnd57 ; } |
487 | 0 | bool isDoubleArg() const { return kind >= DoubleArgBeg && |
488 | 0 | kind <= DoubleArgEnd; } |
489 | | |
490 | 118k | static bool classof(const analyze_format_string::ConversionSpecifier *CS) { |
491 | 118k | return CS->isPrintfKind(); |
492 | 118k | } |
493 | | }; |
494 | | |
495 | | using analyze_format_string::ArgType; |
496 | | using analyze_format_string::LengthModifier; |
497 | | using analyze_format_string::OptionalAmount; |
498 | | using analyze_format_string::OptionalFlag; |
499 | | |
500 | | class PrintfSpecifier : public analyze_format_string::FormatSpecifier { |
501 | | OptionalFlag HasThousandsGrouping; // ''', POSIX extension. |
502 | | OptionalFlag IsLeftJustified; // '-' |
503 | | OptionalFlag HasPlusPrefix; // '+' |
504 | | OptionalFlag HasSpacePrefix; // ' ' |
505 | | OptionalFlag HasAlternativeForm; // '#' |
506 | | OptionalFlag HasLeadingZeroes; // '0' |
507 | | OptionalFlag HasObjCTechnicalTerm; // '[tt]' |
508 | | OptionalFlag IsPrivate; // '{private}' |
509 | | OptionalFlag IsPublic; // '{public}' |
510 | | OptionalFlag IsSensitive; // '{sensitive}' |
511 | | OptionalAmount Precision; |
512 | | StringRef MaskType; |
513 | | |
514 | | ArgType getScalarArgType(ASTContext &Ctx, bool IsObjCLiteral) const; |
515 | | |
516 | | public: |
517 | | PrintfSpecifier() |
518 | | : FormatSpecifier(/* isPrintf = */ true), HasThousandsGrouping("'"), |
519 | | IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "), |
520 | | HasAlternativeForm("#"), HasLeadingZeroes("0"), |
521 | | HasObjCTechnicalTerm("tt"), IsPrivate("private"), IsPublic("public"), |
522 | 49.8k | IsSensitive("sensitive") {} |
523 | | |
524 | | static PrintfSpecifier Parse(const char *beg, const char *end); |
525 | | |
526 | | // Methods for incrementally constructing the PrintfSpecifier. |
527 | 31.9k | void setConversionSpecifier(const PrintfConversionSpecifier &cs) { |
528 | 31.9k | CS = cs; |
529 | 31.9k | } |
530 | 22 | void setHasThousandsGrouping(const char *position) { |
531 | 22 | HasThousandsGrouping.setPosition(position); |
532 | 22 | } |
533 | 34 | void setIsLeftJustified(const char *position) { |
534 | 34 | IsLeftJustified.setPosition(position); |
535 | 34 | } |
536 | 93 | void setHasPlusPrefix(const char *position) { |
537 | 93 | HasPlusPrefix.setPosition(position); |
538 | 93 | } |
539 | 51 | void setHasSpacePrefix(const char *position) { |
540 | 51 | HasSpacePrefix.setPosition(position); |
541 | 51 | } |
542 | 61 | void setHasAlternativeForm(const char *position) { |
543 | 61 | HasAlternativeForm.setPosition(position); |
544 | 61 | } |
545 | 81 | void setHasLeadingZeros(const char *position) { |
546 | 81 | HasLeadingZeroes.setPosition(position); |
547 | 81 | } |
548 | 13 | void setHasObjCTechnicalTerm(const char *position) { |
549 | 13 | HasObjCTechnicalTerm.setPosition(position); |
550 | 13 | } |
551 | 25 | void setIsPrivate(const char *position) { IsPrivate.setPosition(position); } |
552 | 20 | void setIsPublic(const char *position) { IsPublic.setPosition(position); } |
553 | 8 | void setIsSensitive(const char *position) { |
554 | 8 | IsSensitive.setPosition(position); |
555 | 8 | } |
556 | 0 | void setUsesPositionalArg() { UsesPositionalArg = true; } |
557 | | |
558 | | // Methods for querying the format specifier. |
559 | | |
560 | 118k | const PrintfConversionSpecifier &getConversionSpecifier() const { |
561 | 118k | return cast<PrintfConversionSpecifier>(CS); |
562 | 118k | } |
563 | | |
564 | 256 | void setPrecision(const OptionalAmount &Amt) { |
565 | 256 | Precision = Amt; |
566 | 256 | Precision.setUsesDotPrefix(); |
567 | 256 | } |
568 | | |
569 | 29.9k | const OptionalAmount &getPrecision() const { |
570 | 29.9k | return Precision; |
571 | 29.9k | } |
572 | | |
573 | 29.6k | bool consumesDataArgument() const { |
574 | 29.6k | return getConversionSpecifier().consumesDataArgument(); |
575 | 29.6k | } |
576 | | |
577 | | /// Returns the builtin type that a data argument |
578 | | /// paired with this format specifier should have. This method |
579 | | /// will return null if the format specifier does not have |
580 | | /// a matching data argument or the matching argument matches |
581 | | /// more than one type. |
582 | | ArgType getArgType(ASTContext &Ctx, bool IsObjCLiteral) const; |
583 | | |
584 | 4 | const OptionalFlag &hasThousandsGrouping() const { |
585 | 4 | return HasThousandsGrouping; |
586 | 4 | } |
587 | 101 | const OptionalFlag &isLeftJustified() const { return IsLeftJustified; } |
588 | 363 | const OptionalFlag &hasPlusPrefix() const { return HasPlusPrefix; } |
589 | 319 | const OptionalFlag &hasAlternativeForm() const { return HasAlternativeForm; } |
590 | 26.5k | const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; } |
591 | 26.7k | const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; } |
592 | 0 | const OptionalFlag &hasObjCTechnicalTerm() const { return HasObjCTechnicalTerm; } |
593 | 26.4k | const OptionalFlag &isPrivate() const { return IsPrivate; } |
594 | 26.4k | const OptionalFlag &isPublic() const { return IsPublic; } |
595 | 3.00k | const OptionalFlag &isSensitive() const { return IsSensitive; } |
596 | 91.4k | bool usesPositionalArg() const { return UsesPositionalArg; } |
597 | | |
598 | 3.00k | StringRef getMaskType() const { return MaskType; } |
599 | 17 | void setMaskType(StringRef S) { MaskType = S; } |
600 | | |
601 | | /// Changes the specifier and length according to a QualType, retaining any |
602 | | /// flags or options. Returns true on success, or false when a conversion |
603 | | /// was not successful. |
604 | | bool fixType(QualType QT, const LangOptions &LangOpt, ASTContext &Ctx, |
605 | | bool IsObjCLiteral); |
606 | | |
607 | | void toString(raw_ostream &os) const; |
608 | | |
609 | | // Validation methods - to check if any element results in undefined behavior |
610 | | bool hasValidPlusPrefix() const; |
611 | | bool hasValidAlternativeForm() const; |
612 | | bool hasValidLeadingZeros() const; |
613 | | bool hasValidSpacePrefix() const; |
614 | | bool hasValidLeftJustified() const; |
615 | | bool hasValidThousandsGroupingPrefix() const; |
616 | | |
617 | | bool hasValidPrecision() const; |
618 | | bool hasValidFieldWidth() const; |
619 | | }; |
620 | | } // end analyze_printf namespace |
621 | | |
622 | | //===----------------------------------------------------------------------===// |
623 | | /// Pieces specific to fscanf format strings. |
624 | | |
625 | | namespace analyze_scanf { |
626 | | |
627 | | class ScanfConversionSpecifier : |
628 | | public analyze_format_string::ConversionSpecifier { |
629 | | public: |
630 | | ScanfConversionSpecifier() |
631 | 0 | : ConversionSpecifier(false, nullptr, InvalidSpecifier) {} |
632 | | |
633 | | ScanfConversionSpecifier(const char *pos, Kind k) |
634 | 1.95k | : ConversionSpecifier(false, pos, k) {} |
635 | | |
636 | 2.89k | static bool classof(const analyze_format_string::ConversionSpecifier *CS) { |
637 | 2.89k | return !CS->isPrintfKind(); |
638 | 2.89k | } |
639 | | }; |
640 | | |
641 | | using analyze_format_string::ArgType; |
642 | | using analyze_format_string::LengthModifier; |
643 | | using analyze_format_string::OptionalAmount; |
644 | | using analyze_format_string::OptionalFlag; |
645 | | |
646 | | class ScanfSpecifier : public analyze_format_string::FormatSpecifier { |
647 | | OptionalFlag SuppressAssignment; // '*' |
648 | | public: |
649 | | ScanfSpecifier() : |
650 | | FormatSpecifier(/* isPrintf = */ false), |
651 | 2.09k | SuppressAssignment("*") {} |
652 | | |
653 | 22 | void setSuppressAssignment(const char *position) { |
654 | 22 | SuppressAssignment.setPosition(position); |
655 | 22 | } |
656 | | |
657 | 1.90k | const OptionalFlag &getSuppressAssignment() const { |
658 | 1.90k | return SuppressAssignment; |
659 | 1.90k | } |
660 | | |
661 | 1.95k | void setConversionSpecifier(const ScanfConversionSpecifier &cs) { |
662 | 1.95k | CS = cs; |
663 | 1.95k | } |
664 | | |
665 | 2.89k | const ScanfConversionSpecifier &getConversionSpecifier() const { |
666 | 2.89k | return cast<ScanfConversionSpecifier>(CS); |
667 | 2.89k | } |
668 | | |
669 | 2.85k | bool consumesDataArgument() const { |
670 | 2.85k | return CS.consumesDataArgument() && !SuppressAssignment2.84k ; |
671 | 2.85k | } |
672 | | |
673 | | ArgType getArgType(ASTContext &Ctx) const; |
674 | | |
675 | | bool fixType(QualType QT, QualType RawQT, const LangOptions &LangOpt, |
676 | | ASTContext &Ctx); |
677 | | |
678 | | void toString(raw_ostream &os) const; |
679 | | |
680 | | static ScanfSpecifier Parse(const char *beg, const char *end); |
681 | | }; |
682 | | |
683 | | } // end analyze_scanf namespace |
684 | | |
685 | | //===----------------------------------------------------------------------===// |
686 | | // Parsing and processing of format strings (both fprintf and fscanf). |
687 | | |
688 | | namespace analyze_format_string { |
689 | | |
690 | | enum PositionContext { FieldWidthPos = 0, PrecisionPos = 1 }; |
691 | | |
692 | | class FormatStringHandler { |
693 | | public: |
694 | 23.7k | FormatStringHandler() {} |
695 | | virtual ~FormatStringHandler(); |
696 | | |
697 | 0 | virtual void HandleNullChar(const char *nullCharacter) {} |
698 | | |
699 | 12 | virtual void HandlePosition(const char *startPos, unsigned posLen) {} |
700 | | |
701 | | virtual void HandleInvalidPosition(const char *startPos, unsigned posLen, |
702 | 0 | PositionContext p) {} |
703 | | |
704 | 0 | virtual void HandleZeroPosition(const char *startPos, unsigned posLen) {} |
705 | | |
706 | | virtual void HandleIncompleteSpecifier(const char *startSpecifier, |
707 | 8 | unsigned specifierLen) {} |
708 | | |
709 | | virtual void HandleEmptyObjCModifierFlag(const char *startFlags, |
710 | 0 | unsigned flagsLen) {} |
711 | | |
712 | | virtual void HandleInvalidObjCModifierFlag(const char *startFlag, |
713 | 0 | unsigned flagLen) {} |
714 | | |
715 | | virtual void HandleObjCFlagsWithNonObjCConversion(const char *flagsStart, |
716 | | const char *flagsEnd, |
717 | 1 | const char *conversionPosition) {} |
718 | | // Printf-specific handlers. |
719 | | |
720 | | virtual bool HandleInvalidPrintfConversionSpecifier( |
721 | | const analyze_printf::PrintfSpecifier &FS, |
722 | | const char *startSpecifier, |
723 | 13 | unsigned specifierLen) { |
724 | 13 | return true; |
725 | 13 | } |
726 | | |
727 | | virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, |
728 | | const char *startSpecifier, |
729 | | unsigned specifierLen, |
730 | 0 | const TargetInfo &Target) { |
731 | 0 | return true; |
732 | 0 | } |
733 | | |
734 | | /// Handle mask types whose sizes are not between one and eight bytes. |
735 | 0 | virtual void handleInvalidMaskType(StringRef MaskType) {} |
736 | | |
737 | | // Scanf-specific handlers. |
738 | | |
739 | | virtual bool HandleInvalidScanfConversionSpecifier( |
740 | | const analyze_scanf::ScanfSpecifier &FS, |
741 | | const char *startSpecifier, |
742 | 12 | unsigned specifierLen) { |
743 | 12 | return true; |
744 | 12 | } |
745 | | |
746 | | virtual bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS, |
747 | | const char *startSpecifier, |
748 | 0 | unsigned specifierLen) { |
749 | 0 | return true; |
750 | 0 | } |
751 | | |
752 | 4 | virtual void HandleIncompleteScanList(const char *start, const char *end) {} |
753 | | }; |
754 | | |
755 | | bool ParsePrintfString(FormatStringHandler &H, |
756 | | const char *beg, const char *end, const LangOptions &LO, |
757 | | const TargetInfo &Target, bool isFreeBSDKPrintf); |
758 | | |
759 | | bool ParseFormatStringHasSArg(const char *beg, const char *end, |
760 | | const LangOptions &LO, const TargetInfo &Target); |
761 | | |
762 | | bool ParseScanfString(FormatStringHandler &H, |
763 | | const char *beg, const char *end, const LangOptions &LO, |
764 | | const TargetInfo &Target); |
765 | | |
766 | | /// Return true if the given string has at least one formatting specifier. |
767 | | bool parseFormatStringHasFormattingSpecifiers(const char *Begin, |
768 | | const char *End, |
769 | | const LangOptions &LO, |
770 | | const TargetInfo &Target); |
771 | | |
772 | | } // end analyze_format_string namespace |
773 | | } // end clang namespace |
774 | | #endif |