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 |