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