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 | // ISO/IEC TR 18037 (fixed-point) specific specifiers. |
175 | kArg, // %k for signed accum types |
176 | KArg, // %K for unsigned accum types |
177 | rArg, // %r for signed fract types |
178 | RArg, // %R for unsigned fract types |
179 | FixedPointArgBeg = kArg, |
180 | FixedPointArgEnd = RArg, |
181 | |
182 | // Objective-C specific specifiers. |
183 | ObjCObjArg, // '@' |
184 | ObjCBeg = ObjCObjArg, |
185 | ObjCEnd = ObjCObjArg, |
186 | |
187 | // FreeBSD kernel specific specifiers. |
188 | FreeBSDbArg, |
189 | FreeBSDDArg, |
190 | FreeBSDrArg, |
191 | FreeBSDyArg, |
192 | |
193 | // GlibC specific specifiers. |
194 | PrintErrno, // 'm' |
195 | |
196 | PrintfConvBeg = ObjCObjArg, |
197 | PrintfConvEnd = PrintErrno, |
198 | |
199 | // ** Scanf-specific ** |
200 | ScanListArg, // '[' |
201 | ScanfConvBeg = ScanListArg, |
202 | ScanfConvEnd = ScanListArg |
203 | }; |
204 | |
205 | ConversionSpecifier(bool isPrintf = true) |
206 | : IsPrintf(isPrintf), Position(nullptr), EndScanList(nullptr), |
207 | kind(InvalidSpecifier) {} |
208 | |
209 | ConversionSpecifier(bool isPrintf, const char *pos, Kind k) |
210 | : IsPrintf(isPrintf), Position(pos), EndScanList(nullptr), kind(k) {} |
211 | |
212 | const char *getStart() const { |
213 | return Position; |
214 | } |
215 | |
216 | StringRef getCharacters() const { |
217 | return StringRef(getStart(), getLength()); |
218 | } |
219 | |
220 | bool consumesDataArgument() const { |
221 | switch (kind) { |
222 | case PrintErrno: |
223 | assert(IsPrintf); |
224 | return false; |
225 | case PercentArg: |
226 | return false; |
227 | case InvalidSpecifier: |
228 | return false; |
229 | default: |
230 | return true; |
231 | } |
232 | } |
233 | |
234 | Kind getKind() const { return kind; } |
235 | void setKind(Kind k) { kind = k; } |
236 | unsigned getLength() const { |
237 | return EndScanList ? EndScanList - Position : 1; |
238 | } |
239 | void setEndScanList(const char *pos) { EndScanList = pos; } |
240 | |
241 | bool isIntArg() const { return (kind >= IntArgBeg && kind <= IntArgEnd) || |
242 | kind == FreeBSDrArg || kind == FreeBSDyArg; } |
243 | bool isUIntArg() const { return kind >= UIntArgBeg && kind <= UIntArgEnd; } |
244 | bool isAnyIntArg() const { return kind >= IntArgBeg && kind <= UIntArgEnd; } |
245 | bool isDoubleArg() const { |
246 | return kind >= DoubleArgBeg && kind <= DoubleArgEnd; |
247 | } |
248 | bool isFixedPointArg() const { |
249 | return kind >= FixedPointArgBeg && kind <= FixedPointArgEnd; |
250 | } |
251 | |
252 | const char *toString() const; |
253 | |
254 | bool isPrintfKind() const { return IsPrintf; } |
255 | |
256 | std::optional<ConversionSpecifier> getStandardSpecifier() const; |
257 | |
258 | protected: |
259 | bool IsPrintf; |
260 | const char *Position; |
261 | const char *EndScanList; |
262 | Kind kind; |
263 | }; |
264 | |
265 | class ArgType { |
266 | public: |
267 | enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy, |
268 | AnyCharTy, CStrTy, WCStrTy, WIntTy }; |
269 | |
270 | /// How well a given conversion specifier matches its argument. |
271 | enum MatchKind { |
272 | /// The conversion specifier and the argument types are incompatible. For |
273 | /// instance, "%d" and float. |
274 | NoMatch = 0, |
275 | /// The conversion specifier and the argument type are compatible. For |
276 | /// instance, "%d" and int. |
277 | Match = 1, |
278 | /// The conversion specifier and the argument type are compatible because of |
279 | /// default argument promotions. For instance, "%hhd" and int. |
280 | MatchPromotion, |
281 | /// The conversion specifier and the argument type are compatible but still |
282 | /// seems likely to be an error. For instanace, "%hhd" and short. |
283 | NoMatchPromotionTypeConfusion, |
284 | /// The conversion specifier and the argument type are disallowed by the C |
285 | /// standard, but are in practice harmless. For instance, "%p" and int*. |
286 | NoMatchPedantic, |
287 | /// The conversion specifier and the argument type have different sign. |
288 | NoMatchSignedness, |
289 | /// The conversion specifier and the argument type are compatible, but still |
290 | /// seems likely to be an error. For instance, "%hd" and _Bool. |
291 | NoMatchTypeConfusion, |
292 | }; |
293 | |
294 | private: |
295 | const Kind K; |
296 | QualType T; |
297 | const char *Name = nullptr; |
298 | bool Ptr = false; |
299 | |
300 | /// The TypeKind identifies certain well-known types like size_t and |
301 | /// ptrdiff_t. |
302 | enum class TypeKind { DontCare, SizeT, PtrdiffT }; |
303 | TypeKind TK = TypeKind::DontCare; |
304 | |
305 | public: |
306 | ArgType(Kind K = UnknownTy, const char *N = nullptr) : K(K), Name(N) {} |
307 | ArgType(QualType T, const char *N = nullptr) : K(SpecificTy), T(T), Name(N) {} |
308 | ArgType(CanQualType T) : K(SpecificTy), T(T) {} |
309 | |
310 | static ArgType Invalid() { return ArgType(InvalidTy); } |
311 | bool isValid() const { return K != InvalidTy; } |
312 | |
313 | bool isSizeT() const { return TK == TypeKind::SizeT; } |
314 | |
315 | bool isPtrdiffT() const { return TK == TypeKind::PtrdiffT; } |
316 | |
317 | /// Create an ArgType which corresponds to the type pointer to A. |
318 | static ArgType PtrTo(const ArgType& A) { |
319 | assert(A.K >= InvalidTy && "ArgType cannot be pointer to invalid/unknown" ); |
320 | ArgType Res = A; |
321 | Res.Ptr = true; |
322 | return Res; |
323 | } |
324 | |
325 | /// Create an ArgType which corresponds to the size_t/ssize_t type. |
326 | static ArgType makeSizeT(const ArgType &A) { |
327 | ArgType Res = A; |
328 | Res.TK = TypeKind::SizeT; |
329 | return Res; |
330 | } |
331 | |
332 | /// Create an ArgType which corresponds to the ptrdiff_t/unsigned ptrdiff_t |
333 | /// type. |
334 | static ArgType makePtrdiffT(const ArgType &A) { |
335 | ArgType Res = A; |
336 | Res.TK = TypeKind::PtrdiffT; |
337 | return Res; |
338 | } |
339 | |
340 | MatchKind matchesType(ASTContext &C, QualType argTy) const; |
341 | |
342 | QualType getRepresentativeType(ASTContext &C) const; |
343 | |
344 | ArgType makeVectorType(ASTContext &C, unsigned NumElts) const; |
345 | |
346 | std::string getRepresentativeTypeName(ASTContext &C) const; |
347 | }; |
348 | |
349 | class OptionalAmount { |
350 | public: |
351 | enum HowSpecified { NotSpecified, Constant, Arg, Invalid }; |
352 | |
353 | OptionalAmount(HowSpecified howSpecified, |
354 | unsigned amount, |
355 | const char *amountStart, |
356 | unsigned amountLength, |
357 | bool usesPositionalArg) |
358 | : start(amountStart), length(amountLength), hs(howSpecified), amt(amount), |
359 | UsesPositionalArg(usesPositionalArg), UsesDotPrefix(false) {} |
360 | |
361 | OptionalAmount(bool valid = true) |
362 | : start(nullptr),length(0), hs(valid ? NotSpecified : Invalid), amt(0), |
363 | UsesPositionalArg(false), UsesDotPrefix(false) {} |
364 | |
365 | explicit OptionalAmount(unsigned Amount) |
366 | : start(nullptr), length(0), hs(Constant), amt(Amount), |
367 | UsesPositionalArg(false), UsesDotPrefix(false) {} |
368 | |
369 | bool isInvalid() const { |
370 | return hs == Invalid; |
371 | } |
372 | |
373 | HowSpecified getHowSpecified() const { return hs; } |
374 | void setHowSpecified(HowSpecified h) { hs = h; } |
375 | |
376 | bool hasDataArgument() const { return hs == Arg; } |
377 | |
378 | unsigned getArgIndex() const { |
379 | assert(hasDataArgument()); |
380 | return amt; |
381 | } |
382 | |
383 | unsigned getConstantAmount() const { |
384 | assert(hs == Constant); |
385 | return amt; |
386 | } |
387 | |
388 | const char *getStart() const { |
389 | // We include the . character if it is given. |
390 | return start - UsesDotPrefix; |
391 | } |
392 | |
393 | unsigned getConstantLength() const { |
394 | assert(hs == Constant); |
395 | return length + UsesDotPrefix; |
396 | } |
397 | |
398 | ArgType getArgType(ASTContext &Ctx) const; |
399 | |
400 | void toString(raw_ostream &os) const; |
401 | |
402 | bool usesPositionalArg() const { return (bool) UsesPositionalArg; } |
403 | unsigned getPositionalArgIndex() const { |
404 | assert(hasDataArgument()); |
405 | return amt + 1; |
406 | } |
407 | |
408 | bool usesDotPrefix() const { return UsesDotPrefix; } |
409 | void setUsesDotPrefix() { UsesDotPrefix = true; } |
410 | |
411 | private: |
412 | const char *start; |
413 | unsigned length; |
414 | HowSpecified hs; |
415 | unsigned amt; |
416 | bool UsesPositionalArg : 1; |
417 | bool UsesDotPrefix; |
418 | }; |
419 | |
420 | |
421 | class FormatSpecifier { |
422 | protected: |
423 | LengthModifier LM; |
424 | OptionalAmount FieldWidth; |
425 | ConversionSpecifier CS; |
426 | OptionalAmount VectorNumElts; |
427 | |
428 | /// Positional arguments, an IEEE extension: |
429 | /// IEEE Std 1003.1, 2004 Edition |
430 | /// http://www.opengroup.org/onlinepubs/009695399/functions/printf.html |
431 | bool UsesPositionalArg; |
432 | unsigned argIndex; |
433 | public: |
434 | FormatSpecifier(bool isPrintf) |
435 | : CS(isPrintf), VectorNumElts(false), |
436 | UsesPositionalArg(false), argIndex(0) {} |
437 | |
438 | void setLengthModifier(LengthModifier lm) { |
439 | LM = lm; |
440 | } |
441 | |
442 | void setUsesPositionalArg() { UsesPositionalArg = true; } |
443 | |
444 | void setArgIndex(unsigned i) { |
445 | argIndex = i; |
446 | } |
447 | |
448 | unsigned getArgIndex() const { |
449 | return argIndex; |
450 | } |
451 | |
452 | unsigned getPositionalArgIndex() const { |
453 | return argIndex + 1; |
454 | } |
455 | |
456 | const LengthModifier &getLengthModifier() const { |
457 | return LM; |
458 | } |
459 | |
460 | const OptionalAmount &getFieldWidth() const { |
461 | return FieldWidth; |
462 | } |
463 | |
464 | void setVectorNumElts(const OptionalAmount &Amt) { |
465 | VectorNumElts = Amt; |
466 | } |
467 | |
468 | const OptionalAmount &getVectorNumElts() const { |
469 | return VectorNumElts; |
470 | } |
471 | |
472 | void setFieldWidth(const OptionalAmount &Amt) { |
473 | FieldWidth = Amt; |
474 | } |
475 | |
476 | bool usesPositionalArg() const { return UsesPositionalArg; } |
477 | |
478 | bool hasValidLengthModifier(const TargetInfo &Target, |
479 | const LangOptions &LO) const; |
480 | |
481 | bool hasStandardLengthModifier() const; |
482 | |
483 | std::optional<LengthModifier> getCorrectedLengthModifier() const; |
484 | |
485 | bool hasStandardConversionSpecifier(const LangOptions &LangOpt) const; |
486 | |
487 | bool hasStandardLengthConversionCombination() const; |
488 | |
489 | /// For a TypedefType QT, if it is a named integer type such as size_t, |
490 | /// assign the appropriate value to LM and return true. |
491 | static bool namedTypeToLengthModifier(QualType QT, LengthModifier &LM); |
492 | }; |
493 | |
494 | } // end analyze_format_string namespace |
495 | |
496 | //===----------------------------------------------------------------------===// |
497 | /// Pieces specific to fprintf format strings. |
498 | |
499 | namespace analyze_printf { |
500 | |
501 | class PrintfConversionSpecifier : |
502 | public analyze_format_string::ConversionSpecifier { |
503 | public: |
504 | PrintfConversionSpecifier() |
505 | : ConversionSpecifier(true, nullptr, InvalidSpecifier) {} |
506 | |
507 | PrintfConversionSpecifier(const char *pos, Kind k) |
508 | : ConversionSpecifier(true, pos, k) {} |
509 | |
510 | bool isObjCArg() const { return kind >= ObjCBeg && kind <= ObjCEnd; } |
511 | bool isDoubleArg() const { return kind >= DoubleArgBeg && |
512 | kind <= DoubleArgEnd; } |
513 | |
514 | static bool classof(const analyze_format_string::ConversionSpecifier *CS) { |
515 | return CS->isPrintfKind(); |
516 | } |
517 | }; |
518 | |
519 | using analyze_format_string::ArgType; |
520 | using analyze_format_string::LengthModifier; |
521 | using analyze_format_string::OptionalAmount; |
522 | using analyze_format_string::OptionalFlag; |
523 | |
524 | class PrintfSpecifier : public analyze_format_string::FormatSpecifier { |
525 | OptionalFlag HasThousandsGrouping; // ''', POSIX extension. |
526 | OptionalFlag IsLeftJustified; // '-' |
527 | OptionalFlag HasPlusPrefix; // '+' |
528 | OptionalFlag HasSpacePrefix; // ' ' |
529 | OptionalFlag HasAlternativeForm; // '#' |
530 | OptionalFlag HasLeadingZeroes; // '0' |
531 | OptionalFlag HasObjCTechnicalTerm; // '[tt]' |
532 | OptionalFlag IsPrivate; // '{private}' |
533 | OptionalFlag IsPublic; // '{public}' |
534 | OptionalFlag IsSensitive; // '{sensitive}' |
535 | OptionalAmount Precision; |
536 | StringRef MaskType; |
537 | |
538 | ArgType getScalarArgType(ASTContext &Ctx, bool IsObjCLiteral) const; |
539 | |
540 | public: |
541 | PrintfSpecifier() |
542 | : FormatSpecifier(/* isPrintf = */ true), HasThousandsGrouping("'" ), |
543 | IsLeftJustified("-" ), HasPlusPrefix("+" ), HasSpacePrefix(" " ), |
544 | HasAlternativeForm("#" ), HasLeadingZeroes("0" ), |
545 | HasObjCTechnicalTerm("tt" ), IsPrivate("private" ), IsPublic("public" ), |
546 | IsSensitive("sensitive" ) {} |
547 | |
548 | static PrintfSpecifier Parse(const char *beg, const char *end); |
549 | |
550 | // Methods for incrementally constructing the PrintfSpecifier. |
551 | void setConversionSpecifier(const PrintfConversionSpecifier &cs) { |
552 | CS = cs; |
553 | } |
554 | void setHasThousandsGrouping(const char *position) { |
555 | HasThousandsGrouping.setPosition(position); |
556 | } |
557 | void setIsLeftJustified(const char *position) { |
558 | IsLeftJustified.setPosition(position); |
559 | } |
560 | void setHasPlusPrefix(const char *position) { |
561 | HasPlusPrefix.setPosition(position); |
562 | } |
563 | void setHasSpacePrefix(const char *position) { |
564 | HasSpacePrefix.setPosition(position); |
565 | } |
566 | void setHasAlternativeForm(const char *position) { |
567 | HasAlternativeForm.setPosition(position); |
568 | } |
569 | void setHasLeadingZeros(const char *position) { |
570 | HasLeadingZeroes.setPosition(position); |
571 | } |
572 | void setHasObjCTechnicalTerm(const char *position) { |
573 | HasObjCTechnicalTerm.setPosition(position); |
574 | } |
575 | void setIsPrivate(const char *position) { IsPrivate.setPosition(position); } |
576 | void setIsPublic(const char *position) { IsPublic.setPosition(position); } |
577 | void setIsSensitive(const char *position) { |
578 | IsSensitive.setPosition(position); |
579 | } |
580 | void setUsesPositionalArg() { UsesPositionalArg = true; } |
581 | |
582 | // Methods for querying the format specifier. |
583 | |
584 | const PrintfConversionSpecifier &getConversionSpecifier() const { |
585 | return cast<PrintfConversionSpecifier>(Val: CS); |
586 | } |
587 | |
588 | void setPrecision(const OptionalAmount &Amt) { |
589 | Precision = Amt; |
590 | Precision.setUsesDotPrefix(); |
591 | } |
592 | |
593 | const OptionalAmount &getPrecision() const { |
594 | return Precision; |
595 | } |
596 | |
597 | bool consumesDataArgument() const { |
598 | return getConversionSpecifier().consumesDataArgument(); |
599 | } |
600 | |
601 | /// Returns the builtin type that a data argument |
602 | /// paired with this format specifier should have. This method |
603 | /// will return null if the format specifier does not have |
604 | /// a matching data argument or the matching argument matches |
605 | /// more than one type. |
606 | ArgType getArgType(ASTContext &Ctx, bool IsObjCLiteral) const; |
607 | |
608 | const OptionalFlag &hasThousandsGrouping() const { |
609 | return HasThousandsGrouping; |
610 | } |
611 | const OptionalFlag &isLeftJustified() const { return IsLeftJustified; } |
612 | const OptionalFlag &hasPlusPrefix() const { return HasPlusPrefix; } |
613 | const OptionalFlag &hasAlternativeForm() const { return HasAlternativeForm; } |
614 | const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; } |
615 | const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; } |
616 | const OptionalFlag &hasObjCTechnicalTerm() const { return HasObjCTechnicalTerm; } |
617 | const OptionalFlag &isPrivate() const { return IsPrivate; } |
618 | const OptionalFlag &isPublic() const { return IsPublic; } |
619 | const OptionalFlag &isSensitive() const { return IsSensitive; } |
620 | bool usesPositionalArg() const { return UsesPositionalArg; } |
621 | |
622 | StringRef getMaskType() const { return MaskType; } |
623 | void setMaskType(StringRef S) { MaskType = S; } |
624 | |
625 | /// Changes the specifier and length according to a QualType, retaining any |
626 | /// flags or options. Returns true on success, or false when a conversion |
627 | /// was not successful. |
628 | bool fixType(QualType QT, const LangOptions &LangOpt, ASTContext &Ctx, |
629 | bool IsObjCLiteral); |
630 | |
631 | void toString(raw_ostream &os) const; |
632 | |
633 | // Validation methods - to check if any element results in undefined behavior |
634 | bool hasValidPlusPrefix() const; |
635 | bool hasValidAlternativeForm() const; |
636 | bool hasValidLeadingZeros() const; |
637 | bool hasValidSpacePrefix() const; |
638 | bool hasValidLeftJustified() const; |
639 | bool hasValidThousandsGroupingPrefix() const; |
640 | |
641 | bool hasValidPrecision() const; |
642 | bool hasValidFieldWidth() const; |
643 | }; |
644 | } // end analyze_printf namespace |
645 | |
646 | //===----------------------------------------------------------------------===// |
647 | /// Pieces specific to fscanf format strings. |
648 | |
649 | namespace analyze_scanf { |
650 | |
651 | class ScanfConversionSpecifier : |
652 | public analyze_format_string::ConversionSpecifier { |
653 | public: |
654 | ScanfConversionSpecifier() |
655 | : ConversionSpecifier(false, nullptr, InvalidSpecifier) {} |
656 | |
657 | ScanfConversionSpecifier(const char *pos, Kind k) |
658 | : ConversionSpecifier(false, pos, k) {} |
659 | |
660 | static bool classof(const analyze_format_string::ConversionSpecifier *CS) { |
661 | return !CS->isPrintfKind(); |
662 | } |
663 | }; |
664 | |
665 | using analyze_format_string::ArgType; |
666 | using analyze_format_string::LengthModifier; |
667 | using analyze_format_string::OptionalAmount; |
668 | using analyze_format_string::OptionalFlag; |
669 | |
670 | class ScanfSpecifier : public analyze_format_string::FormatSpecifier { |
671 | OptionalFlag SuppressAssignment; // '*' |
672 | public: |
673 | ScanfSpecifier() : |
674 | FormatSpecifier(/* isPrintf = */ false), |
675 | SuppressAssignment("*" ) {} |
676 | |
677 | void setSuppressAssignment(const char *position) { |
678 | SuppressAssignment.setPosition(position); |
679 | } |
680 | |
681 | const OptionalFlag &getSuppressAssignment() const { |
682 | return SuppressAssignment; |
683 | } |
684 | |
685 | void setConversionSpecifier(const ScanfConversionSpecifier &cs) { |
686 | CS = cs; |
687 | } |
688 | |
689 | const ScanfConversionSpecifier &getConversionSpecifier() const { |
690 | return cast<ScanfConversionSpecifier>(Val: CS); |
691 | } |
692 | |
693 | bool consumesDataArgument() const { |
694 | return CS.consumesDataArgument() && !SuppressAssignment; |
695 | } |
696 | |
697 | ArgType getArgType(ASTContext &Ctx) const; |
698 | |
699 | bool fixType(QualType QT, QualType RawQT, const LangOptions &LangOpt, |
700 | ASTContext &Ctx); |
701 | |
702 | void toString(raw_ostream &os) const; |
703 | |
704 | static ScanfSpecifier Parse(const char *beg, const char *end); |
705 | }; |
706 | |
707 | } // end analyze_scanf namespace |
708 | |
709 | //===----------------------------------------------------------------------===// |
710 | // Parsing and processing of format strings (both fprintf and fscanf). |
711 | |
712 | namespace analyze_format_string { |
713 | |
714 | enum PositionContext { FieldWidthPos = 0, PrecisionPos = 1 }; |
715 | |
716 | class FormatStringHandler { |
717 | public: |
718 | FormatStringHandler() {} |
719 | virtual ~FormatStringHandler(); |
720 | |
721 | virtual void HandleNullChar(const char *nullCharacter) {} |
722 | |
723 | virtual void HandlePosition(const char *startPos, unsigned posLen) {} |
724 | |
725 | virtual void HandleInvalidPosition(const char *startPos, unsigned posLen, |
726 | PositionContext p) {} |
727 | |
728 | virtual void HandleZeroPosition(const char *startPos, unsigned posLen) {} |
729 | |
730 | virtual void HandleIncompleteSpecifier(const char *startSpecifier, |
731 | unsigned specifierLen) {} |
732 | |
733 | virtual void HandleEmptyObjCModifierFlag(const char *startFlags, |
734 | unsigned flagsLen) {} |
735 | |
736 | virtual void HandleInvalidObjCModifierFlag(const char *startFlag, |
737 | unsigned flagLen) {} |
738 | |
739 | virtual void HandleObjCFlagsWithNonObjCConversion(const char *flagsStart, |
740 | const char *flagsEnd, |
741 | const char *conversionPosition) {} |
742 | // Printf-specific handlers. |
743 | |
744 | virtual bool HandleInvalidPrintfConversionSpecifier( |
745 | const analyze_printf::PrintfSpecifier &FS, |
746 | const char *startSpecifier, |
747 | unsigned specifierLen) { |
748 | return true; |
749 | } |
750 | |
751 | virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, |
752 | const char *startSpecifier, |
753 | unsigned specifierLen, |
754 | const TargetInfo &Target) { |
755 | return true; |
756 | } |
757 | |
758 | /// Handle mask types whose sizes are not between one and eight bytes. |
759 | virtual void handleInvalidMaskType(StringRef MaskType) {} |
760 | |
761 | // Scanf-specific handlers. |
762 | |
763 | virtual bool HandleInvalidScanfConversionSpecifier( |
764 | const analyze_scanf::ScanfSpecifier &FS, |
765 | const char *startSpecifier, |
766 | unsigned specifierLen) { |
767 | return true; |
768 | } |
769 | |
770 | virtual bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS, |
771 | const char *startSpecifier, |
772 | unsigned specifierLen) { |
773 | return true; |
774 | } |
775 | |
776 | virtual void HandleIncompleteScanList(const char *start, const char *end) {} |
777 | }; |
778 | |
779 | bool ParsePrintfString(FormatStringHandler &H, |
780 | const char *beg, const char *end, const LangOptions &LO, |
781 | const TargetInfo &Target, bool isFreeBSDKPrintf); |
782 | |
783 | bool ParseFormatStringHasSArg(const char *beg, const char *end, |
784 | const LangOptions &LO, const TargetInfo &Target); |
785 | |
786 | bool ParseScanfString(FormatStringHandler &H, |
787 | const char *beg, const char *end, const LangOptions &LO, |
788 | const TargetInfo &Target); |
789 | |
790 | /// Return true if the given string has at least one formatting specifier. |
791 | bool parseFormatStringHasFormattingSpecifiers(const char *Begin, |
792 | const char *End, |
793 | const LangOptions &LO, |
794 | const TargetInfo &Target); |
795 | |
796 | } // end analyze_format_string namespace |
797 | } // end clang namespace |
798 | #endif |
799 | |