10b57cec5SDimitry Andric //= FormatString.h - Analysis of printf/fprintf format strings --*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines APIs for analyzing the format strings of printf, fscanf, 100b57cec5SDimitry Andric // and friends. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric // The structure of format strings for fprintf are described in C99 7.19.6.1. 130b57cec5SDimitry Andric // 140b57cec5SDimitry Andric // The structure of format strings for fscanf are described in C99 7.19.6.2. 150b57cec5SDimitry Andric // 160b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_FORMATSTRING_H 190b57cec5SDimitry Andric #define LLVM_CLANG_ANALYSIS_ANALYSES_FORMATSTRING_H 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric #include "clang/AST/CanonicalType.h" 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric namespace clang { 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric class TargetInfo; 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 280b57cec5SDimitry Andric /// Common components of both fprintf and fscanf format strings. 290b57cec5SDimitry Andric namespace analyze_format_string { 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric /// Class representing optional flags with location and representation 320b57cec5SDimitry Andric /// information. 330b57cec5SDimitry Andric class OptionalFlag { 340b57cec5SDimitry Andric public: 350b57cec5SDimitry Andric OptionalFlag(const char *Representation) 360b57cec5SDimitry Andric : representation(Representation), flag(false) {} 370b57cec5SDimitry Andric bool isSet() const { return flag; } 380b57cec5SDimitry Andric void set() { flag = true; } 390b57cec5SDimitry Andric void clear() { flag = false; } 400b57cec5SDimitry Andric void setPosition(const char *position) { 410b57cec5SDimitry Andric assert(position); 420b57cec5SDimitry Andric flag = true; 430b57cec5SDimitry Andric this->position = position; 440b57cec5SDimitry Andric } 450b57cec5SDimitry Andric const char *getPosition() const { 460b57cec5SDimitry Andric assert(position); 470b57cec5SDimitry Andric return position; 480b57cec5SDimitry Andric } 490b57cec5SDimitry Andric const char *toString() const { return representation; } 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric // Overloaded operators for bool like qualities 520b57cec5SDimitry Andric explicit operator bool() const { return flag; } 530b57cec5SDimitry Andric OptionalFlag& operator=(const bool &rhs) { 540b57cec5SDimitry Andric flag = rhs; 550b57cec5SDimitry Andric return *this; // Return a reference to myself. 560b57cec5SDimitry Andric } 570b57cec5SDimitry Andric private: 580b57cec5SDimitry Andric const char *representation; 590b57cec5SDimitry Andric const char *position; 600b57cec5SDimitry Andric bool flag; 610b57cec5SDimitry Andric }; 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric /// Represents the length modifier in a format string in scanf/printf. 640b57cec5SDimitry Andric class LengthModifier { 650b57cec5SDimitry Andric public: 660b57cec5SDimitry Andric enum Kind { 670b57cec5SDimitry Andric None, 680b57cec5SDimitry Andric AsChar, // 'hh' 690b57cec5SDimitry Andric AsShort, // 'h' 700b57cec5SDimitry Andric AsShortLong, // 'hl' (OpenCL float/int vector element) 710b57cec5SDimitry Andric AsLong, // 'l' 720b57cec5SDimitry Andric AsLongLong, // 'll' 730b57cec5SDimitry Andric AsQuad, // 'q' (BSD, deprecated, for 64-bit integer types) 740b57cec5SDimitry Andric AsIntMax, // 'j' 750b57cec5SDimitry Andric AsSizeT, // 'z' 760b57cec5SDimitry Andric AsPtrDiff, // 't' 770b57cec5SDimitry Andric AsInt32, // 'I32' (MSVCRT, like __int32) 780b57cec5SDimitry Andric AsInt3264, // 'I' (MSVCRT, like __int3264 from MIDL) 790b57cec5SDimitry Andric AsInt64, // 'I64' (MSVCRT, like __int64) 800b57cec5SDimitry Andric AsLongDouble, // 'L' 810b57cec5SDimitry Andric AsAllocate, // for '%as', GNU extension to C90 scanf 820b57cec5SDimitry Andric AsMAllocate, // for '%ms', GNU extension to scanf 830b57cec5SDimitry Andric AsWide, // 'w' (MSVCRT, like l but only for c, C, s, S, or Z 840b57cec5SDimitry Andric AsWideChar = AsLong // for '%ls', only makes sense for printf 850b57cec5SDimitry Andric }; 860b57cec5SDimitry Andric 870b57cec5SDimitry Andric LengthModifier() 880b57cec5SDimitry Andric : Position(nullptr), kind(None) {} 890b57cec5SDimitry Andric LengthModifier(const char *pos, Kind k) 900b57cec5SDimitry Andric : Position(pos), kind(k) {} 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric const char *getStart() const { 930b57cec5SDimitry Andric return Position; 940b57cec5SDimitry Andric } 950b57cec5SDimitry Andric 960b57cec5SDimitry Andric unsigned getLength() const { 970b57cec5SDimitry Andric switch (kind) { 980b57cec5SDimitry Andric default: 990b57cec5SDimitry Andric return 1; 1000b57cec5SDimitry Andric case AsLongLong: 1010b57cec5SDimitry Andric case AsChar: 1020b57cec5SDimitry Andric return 2; 1030b57cec5SDimitry Andric case AsInt32: 1040b57cec5SDimitry Andric case AsInt64: 1050b57cec5SDimitry Andric return 3; 1060b57cec5SDimitry Andric case None: 1070b57cec5SDimitry Andric return 0; 1080b57cec5SDimitry Andric } 1090b57cec5SDimitry Andric } 1100b57cec5SDimitry Andric 1110b57cec5SDimitry Andric Kind getKind() const { return kind; } 1120b57cec5SDimitry Andric void setKind(Kind k) { kind = k; } 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric const char *toString() const; 1150b57cec5SDimitry Andric 1160b57cec5SDimitry Andric private: 1170b57cec5SDimitry Andric const char *Position; 1180b57cec5SDimitry Andric Kind kind; 1190b57cec5SDimitry Andric }; 1200b57cec5SDimitry Andric 1210b57cec5SDimitry Andric class ConversionSpecifier { 1220b57cec5SDimitry Andric public: 1230b57cec5SDimitry Andric enum Kind { 1240b57cec5SDimitry Andric InvalidSpecifier = 0, 1250b57cec5SDimitry Andric // C99 conversion specifiers. 1260b57cec5SDimitry Andric cArg, 1270b57cec5SDimitry Andric dArg, 1280b57cec5SDimitry Andric DArg, // Apple extension 1290b57cec5SDimitry Andric iArg, 1300b57cec5SDimitry Andric IntArgBeg = dArg, 1310b57cec5SDimitry Andric IntArgEnd = iArg, 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric oArg, 1340b57cec5SDimitry Andric OArg, // Apple extension 1350b57cec5SDimitry Andric uArg, 1360b57cec5SDimitry Andric UArg, // Apple extension 1370b57cec5SDimitry Andric xArg, 1380b57cec5SDimitry Andric XArg, 1390b57cec5SDimitry Andric UIntArgBeg = oArg, 1400b57cec5SDimitry Andric UIntArgEnd = XArg, 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric fArg, 1430b57cec5SDimitry Andric FArg, 1440b57cec5SDimitry Andric eArg, 1450b57cec5SDimitry Andric EArg, 1460b57cec5SDimitry Andric gArg, 1470b57cec5SDimitry Andric GArg, 1480b57cec5SDimitry Andric aArg, 1490b57cec5SDimitry Andric AArg, 1500b57cec5SDimitry Andric DoubleArgBeg = fArg, 1510b57cec5SDimitry Andric DoubleArgEnd = AArg, 1520b57cec5SDimitry Andric 1530b57cec5SDimitry Andric sArg, 1540b57cec5SDimitry Andric pArg, 1550b57cec5SDimitry Andric nArg, 1560b57cec5SDimitry Andric PercentArg, 1570b57cec5SDimitry Andric CArg, 1580b57cec5SDimitry Andric SArg, 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric // Apple extension: P specifies to os_log that the data being pointed to is 1610b57cec5SDimitry Andric // to be copied by os_log. The precision indicates the number of bytes to 1620b57cec5SDimitry Andric // copy. 1630b57cec5SDimitry Andric PArg, 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric // ** Printf-specific ** 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric ZArg, // MS extension 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric // Objective-C specific specifiers. 1700b57cec5SDimitry Andric ObjCObjArg, // '@' 1710b57cec5SDimitry Andric ObjCBeg = ObjCObjArg, 1720b57cec5SDimitry Andric ObjCEnd = ObjCObjArg, 1730b57cec5SDimitry Andric 1740b57cec5SDimitry Andric // FreeBSD kernel specific specifiers. 1750b57cec5SDimitry Andric FreeBSDbArg, 1760b57cec5SDimitry Andric FreeBSDDArg, 1770b57cec5SDimitry Andric FreeBSDrArg, 1780b57cec5SDimitry Andric FreeBSDyArg, 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric // GlibC specific specifiers. 1810b57cec5SDimitry Andric PrintErrno, // 'm' 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric PrintfConvBeg = ObjCObjArg, 1840b57cec5SDimitry Andric PrintfConvEnd = PrintErrno, 1850b57cec5SDimitry Andric 1860b57cec5SDimitry Andric // ** Scanf-specific ** 1870b57cec5SDimitry Andric ScanListArg, // '[' 1880b57cec5SDimitry Andric ScanfConvBeg = ScanListArg, 1890b57cec5SDimitry Andric ScanfConvEnd = ScanListArg 1900b57cec5SDimitry Andric }; 1910b57cec5SDimitry Andric 1920b57cec5SDimitry Andric ConversionSpecifier(bool isPrintf = true) 1930b57cec5SDimitry Andric : IsPrintf(isPrintf), Position(nullptr), EndScanList(nullptr), 1940b57cec5SDimitry Andric kind(InvalidSpecifier) {} 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric ConversionSpecifier(bool isPrintf, const char *pos, Kind k) 1970b57cec5SDimitry Andric : IsPrintf(isPrintf), Position(pos), EndScanList(nullptr), kind(k) {} 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric const char *getStart() const { 2000b57cec5SDimitry Andric return Position; 2010b57cec5SDimitry Andric } 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric StringRef getCharacters() const { 2040b57cec5SDimitry Andric return StringRef(getStart(), getLength()); 2050b57cec5SDimitry Andric } 2060b57cec5SDimitry Andric 2070b57cec5SDimitry Andric bool consumesDataArgument() const { 2080b57cec5SDimitry Andric switch (kind) { 2090b57cec5SDimitry Andric case PrintErrno: 2100b57cec5SDimitry Andric assert(IsPrintf); 2110b57cec5SDimitry Andric return false; 2120b57cec5SDimitry Andric case PercentArg: 2130b57cec5SDimitry Andric return false; 2140b57cec5SDimitry Andric case InvalidSpecifier: 2150b57cec5SDimitry Andric return false; 2160b57cec5SDimitry Andric default: 2170b57cec5SDimitry Andric return true; 2180b57cec5SDimitry Andric } 2190b57cec5SDimitry Andric } 2200b57cec5SDimitry Andric 2210b57cec5SDimitry Andric Kind getKind() const { return kind; } 2220b57cec5SDimitry Andric void setKind(Kind k) { kind = k; } 2230b57cec5SDimitry Andric unsigned getLength() const { 2240b57cec5SDimitry Andric return EndScanList ? EndScanList - Position : 1; 2250b57cec5SDimitry Andric } 2260b57cec5SDimitry Andric void setEndScanList(const char *pos) { EndScanList = pos; } 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric bool isIntArg() const { return (kind >= IntArgBeg && kind <= IntArgEnd) || 2290b57cec5SDimitry Andric kind == FreeBSDrArg || kind == FreeBSDyArg; } 2300b57cec5SDimitry Andric bool isUIntArg() const { return kind >= UIntArgBeg && kind <= UIntArgEnd; } 2310b57cec5SDimitry Andric bool isAnyIntArg() const { return kind >= IntArgBeg && kind <= UIntArgEnd; } 2320b57cec5SDimitry Andric bool isDoubleArg() const { 2330b57cec5SDimitry Andric return kind >= DoubleArgBeg && kind <= DoubleArgEnd; 2340b57cec5SDimitry Andric } 2350b57cec5SDimitry Andric 2360b57cec5SDimitry Andric const char *toString() const; 2370b57cec5SDimitry Andric 2380b57cec5SDimitry Andric bool isPrintfKind() const { return IsPrintf; } 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric Optional<ConversionSpecifier> getStandardSpecifier() const; 2410b57cec5SDimitry Andric 2420b57cec5SDimitry Andric protected: 2430b57cec5SDimitry Andric bool IsPrintf; 2440b57cec5SDimitry Andric const char *Position; 2450b57cec5SDimitry Andric const char *EndScanList; 2460b57cec5SDimitry Andric Kind kind; 2470b57cec5SDimitry Andric }; 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric class ArgType { 2500b57cec5SDimitry Andric public: 2510b57cec5SDimitry Andric enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy, 2520b57cec5SDimitry Andric AnyCharTy, CStrTy, WCStrTy, WIntTy }; 2530b57cec5SDimitry Andric 2540b57cec5SDimitry Andric enum MatchKind { NoMatch = 0, Match = 1, NoMatchPedantic }; 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric private: 2570b57cec5SDimitry Andric const Kind K; 2580b57cec5SDimitry Andric QualType T; 2590b57cec5SDimitry Andric const char *Name = nullptr; 2600b57cec5SDimitry Andric bool Ptr = false; 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric /// The TypeKind identifies certain well-known types like size_t and 2630b57cec5SDimitry Andric /// ptrdiff_t. 2640b57cec5SDimitry Andric enum class TypeKind { DontCare, SizeT, PtrdiffT }; 2650b57cec5SDimitry Andric TypeKind TK = TypeKind::DontCare; 2660b57cec5SDimitry Andric 2670b57cec5SDimitry Andric public: 2680b57cec5SDimitry Andric ArgType(Kind K = UnknownTy, const char *N = nullptr) : K(K), Name(N) {} 2690b57cec5SDimitry Andric ArgType(QualType T, const char *N = nullptr) : K(SpecificTy), T(T), Name(N) {} 2700b57cec5SDimitry Andric ArgType(CanQualType T) : K(SpecificTy), T(T) {} 2710b57cec5SDimitry Andric 2720b57cec5SDimitry Andric static ArgType Invalid() { return ArgType(InvalidTy); } 2730b57cec5SDimitry Andric bool isValid() const { return K != InvalidTy; } 2740b57cec5SDimitry Andric 2750b57cec5SDimitry Andric bool isSizeT() const { return TK == TypeKind::SizeT; } 2760b57cec5SDimitry Andric 2770b57cec5SDimitry Andric bool isPtrdiffT() const { return TK == TypeKind::PtrdiffT; } 2780b57cec5SDimitry Andric 2790b57cec5SDimitry Andric /// Create an ArgType which corresponds to the type pointer to A. 2800b57cec5SDimitry Andric static ArgType PtrTo(const ArgType& A) { 2810b57cec5SDimitry Andric assert(A.K >= InvalidTy && "ArgType cannot be pointer to invalid/unknown"); 2820b57cec5SDimitry Andric ArgType Res = A; 2830b57cec5SDimitry Andric Res.Ptr = true; 2840b57cec5SDimitry Andric return Res; 2850b57cec5SDimitry Andric } 2860b57cec5SDimitry Andric 2870b57cec5SDimitry Andric /// Create an ArgType which corresponds to the size_t/ssize_t type. 2880b57cec5SDimitry Andric static ArgType makeSizeT(const ArgType &A) { 2890b57cec5SDimitry Andric ArgType Res = A; 2900b57cec5SDimitry Andric Res.TK = TypeKind::SizeT; 2910b57cec5SDimitry Andric return Res; 2920b57cec5SDimitry Andric } 2930b57cec5SDimitry Andric 2940b57cec5SDimitry Andric /// Create an ArgType which corresponds to the ptrdiff_t/unsigned ptrdiff_t 2950b57cec5SDimitry Andric /// type. 2960b57cec5SDimitry Andric static ArgType makePtrdiffT(const ArgType &A) { 2970b57cec5SDimitry Andric ArgType Res = A; 2980b57cec5SDimitry Andric Res.TK = TypeKind::PtrdiffT; 2990b57cec5SDimitry Andric return Res; 3000b57cec5SDimitry Andric } 3010b57cec5SDimitry Andric 3020b57cec5SDimitry Andric MatchKind matchesType(ASTContext &C, QualType argTy) const; 3030b57cec5SDimitry Andric 3040b57cec5SDimitry Andric QualType getRepresentativeType(ASTContext &C) const; 3050b57cec5SDimitry Andric 3060b57cec5SDimitry Andric ArgType makeVectorType(ASTContext &C, unsigned NumElts) const; 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric std::string getRepresentativeTypeName(ASTContext &C) const; 3090b57cec5SDimitry Andric }; 3100b57cec5SDimitry Andric 3110b57cec5SDimitry Andric class OptionalAmount { 3120b57cec5SDimitry Andric public: 3130b57cec5SDimitry Andric enum HowSpecified { NotSpecified, Constant, Arg, Invalid }; 3140b57cec5SDimitry Andric 3150b57cec5SDimitry Andric OptionalAmount(HowSpecified howSpecified, 3160b57cec5SDimitry Andric unsigned amount, 3170b57cec5SDimitry Andric const char *amountStart, 3180b57cec5SDimitry Andric unsigned amountLength, 3190b57cec5SDimitry Andric bool usesPositionalArg) 3200b57cec5SDimitry Andric : start(amountStart), length(amountLength), hs(howSpecified), amt(amount), 3210b57cec5SDimitry Andric UsesPositionalArg(usesPositionalArg), UsesDotPrefix(0) {} 3220b57cec5SDimitry Andric 3230b57cec5SDimitry Andric OptionalAmount(bool valid = true) 3240b57cec5SDimitry Andric : start(nullptr),length(0), hs(valid ? NotSpecified : Invalid), amt(0), 3250b57cec5SDimitry Andric UsesPositionalArg(0), UsesDotPrefix(0) {} 3260b57cec5SDimitry Andric 3270b57cec5SDimitry Andric explicit OptionalAmount(unsigned Amount) 3280b57cec5SDimitry Andric : start(nullptr), length(0), hs(Constant), amt(Amount), 3290b57cec5SDimitry Andric UsesPositionalArg(false), UsesDotPrefix(false) {} 3300b57cec5SDimitry Andric 3310b57cec5SDimitry Andric bool isInvalid() const { 3320b57cec5SDimitry Andric return hs == Invalid; 3330b57cec5SDimitry Andric } 3340b57cec5SDimitry Andric 3350b57cec5SDimitry Andric HowSpecified getHowSpecified() const { return hs; } 3360b57cec5SDimitry Andric void setHowSpecified(HowSpecified h) { hs = h; } 3370b57cec5SDimitry Andric 3380b57cec5SDimitry Andric bool hasDataArgument() const { return hs == Arg; } 3390b57cec5SDimitry Andric 3400b57cec5SDimitry Andric unsigned getArgIndex() const { 3410b57cec5SDimitry Andric assert(hasDataArgument()); 3420b57cec5SDimitry Andric return amt; 3430b57cec5SDimitry Andric } 3440b57cec5SDimitry Andric 3450b57cec5SDimitry Andric unsigned getConstantAmount() const { 3460b57cec5SDimitry Andric assert(hs == Constant); 3470b57cec5SDimitry Andric return amt; 3480b57cec5SDimitry Andric } 3490b57cec5SDimitry Andric 3500b57cec5SDimitry Andric const char *getStart() const { 3510b57cec5SDimitry Andric // We include the . character if it is given. 3520b57cec5SDimitry Andric return start - UsesDotPrefix; 3530b57cec5SDimitry Andric } 3540b57cec5SDimitry Andric 3550b57cec5SDimitry Andric unsigned getConstantLength() const { 3560b57cec5SDimitry Andric assert(hs == Constant); 3570b57cec5SDimitry Andric return length + UsesDotPrefix; 3580b57cec5SDimitry Andric } 3590b57cec5SDimitry Andric 3600b57cec5SDimitry Andric ArgType getArgType(ASTContext &Ctx) const; 3610b57cec5SDimitry Andric 3620b57cec5SDimitry Andric void toString(raw_ostream &os) const; 3630b57cec5SDimitry Andric 3640b57cec5SDimitry Andric bool usesPositionalArg() const { return (bool) UsesPositionalArg; } 3650b57cec5SDimitry Andric unsigned getPositionalArgIndex() const { 3660b57cec5SDimitry Andric assert(hasDataArgument()); 3670b57cec5SDimitry Andric return amt + 1; 3680b57cec5SDimitry Andric } 3690b57cec5SDimitry Andric 3700b57cec5SDimitry Andric bool usesDotPrefix() const { return UsesDotPrefix; } 3710b57cec5SDimitry Andric void setUsesDotPrefix() { UsesDotPrefix = true; } 3720b57cec5SDimitry Andric 3730b57cec5SDimitry Andric private: 3740b57cec5SDimitry Andric const char *start; 3750b57cec5SDimitry Andric unsigned length; 3760b57cec5SDimitry Andric HowSpecified hs; 3770b57cec5SDimitry Andric unsigned amt; 3780b57cec5SDimitry Andric bool UsesPositionalArg : 1; 3790b57cec5SDimitry Andric bool UsesDotPrefix; 3800b57cec5SDimitry Andric }; 3810b57cec5SDimitry Andric 3820b57cec5SDimitry Andric 3830b57cec5SDimitry Andric class FormatSpecifier { 3840b57cec5SDimitry Andric protected: 3850b57cec5SDimitry Andric LengthModifier LM; 3860b57cec5SDimitry Andric OptionalAmount FieldWidth; 3870b57cec5SDimitry Andric ConversionSpecifier CS; 3880b57cec5SDimitry Andric OptionalAmount VectorNumElts; 3890b57cec5SDimitry Andric 3900b57cec5SDimitry Andric /// Positional arguments, an IEEE extension: 3910b57cec5SDimitry Andric /// IEEE Std 1003.1, 2004 Edition 3920b57cec5SDimitry Andric /// http://www.opengroup.org/onlinepubs/009695399/functions/printf.html 3930b57cec5SDimitry Andric bool UsesPositionalArg; 3940b57cec5SDimitry Andric unsigned argIndex; 3950b57cec5SDimitry Andric public: 3960b57cec5SDimitry Andric FormatSpecifier(bool isPrintf) 3970b57cec5SDimitry Andric : CS(isPrintf), VectorNumElts(false), 3980b57cec5SDimitry Andric UsesPositionalArg(false), argIndex(0) {} 3990b57cec5SDimitry Andric 4000b57cec5SDimitry Andric void setLengthModifier(LengthModifier lm) { 4010b57cec5SDimitry Andric LM = lm; 4020b57cec5SDimitry Andric } 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric void setUsesPositionalArg() { UsesPositionalArg = true; } 4050b57cec5SDimitry Andric 4060b57cec5SDimitry Andric void setArgIndex(unsigned i) { 4070b57cec5SDimitry Andric argIndex = i; 4080b57cec5SDimitry Andric } 4090b57cec5SDimitry Andric 4100b57cec5SDimitry Andric unsigned getArgIndex() const { 4110b57cec5SDimitry Andric return argIndex; 4120b57cec5SDimitry Andric } 4130b57cec5SDimitry Andric 4140b57cec5SDimitry Andric unsigned getPositionalArgIndex() const { 4150b57cec5SDimitry Andric return argIndex + 1; 4160b57cec5SDimitry Andric } 4170b57cec5SDimitry Andric 4180b57cec5SDimitry Andric const LengthModifier &getLengthModifier() const { 4190b57cec5SDimitry Andric return LM; 4200b57cec5SDimitry Andric } 4210b57cec5SDimitry Andric 4220b57cec5SDimitry Andric const OptionalAmount &getFieldWidth() const { 4230b57cec5SDimitry Andric return FieldWidth; 4240b57cec5SDimitry Andric } 4250b57cec5SDimitry Andric 4260b57cec5SDimitry Andric void setVectorNumElts(const OptionalAmount &Amt) { 4270b57cec5SDimitry Andric VectorNumElts = Amt; 4280b57cec5SDimitry Andric } 4290b57cec5SDimitry Andric 4300b57cec5SDimitry Andric const OptionalAmount &getVectorNumElts() const { 4310b57cec5SDimitry Andric return VectorNumElts; 4320b57cec5SDimitry Andric } 4330b57cec5SDimitry Andric 4340b57cec5SDimitry Andric void setFieldWidth(const OptionalAmount &Amt) { 4350b57cec5SDimitry Andric FieldWidth = Amt; 4360b57cec5SDimitry Andric } 4370b57cec5SDimitry Andric 4380b57cec5SDimitry Andric bool usesPositionalArg() const { return UsesPositionalArg; } 4390b57cec5SDimitry Andric 4400b57cec5SDimitry Andric bool hasValidLengthModifier(const TargetInfo &Target, 4410b57cec5SDimitry Andric const LangOptions &LO) const; 4420b57cec5SDimitry Andric 4430b57cec5SDimitry Andric bool hasStandardLengthModifier() const; 4440b57cec5SDimitry Andric 4450b57cec5SDimitry Andric Optional<LengthModifier> getCorrectedLengthModifier() const; 4460b57cec5SDimitry Andric 4470b57cec5SDimitry Andric bool hasStandardConversionSpecifier(const LangOptions &LangOpt) const; 4480b57cec5SDimitry Andric 4490b57cec5SDimitry Andric bool hasStandardLengthConversionCombination() const; 4500b57cec5SDimitry Andric 4510b57cec5SDimitry Andric /// For a TypedefType QT, if it is a named integer type such as size_t, 4520b57cec5SDimitry Andric /// assign the appropriate value to LM and return true. 4530b57cec5SDimitry Andric static bool namedTypeToLengthModifier(QualType QT, LengthModifier &LM); 4540b57cec5SDimitry Andric }; 4550b57cec5SDimitry Andric 4560b57cec5SDimitry Andric } // end analyze_format_string namespace 4570b57cec5SDimitry Andric 4580b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 4590b57cec5SDimitry Andric /// Pieces specific to fprintf format strings. 4600b57cec5SDimitry Andric 4610b57cec5SDimitry Andric namespace analyze_printf { 4620b57cec5SDimitry Andric 4630b57cec5SDimitry Andric class PrintfConversionSpecifier : 4640b57cec5SDimitry Andric public analyze_format_string::ConversionSpecifier { 4650b57cec5SDimitry Andric public: 4660b57cec5SDimitry Andric PrintfConversionSpecifier() 4670b57cec5SDimitry Andric : ConversionSpecifier(true, nullptr, InvalidSpecifier) {} 4680b57cec5SDimitry Andric 4690b57cec5SDimitry Andric PrintfConversionSpecifier(const char *pos, Kind k) 4700b57cec5SDimitry Andric : ConversionSpecifier(true, pos, k) {} 4710b57cec5SDimitry Andric 4720b57cec5SDimitry Andric bool isObjCArg() const { return kind >= ObjCBeg && kind <= ObjCEnd; } 4730b57cec5SDimitry Andric bool isDoubleArg() const { return kind >= DoubleArgBeg && 4740b57cec5SDimitry Andric kind <= DoubleArgEnd; } 4750b57cec5SDimitry Andric 4760b57cec5SDimitry Andric static bool classof(const analyze_format_string::ConversionSpecifier *CS) { 4770b57cec5SDimitry Andric return CS->isPrintfKind(); 4780b57cec5SDimitry Andric } 4790b57cec5SDimitry Andric }; 4800b57cec5SDimitry Andric 4810b57cec5SDimitry Andric using analyze_format_string::ArgType; 4820b57cec5SDimitry Andric using analyze_format_string::LengthModifier; 4830b57cec5SDimitry Andric using analyze_format_string::OptionalAmount; 4840b57cec5SDimitry Andric using analyze_format_string::OptionalFlag; 4850b57cec5SDimitry Andric 4860b57cec5SDimitry Andric class PrintfSpecifier : public analyze_format_string::FormatSpecifier { 4870b57cec5SDimitry Andric OptionalFlag HasThousandsGrouping; // ''', POSIX extension. 4880b57cec5SDimitry Andric OptionalFlag IsLeftJustified; // '-' 4890b57cec5SDimitry Andric OptionalFlag HasPlusPrefix; // '+' 4900b57cec5SDimitry Andric OptionalFlag HasSpacePrefix; // ' ' 4910b57cec5SDimitry Andric OptionalFlag HasAlternativeForm; // '#' 4920b57cec5SDimitry Andric OptionalFlag HasLeadingZeroes; // '0' 4930b57cec5SDimitry Andric OptionalFlag HasObjCTechnicalTerm; // '[tt]' 4940b57cec5SDimitry Andric OptionalFlag IsPrivate; // '{private}' 4950b57cec5SDimitry Andric OptionalFlag IsPublic; // '{public}' 4960b57cec5SDimitry Andric OptionalFlag IsSensitive; // '{sensitive}' 4970b57cec5SDimitry Andric OptionalAmount Precision; 4980b57cec5SDimitry Andric StringRef MaskType; 4990b57cec5SDimitry Andric 5000b57cec5SDimitry Andric ArgType getScalarArgType(ASTContext &Ctx, bool IsObjCLiteral) const; 5010b57cec5SDimitry Andric 5020b57cec5SDimitry Andric public: 5030b57cec5SDimitry Andric PrintfSpecifier() 5040b57cec5SDimitry Andric : FormatSpecifier(/* isPrintf = */ true), HasThousandsGrouping("'"), 5050b57cec5SDimitry Andric IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "), 5060b57cec5SDimitry Andric HasAlternativeForm("#"), HasLeadingZeroes("0"), 5070b57cec5SDimitry Andric HasObjCTechnicalTerm("tt"), IsPrivate("private"), IsPublic("public"), 5080b57cec5SDimitry Andric IsSensitive("sensitive") {} 5090b57cec5SDimitry Andric 5100b57cec5SDimitry Andric static PrintfSpecifier Parse(const char *beg, const char *end); 5110b57cec5SDimitry Andric 5120b57cec5SDimitry Andric // Methods for incrementally constructing the PrintfSpecifier. 5130b57cec5SDimitry Andric void setConversionSpecifier(const PrintfConversionSpecifier &cs) { 5140b57cec5SDimitry Andric CS = cs; 5150b57cec5SDimitry Andric } 5160b57cec5SDimitry Andric void setHasThousandsGrouping(const char *position) { 5170b57cec5SDimitry Andric HasThousandsGrouping.setPosition(position); 5180b57cec5SDimitry Andric } 5190b57cec5SDimitry Andric void setIsLeftJustified(const char *position) { 5200b57cec5SDimitry Andric IsLeftJustified.setPosition(position); 5210b57cec5SDimitry Andric } 5220b57cec5SDimitry Andric void setHasPlusPrefix(const char *position) { 5230b57cec5SDimitry Andric HasPlusPrefix.setPosition(position); 5240b57cec5SDimitry Andric } 5250b57cec5SDimitry Andric void setHasSpacePrefix(const char *position) { 5260b57cec5SDimitry Andric HasSpacePrefix.setPosition(position); 5270b57cec5SDimitry Andric } 5280b57cec5SDimitry Andric void setHasAlternativeForm(const char *position) { 5290b57cec5SDimitry Andric HasAlternativeForm.setPosition(position); 5300b57cec5SDimitry Andric } 5310b57cec5SDimitry Andric void setHasLeadingZeros(const char *position) { 5320b57cec5SDimitry Andric HasLeadingZeroes.setPosition(position); 5330b57cec5SDimitry Andric } 5340b57cec5SDimitry Andric void setHasObjCTechnicalTerm(const char *position) { 5350b57cec5SDimitry Andric HasObjCTechnicalTerm.setPosition(position); 5360b57cec5SDimitry Andric } 5370b57cec5SDimitry Andric void setIsPrivate(const char *position) { IsPrivate.setPosition(position); } 5380b57cec5SDimitry Andric void setIsPublic(const char *position) { IsPublic.setPosition(position); } 5390b57cec5SDimitry Andric void setIsSensitive(const char *position) { 5400b57cec5SDimitry Andric IsSensitive.setPosition(position); 5410b57cec5SDimitry Andric } 5420b57cec5SDimitry Andric void setUsesPositionalArg() { UsesPositionalArg = true; } 5430b57cec5SDimitry Andric 5440b57cec5SDimitry Andric // Methods for querying the format specifier. 5450b57cec5SDimitry Andric 5460b57cec5SDimitry Andric const PrintfConversionSpecifier &getConversionSpecifier() const { 5470b57cec5SDimitry Andric return cast<PrintfConversionSpecifier>(CS); 5480b57cec5SDimitry Andric } 5490b57cec5SDimitry Andric 5500b57cec5SDimitry Andric void setPrecision(const OptionalAmount &Amt) { 5510b57cec5SDimitry Andric Precision = Amt; 5520b57cec5SDimitry Andric Precision.setUsesDotPrefix(); 5530b57cec5SDimitry Andric } 5540b57cec5SDimitry Andric 5550b57cec5SDimitry Andric const OptionalAmount &getPrecision() const { 5560b57cec5SDimitry Andric return Precision; 5570b57cec5SDimitry Andric } 5580b57cec5SDimitry Andric 5590b57cec5SDimitry Andric bool consumesDataArgument() const { 5600b57cec5SDimitry Andric return getConversionSpecifier().consumesDataArgument(); 5610b57cec5SDimitry Andric } 5620b57cec5SDimitry Andric 5630b57cec5SDimitry Andric /// Returns the builtin type that a data argument 5640b57cec5SDimitry Andric /// paired with this format specifier should have. This method 5650b57cec5SDimitry Andric /// will return null if the format specifier does not have 5660b57cec5SDimitry Andric /// a matching data argument or the matching argument matches 5670b57cec5SDimitry Andric /// more than one type. 5680b57cec5SDimitry Andric ArgType getArgType(ASTContext &Ctx, bool IsObjCLiteral) const; 5690b57cec5SDimitry Andric 5700b57cec5SDimitry Andric const OptionalFlag &hasThousandsGrouping() const { 5710b57cec5SDimitry Andric return HasThousandsGrouping; 5720b57cec5SDimitry Andric } 5730b57cec5SDimitry Andric const OptionalFlag &isLeftJustified() const { return IsLeftJustified; } 5740b57cec5SDimitry Andric const OptionalFlag &hasPlusPrefix() const { return HasPlusPrefix; } 5750b57cec5SDimitry Andric const OptionalFlag &hasAlternativeForm() const { return HasAlternativeForm; } 5760b57cec5SDimitry Andric const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; } 5770b57cec5SDimitry Andric const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; } 5780b57cec5SDimitry Andric const OptionalFlag &hasObjCTechnicalTerm() const { return HasObjCTechnicalTerm; } 5790b57cec5SDimitry Andric const OptionalFlag &isPrivate() const { return IsPrivate; } 5800b57cec5SDimitry Andric const OptionalFlag &isPublic() const { return IsPublic; } 5810b57cec5SDimitry Andric const OptionalFlag &isSensitive() const { return IsSensitive; } 5820b57cec5SDimitry Andric bool usesPositionalArg() const { return UsesPositionalArg; } 5830b57cec5SDimitry Andric 5840b57cec5SDimitry Andric StringRef getMaskType() const { return MaskType; } 5850b57cec5SDimitry Andric void setMaskType(StringRef S) { MaskType = S; } 5860b57cec5SDimitry Andric 5870b57cec5SDimitry Andric /// Changes the specifier and length according to a QualType, retaining any 5880b57cec5SDimitry Andric /// flags or options. Returns true on success, or false when a conversion 5890b57cec5SDimitry Andric /// was not successful. 5900b57cec5SDimitry Andric bool fixType(QualType QT, const LangOptions &LangOpt, ASTContext &Ctx, 5910b57cec5SDimitry Andric bool IsObjCLiteral); 5920b57cec5SDimitry Andric 5930b57cec5SDimitry Andric void toString(raw_ostream &os) const; 5940b57cec5SDimitry Andric 5950b57cec5SDimitry Andric // Validation methods - to check if any element results in undefined behavior 5960b57cec5SDimitry Andric bool hasValidPlusPrefix() const; 5970b57cec5SDimitry Andric bool hasValidAlternativeForm() const; 5980b57cec5SDimitry Andric bool hasValidLeadingZeros() const; 5990b57cec5SDimitry Andric bool hasValidSpacePrefix() const; 6000b57cec5SDimitry Andric bool hasValidLeftJustified() const; 6010b57cec5SDimitry Andric bool hasValidThousandsGroupingPrefix() const; 6020b57cec5SDimitry Andric 6030b57cec5SDimitry Andric bool hasValidPrecision() const; 6040b57cec5SDimitry Andric bool hasValidFieldWidth() const; 6050b57cec5SDimitry Andric }; 6060b57cec5SDimitry Andric } // end analyze_printf namespace 6070b57cec5SDimitry Andric 6080b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 6090b57cec5SDimitry Andric /// Pieces specific to fscanf format strings. 6100b57cec5SDimitry Andric 6110b57cec5SDimitry Andric namespace analyze_scanf { 6120b57cec5SDimitry Andric 6130b57cec5SDimitry Andric class ScanfConversionSpecifier : 6140b57cec5SDimitry Andric public analyze_format_string::ConversionSpecifier { 6150b57cec5SDimitry Andric public: 6160b57cec5SDimitry Andric ScanfConversionSpecifier() 6170b57cec5SDimitry Andric : ConversionSpecifier(false, nullptr, InvalidSpecifier) {} 6180b57cec5SDimitry Andric 6190b57cec5SDimitry Andric ScanfConversionSpecifier(const char *pos, Kind k) 6200b57cec5SDimitry Andric : ConversionSpecifier(false, pos, k) {} 6210b57cec5SDimitry Andric 6220b57cec5SDimitry Andric static bool classof(const analyze_format_string::ConversionSpecifier *CS) { 6230b57cec5SDimitry Andric return !CS->isPrintfKind(); 6240b57cec5SDimitry Andric } 6250b57cec5SDimitry Andric }; 6260b57cec5SDimitry Andric 6270b57cec5SDimitry Andric using analyze_format_string::ArgType; 6280b57cec5SDimitry Andric using analyze_format_string::LengthModifier; 6290b57cec5SDimitry Andric using analyze_format_string::OptionalAmount; 6300b57cec5SDimitry Andric using analyze_format_string::OptionalFlag; 6310b57cec5SDimitry Andric 6320b57cec5SDimitry Andric class ScanfSpecifier : public analyze_format_string::FormatSpecifier { 6330b57cec5SDimitry Andric OptionalFlag SuppressAssignment; // '*' 6340b57cec5SDimitry Andric public: 6350b57cec5SDimitry Andric ScanfSpecifier() : 6360b57cec5SDimitry Andric FormatSpecifier(/* isPrintf = */ false), 6370b57cec5SDimitry Andric SuppressAssignment("*") {} 6380b57cec5SDimitry Andric 6390b57cec5SDimitry Andric void setSuppressAssignment(const char *position) { 6400b57cec5SDimitry Andric SuppressAssignment.setPosition(position); 6410b57cec5SDimitry Andric } 6420b57cec5SDimitry Andric 6430b57cec5SDimitry Andric const OptionalFlag &getSuppressAssignment() const { 6440b57cec5SDimitry Andric return SuppressAssignment; 6450b57cec5SDimitry Andric } 6460b57cec5SDimitry Andric 6470b57cec5SDimitry Andric void setConversionSpecifier(const ScanfConversionSpecifier &cs) { 6480b57cec5SDimitry Andric CS = cs; 6490b57cec5SDimitry Andric } 6500b57cec5SDimitry Andric 6510b57cec5SDimitry Andric const ScanfConversionSpecifier &getConversionSpecifier() const { 6520b57cec5SDimitry Andric return cast<ScanfConversionSpecifier>(CS); 6530b57cec5SDimitry Andric } 6540b57cec5SDimitry Andric 6550b57cec5SDimitry Andric bool consumesDataArgument() const { 6560b57cec5SDimitry Andric return CS.consumesDataArgument() && !SuppressAssignment; 6570b57cec5SDimitry Andric } 6580b57cec5SDimitry Andric 6590b57cec5SDimitry Andric ArgType getArgType(ASTContext &Ctx) const; 6600b57cec5SDimitry Andric 6610b57cec5SDimitry Andric bool fixType(QualType QT, QualType RawQT, const LangOptions &LangOpt, 6620b57cec5SDimitry Andric ASTContext &Ctx); 6630b57cec5SDimitry Andric 6640b57cec5SDimitry Andric void toString(raw_ostream &os) const; 6650b57cec5SDimitry Andric 6660b57cec5SDimitry Andric static ScanfSpecifier Parse(const char *beg, const char *end); 6670b57cec5SDimitry Andric }; 6680b57cec5SDimitry Andric 6690b57cec5SDimitry Andric } // end analyze_scanf namespace 6700b57cec5SDimitry Andric 6710b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 6720b57cec5SDimitry Andric // Parsing and processing of format strings (both fprintf and fscanf). 6730b57cec5SDimitry Andric 6740b57cec5SDimitry Andric namespace analyze_format_string { 6750b57cec5SDimitry Andric 6760b57cec5SDimitry Andric enum PositionContext { FieldWidthPos = 0, PrecisionPos = 1 }; 6770b57cec5SDimitry Andric 6780b57cec5SDimitry Andric class FormatStringHandler { 6790b57cec5SDimitry Andric public: 6800b57cec5SDimitry Andric FormatStringHandler() {} 6810b57cec5SDimitry Andric virtual ~FormatStringHandler(); 6820b57cec5SDimitry Andric 6830b57cec5SDimitry Andric virtual void HandleNullChar(const char *nullCharacter) {} 6840b57cec5SDimitry Andric 6850b57cec5SDimitry Andric virtual void HandlePosition(const char *startPos, unsigned posLen) {} 6860b57cec5SDimitry Andric 6870b57cec5SDimitry Andric virtual void HandleInvalidPosition(const char *startPos, unsigned posLen, 6880b57cec5SDimitry Andric PositionContext p) {} 6890b57cec5SDimitry Andric 6900b57cec5SDimitry Andric virtual void HandleZeroPosition(const char *startPos, unsigned posLen) {} 6910b57cec5SDimitry Andric 6920b57cec5SDimitry Andric virtual void HandleIncompleteSpecifier(const char *startSpecifier, 6930b57cec5SDimitry Andric unsigned specifierLen) {} 6940b57cec5SDimitry Andric 6950b57cec5SDimitry Andric virtual void HandleEmptyObjCModifierFlag(const char *startFlags, 6960b57cec5SDimitry Andric unsigned flagsLen) {} 6970b57cec5SDimitry Andric 6980b57cec5SDimitry Andric virtual void HandleInvalidObjCModifierFlag(const char *startFlag, 6990b57cec5SDimitry Andric unsigned flagLen) {} 7000b57cec5SDimitry Andric 7010b57cec5SDimitry Andric virtual void HandleObjCFlagsWithNonObjCConversion(const char *flagsStart, 7020b57cec5SDimitry Andric const char *flagsEnd, 7030b57cec5SDimitry Andric const char *conversionPosition) {} 7040b57cec5SDimitry Andric // Printf-specific handlers. 7050b57cec5SDimitry Andric 7060b57cec5SDimitry Andric virtual bool HandleInvalidPrintfConversionSpecifier( 7070b57cec5SDimitry Andric const analyze_printf::PrintfSpecifier &FS, 7080b57cec5SDimitry Andric const char *startSpecifier, 7090b57cec5SDimitry Andric unsigned specifierLen) { 7100b57cec5SDimitry Andric return true; 7110b57cec5SDimitry Andric } 7120b57cec5SDimitry Andric 7130b57cec5SDimitry Andric virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, 7140b57cec5SDimitry Andric const char *startSpecifier, 7150b57cec5SDimitry Andric unsigned specifierLen) { 7160b57cec5SDimitry Andric return true; 7170b57cec5SDimitry Andric } 7180b57cec5SDimitry Andric 7190b57cec5SDimitry Andric /// Handle mask types whose sizes are not between one and eight bytes. 7200b57cec5SDimitry Andric virtual void handleInvalidMaskType(StringRef MaskType) {} 7210b57cec5SDimitry Andric 7220b57cec5SDimitry Andric // Scanf-specific handlers. 7230b57cec5SDimitry Andric 7240b57cec5SDimitry Andric virtual bool HandleInvalidScanfConversionSpecifier( 7250b57cec5SDimitry Andric const analyze_scanf::ScanfSpecifier &FS, 7260b57cec5SDimitry Andric const char *startSpecifier, 7270b57cec5SDimitry Andric unsigned specifierLen) { 7280b57cec5SDimitry Andric return true; 7290b57cec5SDimitry Andric } 7300b57cec5SDimitry Andric 7310b57cec5SDimitry Andric virtual bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS, 7320b57cec5SDimitry Andric const char *startSpecifier, 7330b57cec5SDimitry Andric unsigned specifierLen) { 7340b57cec5SDimitry Andric return true; 7350b57cec5SDimitry Andric } 7360b57cec5SDimitry Andric 7370b57cec5SDimitry Andric virtual void HandleIncompleteScanList(const char *start, const char *end) {} 7380b57cec5SDimitry Andric }; 7390b57cec5SDimitry Andric 7400b57cec5SDimitry Andric bool ParsePrintfString(FormatStringHandler &H, 7410b57cec5SDimitry Andric const char *beg, const char *end, const LangOptions &LO, 7420b57cec5SDimitry Andric const TargetInfo &Target, bool isFreeBSDKPrintf); 7430b57cec5SDimitry Andric 7440b57cec5SDimitry Andric bool ParseFormatStringHasSArg(const char *beg, const char *end, 7450b57cec5SDimitry Andric const LangOptions &LO, const TargetInfo &Target); 7460b57cec5SDimitry Andric 7470b57cec5SDimitry Andric bool ParseScanfString(FormatStringHandler &H, 7480b57cec5SDimitry Andric const char *beg, const char *end, const LangOptions &LO, 7490b57cec5SDimitry Andric const TargetInfo &Target); 7500b57cec5SDimitry Andric 7510b57cec5SDimitry Andric } // end analyze_format_string namespace 7520b57cec5SDimitry Andric } // end clang namespace 7530b57cec5SDimitry Andric #endif 754