clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
799 lines
24 KiB
C++
799 lines
24 KiB
C++
//= FormatString.h - Analysis of printf/fprintf format strings --*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines APIs for analyzing the format strings of printf, fscanf,
|
|
// and friends.
|
|
//
|
|
// The structure of format strings for fprintf are described in C99 7.19.6.1.
|
|
//
|
|
// The structure of format strings for fscanf are described in C99 7.19.6.2.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_AST_FORMATSTRING_H
|
|
#define LLVM_CLANG_AST_FORMATSTRING_H
|
|
|
|
#include "clang/AST/CanonicalType.h"
|
|
#include <optional>
|
|
|
|
namespace clang {
|
|
|
|
class TargetInfo;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
/// Common components of both fprintf and fscanf format strings.
|
|
namespace analyze_format_string {
|
|
|
|
/// Class representing optional flags with location and representation
|
|
/// information.
|
|
class OptionalFlag {
|
|
public:
|
|
OptionalFlag(const char *Representation)
|
|
: representation(Representation), flag(false) {}
|
|
bool isSet() const { return flag; }
|
|
void set() { flag = true; }
|
|
void clear() { flag = false; }
|
|
void setPosition(const char *position) {
|
|
assert(position);
|
|
flag = true;
|
|
this->position = position;
|
|
}
|
|
const char *getPosition() const {
|
|
assert(position);
|
|
return position;
|
|
}
|
|
const char *toString() const { return representation; }
|
|
|
|
// Overloaded operators for bool like qualities
|
|
explicit operator bool() const { return flag; }
|
|
OptionalFlag& operator=(const bool &rhs) {
|
|
flag = rhs;
|
|
return *this; // Return a reference to myself.
|
|
}
|
|
private:
|
|
const char *representation;
|
|
const char *position;
|
|
bool flag;
|
|
};
|
|
|
|
/// Represents the length modifier in a format string in scanf/printf.
|
|
class LengthModifier {
|
|
public:
|
|
enum Kind {
|
|
None,
|
|
AsChar, // 'hh'
|
|
AsShort, // 'h'
|
|
AsShortLong, // 'hl' (OpenCL float/int vector element)
|
|
AsLong, // 'l'
|
|
AsLongLong, // 'll'
|
|
AsQuad, // 'q' (BSD, deprecated, for 64-bit integer types)
|
|
AsIntMax, // 'j'
|
|
AsSizeT, // 'z'
|
|
AsPtrDiff, // 't'
|
|
AsInt32, // 'I32' (MSVCRT, like __int32)
|
|
AsInt3264, // 'I' (MSVCRT, like __int3264 from MIDL)
|
|
AsInt64, // 'I64' (MSVCRT, like __int64)
|
|
AsLongDouble, // 'L'
|
|
AsAllocate, // for '%as', GNU extension to C90 scanf
|
|
AsMAllocate, // for '%ms', GNU extension to scanf
|
|
AsWide, // 'w' (MSVCRT, like l but only for c, C, s, S, or Z
|
|
AsWideChar = AsLong // for '%ls', only makes sense for printf
|
|
};
|
|
|
|
LengthModifier()
|
|
: Position(nullptr), kind(None) {}
|
|
LengthModifier(const char *pos, Kind k)
|
|
: Position(pos), kind(k) {}
|
|
|
|
const char *getStart() const {
|
|
return Position;
|
|
}
|
|
|
|
unsigned getLength() const {
|
|
switch (kind) {
|
|
default:
|
|
return 1;
|
|
case AsLongLong:
|
|
case AsChar:
|
|
return 2;
|
|
case AsInt32:
|
|
case AsInt64:
|
|
return 3;
|
|
case None:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Kind getKind() const { return kind; }
|
|
void setKind(Kind k) { kind = k; }
|
|
|
|
const char *toString() const;
|
|
|
|
private:
|
|
const char *Position;
|
|
Kind kind;
|
|
};
|
|
|
|
class ConversionSpecifier {
|
|
public:
|
|
enum Kind {
|
|
InvalidSpecifier = 0,
|
|
// C99 conversion specifiers.
|
|
cArg,
|
|
dArg,
|
|
DArg, // Apple extension
|
|
iArg,
|
|
// C23 conversion specifiers.
|
|
bArg,
|
|
BArg,
|
|
|
|
IntArgBeg = dArg,
|
|
IntArgEnd = BArg,
|
|
|
|
oArg,
|
|
OArg, // Apple extension
|
|
uArg,
|
|
UArg, // Apple extension
|
|
xArg,
|
|
XArg,
|
|
UIntArgBeg = oArg,
|
|
UIntArgEnd = XArg,
|
|
|
|
fArg,
|
|
FArg,
|
|
eArg,
|
|
EArg,
|
|
gArg,
|
|
GArg,
|
|
aArg,
|
|
AArg,
|
|
DoubleArgBeg = fArg,
|
|
DoubleArgEnd = AArg,
|
|
|
|
sArg,
|
|
pArg,
|
|
nArg,
|
|
PercentArg,
|
|
CArg,
|
|
SArg,
|
|
|
|
// Apple extension: P specifies to os_log that the data being pointed to is
|
|
// to be copied by os_log. The precision indicates the number of bytes to
|
|
// copy.
|
|
PArg,
|
|
|
|
// ** Printf-specific **
|
|
|
|
ZArg, // MS extension
|
|
|
|
// ISO/IEC TR 18037 (fixed-point) specific specifiers.
|
|
kArg, // %k for signed accum types
|
|
KArg, // %K for unsigned accum types
|
|
rArg, // %r for signed fract types
|
|
RArg, // %R for unsigned fract types
|
|
FixedPointArgBeg = kArg,
|
|
FixedPointArgEnd = RArg,
|
|
|
|
// Objective-C specific specifiers.
|
|
ObjCObjArg, // '@'
|
|
ObjCBeg = ObjCObjArg,
|
|
ObjCEnd = ObjCObjArg,
|
|
|
|
// FreeBSD kernel specific specifiers.
|
|
FreeBSDbArg,
|
|
FreeBSDDArg,
|
|
FreeBSDrArg,
|
|
FreeBSDyArg,
|
|
|
|
// GlibC specific specifiers.
|
|
PrintErrno, // 'm'
|
|
|
|
PrintfConvBeg = ObjCObjArg,
|
|
PrintfConvEnd = PrintErrno,
|
|
|
|
// ** Scanf-specific **
|
|
ScanListArg, // '['
|
|
ScanfConvBeg = ScanListArg,
|
|
ScanfConvEnd = ScanListArg
|
|
};
|
|
|
|
ConversionSpecifier(bool isPrintf = true)
|
|
: IsPrintf(isPrintf), Position(nullptr), EndScanList(nullptr),
|
|
kind(InvalidSpecifier) {}
|
|
|
|
ConversionSpecifier(bool isPrintf, const char *pos, Kind k)
|
|
: IsPrintf(isPrintf), Position(pos), EndScanList(nullptr), kind(k) {}
|
|
|
|
const char *getStart() const {
|
|
return Position;
|
|
}
|
|
|
|
StringRef getCharacters() const {
|
|
return StringRef(getStart(), getLength());
|
|
}
|
|
|
|
bool consumesDataArgument() const {
|
|
switch (kind) {
|
|
case PrintErrno:
|
|
assert(IsPrintf);
|
|
return false;
|
|
case PercentArg:
|
|
return false;
|
|
case InvalidSpecifier:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Kind getKind() const { return kind; }
|
|
void setKind(Kind k) { kind = k; }
|
|
unsigned getLength() const {
|
|
return EndScanList ? EndScanList - Position : 1;
|
|
}
|
|
void setEndScanList(const char *pos) { EndScanList = pos; }
|
|
|
|
bool isIntArg() const { return (kind >= IntArgBeg && kind <= IntArgEnd) ||
|
|
kind == FreeBSDrArg || kind == FreeBSDyArg; }
|
|
bool isUIntArg() const { return kind >= UIntArgBeg && kind <= UIntArgEnd; }
|
|
bool isAnyIntArg() const { return kind >= IntArgBeg && kind <= UIntArgEnd; }
|
|
bool isDoubleArg() const {
|
|
return kind >= DoubleArgBeg && kind <= DoubleArgEnd;
|
|
}
|
|
bool isFixedPointArg() const {
|
|
return kind >= FixedPointArgBeg && kind <= FixedPointArgEnd;
|
|
}
|
|
|
|
const char *toString() const;
|
|
|
|
bool isPrintfKind() const { return IsPrintf; }
|
|
|
|
std::optional<ConversionSpecifier> getStandardSpecifier() const;
|
|
|
|
protected:
|
|
bool IsPrintf;
|
|
const char *Position;
|
|
const char *EndScanList;
|
|
Kind kind;
|
|
};
|
|
|
|
class ArgType {
|
|
public:
|
|
enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy,
|
|
AnyCharTy, CStrTy, WCStrTy, WIntTy };
|
|
|
|
/// How well a given conversion specifier matches its argument.
|
|
enum MatchKind {
|
|
/// The conversion specifier and the argument types are incompatible. For
|
|
/// instance, "%d" and float.
|
|
NoMatch = 0,
|
|
/// The conversion specifier and the argument type are compatible. For
|
|
/// instance, "%d" and int.
|
|
Match = 1,
|
|
/// The conversion specifier and the argument type are compatible because of
|
|
/// default argument promotions. For instance, "%hhd" and int.
|
|
MatchPromotion,
|
|
/// The conversion specifier and the argument type are compatible but still
|
|
/// seems likely to be an error. For instanace, "%hhd" and short.
|
|
NoMatchPromotionTypeConfusion,
|
|
/// The conversion specifier and the argument type are disallowed by the C
|
|
/// standard, but are in practice harmless. For instance, "%p" and int*.
|
|
NoMatchPedantic,
|
|
/// The conversion specifier and the argument type have different sign.
|
|
NoMatchSignedness,
|
|
/// The conversion specifier and the argument type are compatible, but still
|
|
/// seems likely to be an error. For instance, "%hd" and _Bool.
|
|
NoMatchTypeConfusion,
|
|
};
|
|
|
|
private:
|
|
const Kind K;
|
|
QualType T;
|
|
const char *Name = nullptr;
|
|
bool Ptr = false;
|
|
|
|
/// The TypeKind identifies certain well-known types like size_t and
|
|
/// ptrdiff_t.
|
|
enum class TypeKind { DontCare, SizeT, PtrdiffT };
|
|
TypeKind TK = TypeKind::DontCare;
|
|
|
|
public:
|
|
ArgType(Kind K = UnknownTy, const char *N = nullptr) : K(K), Name(N) {}
|
|
ArgType(QualType T, const char *N = nullptr) : K(SpecificTy), T(T), Name(N) {}
|
|
ArgType(CanQualType T) : K(SpecificTy), T(T) {}
|
|
|
|
static ArgType Invalid() { return ArgType(InvalidTy); }
|
|
bool isValid() const { return K != InvalidTy; }
|
|
|
|
bool isSizeT() const { return TK == TypeKind::SizeT; }
|
|
|
|
bool isPtrdiffT() const { return TK == TypeKind::PtrdiffT; }
|
|
|
|
/// Create an ArgType which corresponds to the type pointer to A.
|
|
static ArgType PtrTo(const ArgType& A) {
|
|
assert(A.K >= InvalidTy && "ArgType cannot be pointer to invalid/unknown");
|
|
ArgType Res = A;
|
|
Res.Ptr = true;
|
|
return Res;
|
|
}
|
|
|
|
/// Create an ArgType which corresponds to the size_t/ssize_t type.
|
|
static ArgType makeSizeT(const ArgType &A) {
|
|
ArgType Res = A;
|
|
Res.TK = TypeKind::SizeT;
|
|
return Res;
|
|
}
|
|
|
|
/// Create an ArgType which corresponds to the ptrdiff_t/unsigned ptrdiff_t
|
|
/// type.
|
|
static ArgType makePtrdiffT(const ArgType &A) {
|
|
ArgType Res = A;
|
|
Res.TK = TypeKind::PtrdiffT;
|
|
return Res;
|
|
}
|
|
|
|
MatchKind matchesType(ASTContext &C, QualType argTy) const;
|
|
|
|
QualType getRepresentativeType(ASTContext &C) const;
|
|
|
|
ArgType makeVectorType(ASTContext &C, unsigned NumElts) const;
|
|
|
|
std::string getRepresentativeTypeName(ASTContext &C) const;
|
|
};
|
|
|
|
class OptionalAmount {
|
|
public:
|
|
enum HowSpecified { NotSpecified, Constant, Arg, Invalid };
|
|
|
|
OptionalAmount(HowSpecified howSpecified,
|
|
unsigned amount,
|
|
const char *amountStart,
|
|
unsigned amountLength,
|
|
bool usesPositionalArg)
|
|
: start(amountStart), length(amountLength), hs(howSpecified), amt(amount),
|
|
UsesPositionalArg(usesPositionalArg), UsesDotPrefix(false) {}
|
|
|
|
OptionalAmount(bool valid = true)
|
|
: start(nullptr),length(0), hs(valid ? NotSpecified : Invalid), amt(0),
|
|
UsesPositionalArg(false), UsesDotPrefix(false) {}
|
|
|
|
explicit OptionalAmount(unsigned Amount)
|
|
: start(nullptr), length(0), hs(Constant), amt(Amount),
|
|
UsesPositionalArg(false), UsesDotPrefix(false) {}
|
|
|
|
bool isInvalid() const {
|
|
return hs == Invalid;
|
|
}
|
|
|
|
HowSpecified getHowSpecified() const { return hs; }
|
|
void setHowSpecified(HowSpecified h) { hs = h; }
|
|
|
|
bool hasDataArgument() const { return hs == Arg; }
|
|
|
|
unsigned getArgIndex() const {
|
|
assert(hasDataArgument());
|
|
return amt;
|
|
}
|
|
|
|
unsigned getConstantAmount() const {
|
|
assert(hs == Constant);
|
|
return amt;
|
|
}
|
|
|
|
const char *getStart() const {
|
|
// We include the . character if it is given.
|
|
return start - UsesDotPrefix;
|
|
}
|
|
|
|
unsigned getConstantLength() const {
|
|
assert(hs == Constant);
|
|
return length + UsesDotPrefix;
|
|
}
|
|
|
|
ArgType getArgType(ASTContext &Ctx) const;
|
|
|
|
void toString(raw_ostream &os) const;
|
|
|
|
bool usesPositionalArg() const { return (bool) UsesPositionalArg; }
|
|
unsigned getPositionalArgIndex() const {
|
|
assert(hasDataArgument());
|
|
return amt + 1;
|
|
}
|
|
|
|
bool usesDotPrefix() const { return UsesDotPrefix; }
|
|
void setUsesDotPrefix() { UsesDotPrefix = true; }
|
|
|
|
private:
|
|
const char *start;
|
|
unsigned length;
|
|
HowSpecified hs;
|
|
unsigned amt;
|
|
bool UsesPositionalArg : 1;
|
|
bool UsesDotPrefix;
|
|
};
|
|
|
|
|
|
class FormatSpecifier {
|
|
protected:
|
|
LengthModifier LM;
|
|
OptionalAmount FieldWidth;
|
|
ConversionSpecifier CS;
|
|
OptionalAmount VectorNumElts;
|
|
|
|
/// Positional arguments, an IEEE extension:
|
|
/// IEEE Std 1003.1, 2004 Edition
|
|
/// http://www.opengroup.org/onlinepubs/009695399/functions/printf.html
|
|
bool UsesPositionalArg;
|
|
unsigned argIndex;
|
|
public:
|
|
FormatSpecifier(bool isPrintf)
|
|
: CS(isPrintf), VectorNumElts(false),
|
|
UsesPositionalArg(false), argIndex(0) {}
|
|
|
|
void setLengthModifier(LengthModifier lm) {
|
|
LM = lm;
|
|
}
|
|
|
|
void setUsesPositionalArg() { UsesPositionalArg = true; }
|
|
|
|
void setArgIndex(unsigned i) {
|
|
argIndex = i;
|
|
}
|
|
|
|
unsigned getArgIndex() const {
|
|
return argIndex;
|
|
}
|
|
|
|
unsigned getPositionalArgIndex() const {
|
|
return argIndex + 1;
|
|
}
|
|
|
|
const LengthModifier &getLengthModifier() const {
|
|
return LM;
|
|
}
|
|
|
|
const OptionalAmount &getFieldWidth() const {
|
|
return FieldWidth;
|
|
}
|
|
|
|
void setVectorNumElts(const OptionalAmount &Amt) {
|
|
VectorNumElts = Amt;
|
|
}
|
|
|
|
const OptionalAmount &getVectorNumElts() const {
|
|
return VectorNumElts;
|
|
}
|
|
|
|
void setFieldWidth(const OptionalAmount &Amt) {
|
|
FieldWidth = Amt;
|
|
}
|
|
|
|
bool usesPositionalArg() const { return UsesPositionalArg; }
|
|
|
|
bool hasValidLengthModifier(const TargetInfo &Target,
|
|
const LangOptions &LO) const;
|
|
|
|
bool hasStandardLengthModifier() const;
|
|
|
|
std::optional<LengthModifier> getCorrectedLengthModifier() const;
|
|
|
|
bool hasStandardConversionSpecifier(const LangOptions &LangOpt) const;
|
|
|
|
bool hasStandardLengthConversionCombination() const;
|
|
|
|
/// For a TypedefType QT, if it is a named integer type such as size_t,
|
|
/// assign the appropriate value to LM and return true.
|
|
static bool namedTypeToLengthModifier(QualType QT, LengthModifier &LM);
|
|
};
|
|
|
|
} // end analyze_format_string namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
/// Pieces specific to fprintf format strings.
|
|
|
|
namespace analyze_printf {
|
|
|
|
class PrintfConversionSpecifier :
|
|
public analyze_format_string::ConversionSpecifier {
|
|
public:
|
|
PrintfConversionSpecifier()
|
|
: ConversionSpecifier(true, nullptr, InvalidSpecifier) {}
|
|
|
|
PrintfConversionSpecifier(const char *pos, Kind k)
|
|
: ConversionSpecifier(true, pos, k) {}
|
|
|
|
bool isObjCArg() const { return kind >= ObjCBeg && kind <= ObjCEnd; }
|
|
bool isDoubleArg() const { return kind >= DoubleArgBeg &&
|
|
kind <= DoubleArgEnd; }
|
|
|
|
static bool classof(const analyze_format_string::ConversionSpecifier *CS) {
|
|
return CS->isPrintfKind();
|
|
}
|
|
};
|
|
|
|
using analyze_format_string::ArgType;
|
|
using analyze_format_string::LengthModifier;
|
|
using analyze_format_string::OptionalAmount;
|
|
using analyze_format_string::OptionalFlag;
|
|
|
|
class PrintfSpecifier : public analyze_format_string::FormatSpecifier {
|
|
OptionalFlag HasThousandsGrouping; // ''', POSIX extension.
|
|
OptionalFlag IsLeftJustified; // '-'
|
|
OptionalFlag HasPlusPrefix; // '+'
|
|
OptionalFlag HasSpacePrefix; // ' '
|
|
OptionalFlag HasAlternativeForm; // '#'
|
|
OptionalFlag HasLeadingZeroes; // '0'
|
|
OptionalFlag HasObjCTechnicalTerm; // '[tt]'
|
|
OptionalFlag IsPrivate; // '{private}'
|
|
OptionalFlag IsPublic; // '{public}'
|
|
OptionalFlag IsSensitive; // '{sensitive}'
|
|
OptionalAmount Precision;
|
|
StringRef MaskType;
|
|
|
|
ArgType getScalarArgType(ASTContext &Ctx, bool IsObjCLiteral) const;
|
|
|
|
public:
|
|
PrintfSpecifier()
|
|
: FormatSpecifier(/* isPrintf = */ true), HasThousandsGrouping("'"),
|
|
IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "),
|
|
HasAlternativeForm("#"), HasLeadingZeroes("0"),
|
|
HasObjCTechnicalTerm("tt"), IsPrivate("private"), IsPublic("public"),
|
|
IsSensitive("sensitive") {}
|
|
|
|
static PrintfSpecifier Parse(const char *beg, const char *end);
|
|
|
|
// Methods for incrementally constructing the PrintfSpecifier.
|
|
void setConversionSpecifier(const PrintfConversionSpecifier &cs) {
|
|
CS = cs;
|
|
}
|
|
void setHasThousandsGrouping(const char *position) {
|
|
HasThousandsGrouping.setPosition(position);
|
|
}
|
|
void setIsLeftJustified(const char *position) {
|
|
IsLeftJustified.setPosition(position);
|
|
}
|
|
void setHasPlusPrefix(const char *position) {
|
|
HasPlusPrefix.setPosition(position);
|
|
}
|
|
void setHasSpacePrefix(const char *position) {
|
|
HasSpacePrefix.setPosition(position);
|
|
}
|
|
void setHasAlternativeForm(const char *position) {
|
|
HasAlternativeForm.setPosition(position);
|
|
}
|
|
void setHasLeadingZeros(const char *position) {
|
|
HasLeadingZeroes.setPosition(position);
|
|
}
|
|
void setHasObjCTechnicalTerm(const char *position) {
|
|
HasObjCTechnicalTerm.setPosition(position);
|
|
}
|
|
void setIsPrivate(const char *position) { IsPrivate.setPosition(position); }
|
|
void setIsPublic(const char *position) { IsPublic.setPosition(position); }
|
|
void setIsSensitive(const char *position) {
|
|
IsSensitive.setPosition(position);
|
|
}
|
|
void setUsesPositionalArg() { UsesPositionalArg = true; }
|
|
|
|
// Methods for querying the format specifier.
|
|
|
|
const PrintfConversionSpecifier &getConversionSpecifier() const {
|
|
return cast<PrintfConversionSpecifier>(CS);
|
|
}
|
|
|
|
void setPrecision(const OptionalAmount &Amt) {
|
|
Precision = Amt;
|
|
Precision.setUsesDotPrefix();
|
|
}
|
|
|
|
const OptionalAmount &getPrecision() const {
|
|
return Precision;
|
|
}
|
|
|
|
bool consumesDataArgument() const {
|
|
return getConversionSpecifier().consumesDataArgument();
|
|
}
|
|
|
|
/// Returns the builtin type that a data argument
|
|
/// paired with this format specifier should have. This method
|
|
/// will return null if the format specifier does not have
|
|
/// a matching data argument or the matching argument matches
|
|
/// more than one type.
|
|
ArgType getArgType(ASTContext &Ctx, bool IsObjCLiteral) const;
|
|
|
|
const OptionalFlag &hasThousandsGrouping() const {
|
|
return HasThousandsGrouping;
|
|
}
|
|
const OptionalFlag &isLeftJustified() const { return IsLeftJustified; }
|
|
const OptionalFlag &hasPlusPrefix() const { return HasPlusPrefix; }
|
|
const OptionalFlag &hasAlternativeForm() const { return HasAlternativeForm; }
|
|
const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; }
|
|
const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; }
|
|
const OptionalFlag &hasObjCTechnicalTerm() const { return HasObjCTechnicalTerm; }
|
|
const OptionalFlag &isPrivate() const { return IsPrivate; }
|
|
const OptionalFlag &isPublic() const { return IsPublic; }
|
|
const OptionalFlag &isSensitive() const { return IsSensitive; }
|
|
bool usesPositionalArg() const { return UsesPositionalArg; }
|
|
|
|
StringRef getMaskType() const { return MaskType; }
|
|
void setMaskType(StringRef S) { MaskType = S; }
|
|
|
|
/// Changes the specifier and length according to a QualType, retaining any
|
|
/// flags or options. Returns true on success, or false when a conversion
|
|
/// was not successful.
|
|
bool fixType(QualType QT, const LangOptions &LangOpt, ASTContext &Ctx,
|
|
bool IsObjCLiteral);
|
|
|
|
void toString(raw_ostream &os) const;
|
|
|
|
// Validation methods - to check if any element results in undefined behavior
|
|
bool hasValidPlusPrefix() const;
|
|
bool hasValidAlternativeForm() const;
|
|
bool hasValidLeadingZeros() const;
|
|
bool hasValidSpacePrefix() const;
|
|
bool hasValidLeftJustified() const;
|
|
bool hasValidThousandsGroupingPrefix() const;
|
|
|
|
bool hasValidPrecision() const;
|
|
bool hasValidFieldWidth() const;
|
|
};
|
|
} // end analyze_printf namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
/// Pieces specific to fscanf format strings.
|
|
|
|
namespace analyze_scanf {
|
|
|
|
class ScanfConversionSpecifier :
|
|
public analyze_format_string::ConversionSpecifier {
|
|
public:
|
|
ScanfConversionSpecifier()
|
|
: ConversionSpecifier(false, nullptr, InvalidSpecifier) {}
|
|
|
|
ScanfConversionSpecifier(const char *pos, Kind k)
|
|
: ConversionSpecifier(false, pos, k) {}
|
|
|
|
static bool classof(const analyze_format_string::ConversionSpecifier *CS) {
|
|
return !CS->isPrintfKind();
|
|
}
|
|
};
|
|
|
|
using analyze_format_string::ArgType;
|
|
using analyze_format_string::LengthModifier;
|
|
using analyze_format_string::OptionalAmount;
|
|
using analyze_format_string::OptionalFlag;
|
|
|
|
class ScanfSpecifier : public analyze_format_string::FormatSpecifier {
|
|
OptionalFlag SuppressAssignment; // '*'
|
|
public:
|
|
ScanfSpecifier() :
|
|
FormatSpecifier(/* isPrintf = */ false),
|
|
SuppressAssignment("*") {}
|
|
|
|
void setSuppressAssignment(const char *position) {
|
|
SuppressAssignment.setPosition(position);
|
|
}
|
|
|
|
const OptionalFlag &getSuppressAssignment() const {
|
|
return SuppressAssignment;
|
|
}
|
|
|
|
void setConversionSpecifier(const ScanfConversionSpecifier &cs) {
|
|
CS = cs;
|
|
}
|
|
|
|
const ScanfConversionSpecifier &getConversionSpecifier() const {
|
|
return cast<ScanfConversionSpecifier>(CS);
|
|
}
|
|
|
|
bool consumesDataArgument() const {
|
|
return CS.consumesDataArgument() && !SuppressAssignment;
|
|
}
|
|
|
|
ArgType getArgType(ASTContext &Ctx) const;
|
|
|
|
bool fixType(QualType QT, QualType RawQT, const LangOptions &LangOpt,
|
|
ASTContext &Ctx);
|
|
|
|
void toString(raw_ostream &os) const;
|
|
|
|
static ScanfSpecifier Parse(const char *beg, const char *end);
|
|
};
|
|
|
|
} // end analyze_scanf namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Parsing and processing of format strings (both fprintf and fscanf).
|
|
|
|
namespace analyze_format_string {
|
|
|
|
enum PositionContext { FieldWidthPos = 0, PrecisionPos = 1 };
|
|
|
|
class FormatStringHandler {
|
|
public:
|
|
FormatStringHandler() {}
|
|
virtual ~FormatStringHandler();
|
|
|
|
virtual void HandleNullChar(const char *nullCharacter) {}
|
|
|
|
virtual void HandlePosition(const char *startPos, unsigned posLen) {}
|
|
|
|
virtual void HandleInvalidPosition(const char *startPos, unsigned posLen,
|
|
PositionContext p) {}
|
|
|
|
virtual void HandleZeroPosition(const char *startPos, unsigned posLen) {}
|
|
|
|
virtual void HandleIncompleteSpecifier(const char *startSpecifier,
|
|
unsigned specifierLen) {}
|
|
|
|
virtual void HandleEmptyObjCModifierFlag(const char *startFlags,
|
|
unsigned flagsLen) {}
|
|
|
|
virtual void HandleInvalidObjCModifierFlag(const char *startFlag,
|
|
unsigned flagLen) {}
|
|
|
|
virtual void HandleObjCFlagsWithNonObjCConversion(const char *flagsStart,
|
|
const char *flagsEnd,
|
|
const char *conversionPosition) {}
|
|
// Printf-specific handlers.
|
|
|
|
virtual bool HandleInvalidPrintfConversionSpecifier(
|
|
const analyze_printf::PrintfSpecifier &FS,
|
|
const char *startSpecifier,
|
|
unsigned specifierLen) {
|
|
return true;
|
|
}
|
|
|
|
virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
|
|
const char *startSpecifier,
|
|
unsigned specifierLen,
|
|
const TargetInfo &Target) {
|
|
return true;
|
|
}
|
|
|
|
/// Handle mask types whose sizes are not between one and eight bytes.
|
|
virtual void handleInvalidMaskType(StringRef MaskType) {}
|
|
|
|
// Scanf-specific handlers.
|
|
|
|
virtual bool HandleInvalidScanfConversionSpecifier(
|
|
const analyze_scanf::ScanfSpecifier &FS,
|
|
const char *startSpecifier,
|
|
unsigned specifierLen) {
|
|
return true;
|
|
}
|
|
|
|
virtual bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
|
|
const char *startSpecifier,
|
|
unsigned specifierLen) {
|
|
return true;
|
|
}
|
|
|
|
virtual void HandleIncompleteScanList(const char *start, const char *end) {}
|
|
};
|
|
|
|
bool ParsePrintfString(FormatStringHandler &H,
|
|
const char *beg, const char *end, const LangOptions &LO,
|
|
const TargetInfo &Target, bool isFreeBSDKPrintf);
|
|
|
|
bool ParseFormatStringHasSArg(const char *beg, const char *end,
|
|
const LangOptions &LO, const TargetInfo &Target);
|
|
|
|
bool ParseScanfString(FormatStringHandler &H,
|
|
const char *beg, const char *end, const LangOptions &LO,
|
|
const TargetInfo &Target);
|
|
|
|
/// Return true if the given string has at least one formatting specifier.
|
|
bool parseFormatStringHasFormattingSpecifiers(const char *Begin,
|
|
const char *End,
|
|
const LangOptions &LO,
|
|
const TargetInfo &Target);
|
|
|
|
} // end analyze_format_string namespace
|
|
} // end clang namespace
|
|
#endif
|