mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-04 00:06:50 +00:00
06d367c6c6
This reverts commit r285007 and reapply r284990, with a fix for the opencl test that I broke. Original commit message follows: These new builtins support a mechanism for logging OS events, using a printf-like format string to specify the layout of data in a buffer. The _buffer_size version of the builtin can be used to determine the size of the buffer to allocate to hold the data, and then __builtin_os_log_format can write data into that buffer. This implements format checking to report mismatches between the format string and the data arguments. Most of this code was written by Chris Willmore. Differential Revision: https://reviews.llvm.org/D25888 llvm-svn: 285019
954 lines
28 KiB
C++
954 lines
28 KiB
C++
// FormatString.cpp - Common stuff for handling printf/scanf formats -*- C++ -*-
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Shared details for processing format strings of printf and scanf
|
|
// (and friends).
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "FormatStringParsing.h"
|
|
#include "clang/Basic/LangOptions.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
#include "llvm/Support/ConvertUTF.h"
|
|
|
|
using clang::analyze_format_string::ArgType;
|
|
using clang::analyze_format_string::FormatStringHandler;
|
|
using clang::analyze_format_string::FormatSpecifier;
|
|
using clang::analyze_format_string::LengthModifier;
|
|
using clang::analyze_format_string::OptionalAmount;
|
|
using clang::analyze_format_string::PositionContext;
|
|
using clang::analyze_format_string::ConversionSpecifier;
|
|
using namespace clang;
|
|
|
|
// Key function to FormatStringHandler.
|
|
FormatStringHandler::~FormatStringHandler() {}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Functions for parsing format strings components in both printf and
|
|
// scanf format strings.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
OptionalAmount
|
|
clang::analyze_format_string::ParseAmount(const char *&Beg, const char *E) {
|
|
const char *I = Beg;
|
|
UpdateOnReturn <const char*> UpdateBeg(Beg, I);
|
|
|
|
unsigned accumulator = 0;
|
|
bool hasDigits = false;
|
|
|
|
for ( ; I != E; ++I) {
|
|
char c = *I;
|
|
if (c >= '0' && c <= '9') {
|
|
hasDigits = true;
|
|
accumulator = (accumulator * 10) + (c - '0');
|
|
continue;
|
|
}
|
|
|
|
if (hasDigits)
|
|
return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg,
|
|
false);
|
|
|
|
break;
|
|
}
|
|
|
|
return OptionalAmount();
|
|
}
|
|
|
|
OptionalAmount
|
|
clang::analyze_format_string::ParseNonPositionAmount(const char *&Beg,
|
|
const char *E,
|
|
unsigned &argIndex) {
|
|
if (*Beg == '*') {
|
|
++Beg;
|
|
return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false);
|
|
}
|
|
|
|
return ParseAmount(Beg, E);
|
|
}
|
|
|
|
OptionalAmount
|
|
clang::analyze_format_string::ParsePositionAmount(FormatStringHandler &H,
|
|
const char *Start,
|
|
const char *&Beg,
|
|
const char *E,
|
|
PositionContext p) {
|
|
if (*Beg == '*') {
|
|
const char *I = Beg + 1;
|
|
const OptionalAmount &Amt = ParseAmount(I, E);
|
|
|
|
if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) {
|
|
H.HandleInvalidPosition(Beg, I - Beg, p);
|
|
return OptionalAmount(false);
|
|
}
|
|
|
|
if (I == E) {
|
|
// No more characters left?
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return OptionalAmount(false);
|
|
}
|
|
|
|
assert(Amt.getHowSpecified() == OptionalAmount::Constant);
|
|
|
|
if (*I == '$') {
|
|
// Handle positional arguments
|
|
|
|
// Special case: '*0$', since this is an easy mistake.
|
|
if (Amt.getConstantAmount() == 0) {
|
|
H.HandleZeroPosition(Beg, I - Beg + 1);
|
|
return OptionalAmount(false);
|
|
}
|
|
|
|
const char *Tmp = Beg;
|
|
Beg = ++I;
|
|
|
|
return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1,
|
|
Tmp, 0, true);
|
|
}
|
|
|
|
H.HandleInvalidPosition(Beg, I - Beg, p);
|
|
return OptionalAmount(false);
|
|
}
|
|
|
|
return ParseAmount(Beg, E);
|
|
}
|
|
|
|
|
|
bool
|
|
clang::analyze_format_string::ParseFieldWidth(FormatStringHandler &H,
|
|
FormatSpecifier &CS,
|
|
const char *Start,
|
|
const char *&Beg, const char *E,
|
|
unsigned *argIndex) {
|
|
// FIXME: Support negative field widths.
|
|
if (argIndex) {
|
|
CS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex));
|
|
}
|
|
else {
|
|
const OptionalAmount Amt =
|
|
ParsePositionAmount(H, Start, Beg, E,
|
|
analyze_format_string::FieldWidthPos);
|
|
|
|
if (Amt.isInvalid())
|
|
return true;
|
|
CS.setFieldWidth(Amt);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H,
|
|
FormatSpecifier &FS,
|
|
const char *Start,
|
|
const char *&Beg,
|
|
const char *E) {
|
|
const char *I = Beg;
|
|
|
|
const OptionalAmount &Amt = ParseAmount(I, E);
|
|
|
|
if (I == E) {
|
|
// No more characters left?
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
|
|
if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') {
|
|
// Warn that positional arguments are non-standard.
|
|
H.HandlePosition(Start, I - Start);
|
|
|
|
// Special case: '%0$', since this is an easy mistake.
|
|
if (Amt.getConstantAmount() == 0) {
|
|
H.HandleZeroPosition(Start, I - Start);
|
|
return true;
|
|
}
|
|
|
|
FS.setArgIndex(Amt.getConstantAmount() - 1);
|
|
FS.setUsesPositionalArg();
|
|
// Update the caller's pointer if we decided to consume
|
|
// these characters.
|
|
Beg = I;
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
|
|
const char *&I,
|
|
const char *E,
|
|
const LangOptions &LO,
|
|
bool IsScanf) {
|
|
LengthModifier::Kind lmKind = LengthModifier::None;
|
|
const char *lmPosition = I;
|
|
switch (*I) {
|
|
default:
|
|
return false;
|
|
case 'h':
|
|
++I;
|
|
if (I != E && *I == 'h') {
|
|
++I;
|
|
lmKind = LengthModifier::AsChar;
|
|
} else {
|
|
lmKind = LengthModifier::AsShort;
|
|
}
|
|
break;
|
|
case 'l':
|
|
++I;
|
|
if (I != E && *I == 'l') {
|
|
++I;
|
|
lmKind = LengthModifier::AsLongLong;
|
|
} else {
|
|
lmKind = LengthModifier::AsLong;
|
|
}
|
|
break;
|
|
case 'j': lmKind = LengthModifier::AsIntMax; ++I; break;
|
|
case 'z': lmKind = LengthModifier::AsSizeT; ++I; break;
|
|
case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break;
|
|
case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break;
|
|
case 'q': lmKind = LengthModifier::AsQuad; ++I; break;
|
|
case 'a':
|
|
if (IsScanf && !LO.C99 && !LO.CPlusPlus11) {
|
|
// For scanf in C90, look at the next character to see if this should
|
|
// be parsed as the GNU extension 'a' length modifier. If not, this
|
|
// will be parsed as a conversion specifier.
|
|
++I;
|
|
if (I != E && (*I == 's' || *I == 'S' || *I == '[')) {
|
|
lmKind = LengthModifier::AsAllocate;
|
|
break;
|
|
}
|
|
--I;
|
|
}
|
|
return false;
|
|
case 'm':
|
|
if (IsScanf) {
|
|
lmKind = LengthModifier::AsMAllocate;
|
|
++I;
|
|
break;
|
|
}
|
|
return false;
|
|
// printf: AsInt64, AsInt32, AsInt3264
|
|
// scanf: AsInt64
|
|
case 'I':
|
|
if (I + 1 != E && I + 2 != E) {
|
|
if (I[1] == '6' && I[2] == '4') {
|
|
I += 3;
|
|
lmKind = LengthModifier::AsInt64;
|
|
break;
|
|
}
|
|
if (IsScanf)
|
|
return false;
|
|
|
|
if (I[1] == '3' && I[2] == '2') {
|
|
I += 3;
|
|
lmKind = LengthModifier::AsInt32;
|
|
break;
|
|
}
|
|
}
|
|
++I;
|
|
lmKind = LengthModifier::AsInt3264;
|
|
break;
|
|
case 'w':
|
|
lmKind = LengthModifier::AsWide; ++I; break;
|
|
}
|
|
LengthModifier lm(lmPosition, lmKind);
|
|
FS.setLengthModifier(lm);
|
|
return true;
|
|
}
|
|
|
|
bool clang::analyze_format_string::ParseUTF8InvalidSpecifier(
|
|
const char *SpecifierBegin, const char *FmtStrEnd, unsigned &Len) {
|
|
if (SpecifierBegin + 1 >= FmtStrEnd)
|
|
return false;
|
|
|
|
const llvm::UTF8 *SB =
|
|
reinterpret_cast<const llvm::UTF8 *>(SpecifierBegin + 1);
|
|
const llvm::UTF8 *SE = reinterpret_cast<const llvm::UTF8 *>(FmtStrEnd);
|
|
const char FirstByte = *SB;
|
|
|
|
// If the invalid specifier is a multibyte UTF-8 string, return the
|
|
// total length accordingly so that the conversion specifier can be
|
|
// properly updated to reflect a complete UTF-8 specifier.
|
|
unsigned NumBytes = llvm::getNumBytesForUTF8(FirstByte);
|
|
if (NumBytes == 1)
|
|
return false;
|
|
if (SB + NumBytes > SE)
|
|
return false;
|
|
|
|
Len = NumBytes + 1;
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Methods on ArgType.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
clang::analyze_format_string::ArgType::MatchKind
|
|
ArgType::matchesType(ASTContext &C, QualType argTy) const {
|
|
if (Ptr) {
|
|
// It has to be a pointer.
|
|
const PointerType *PT = argTy->getAs<PointerType>();
|
|
if (!PT)
|
|
return NoMatch;
|
|
|
|
// We cannot write through a const qualified pointer.
|
|
if (PT->getPointeeType().isConstQualified())
|
|
return NoMatch;
|
|
|
|
argTy = PT->getPointeeType();
|
|
}
|
|
|
|
switch (K) {
|
|
case InvalidTy:
|
|
llvm_unreachable("ArgType must be valid");
|
|
|
|
case UnknownTy:
|
|
return Match;
|
|
|
|
case AnyCharTy: {
|
|
if (const EnumType *ETy = argTy->getAs<EnumType>()) {
|
|
// If the enum is incomplete we know nothing about the underlying type.
|
|
// Assume that it's 'int'.
|
|
if (!ETy->getDecl()->isComplete())
|
|
return NoMatch;
|
|
argTy = ETy->getDecl()->getIntegerType();
|
|
}
|
|
|
|
if (const BuiltinType *BT = argTy->getAs<BuiltinType>())
|
|
switch (BT->getKind()) {
|
|
default:
|
|
break;
|
|
case BuiltinType::Char_S:
|
|
case BuiltinType::SChar:
|
|
case BuiltinType::UChar:
|
|
case BuiltinType::Char_U:
|
|
return Match;
|
|
}
|
|
return NoMatch;
|
|
}
|
|
|
|
case SpecificTy: {
|
|
if (const EnumType *ETy = argTy->getAs<EnumType>()) {
|
|
// If the enum is incomplete we know nothing about the underlying type.
|
|
// Assume that it's 'int'.
|
|
if (!ETy->getDecl()->isComplete())
|
|
argTy = C.IntTy;
|
|
else
|
|
argTy = ETy->getDecl()->getIntegerType();
|
|
}
|
|
argTy = C.getCanonicalType(argTy).getUnqualifiedType();
|
|
|
|
if (T == argTy)
|
|
return Match;
|
|
// Check for "compatible types".
|
|
if (const BuiltinType *BT = argTy->getAs<BuiltinType>())
|
|
switch (BT->getKind()) {
|
|
default:
|
|
break;
|
|
case BuiltinType::Char_S:
|
|
case BuiltinType::SChar:
|
|
case BuiltinType::Char_U:
|
|
case BuiltinType::UChar:
|
|
return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match
|
|
: NoMatch;
|
|
case BuiltinType::Short:
|
|
return T == C.UnsignedShortTy ? Match : NoMatch;
|
|
case BuiltinType::UShort:
|
|
return T == C.ShortTy ? Match : NoMatch;
|
|
case BuiltinType::Int:
|
|
return T == C.UnsignedIntTy ? Match : NoMatch;
|
|
case BuiltinType::UInt:
|
|
return T == C.IntTy ? Match : NoMatch;
|
|
case BuiltinType::Long:
|
|
return T == C.UnsignedLongTy ? Match : NoMatch;
|
|
case BuiltinType::ULong:
|
|
return T == C.LongTy ? Match : NoMatch;
|
|
case BuiltinType::LongLong:
|
|
return T == C.UnsignedLongLongTy ? Match : NoMatch;
|
|
case BuiltinType::ULongLong:
|
|
return T == C.LongLongTy ? Match : NoMatch;
|
|
}
|
|
return NoMatch;
|
|
}
|
|
|
|
case CStrTy: {
|
|
const PointerType *PT = argTy->getAs<PointerType>();
|
|
if (!PT)
|
|
return NoMatch;
|
|
QualType pointeeTy = PT->getPointeeType();
|
|
if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>())
|
|
switch (BT->getKind()) {
|
|
case BuiltinType::Void:
|
|
case BuiltinType::Char_U:
|
|
case BuiltinType::UChar:
|
|
case BuiltinType::Char_S:
|
|
case BuiltinType::SChar:
|
|
return Match;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NoMatch;
|
|
}
|
|
|
|
case WCStrTy: {
|
|
const PointerType *PT = argTy->getAs<PointerType>();
|
|
if (!PT)
|
|
return NoMatch;
|
|
QualType pointeeTy =
|
|
C.getCanonicalType(PT->getPointeeType()).getUnqualifiedType();
|
|
return pointeeTy == C.getWideCharType() ? Match : NoMatch;
|
|
}
|
|
|
|
case WIntTy: {
|
|
|
|
QualType PromoArg =
|
|
argTy->isPromotableIntegerType()
|
|
? C.getPromotedIntegerType(argTy) : argTy;
|
|
|
|
QualType WInt = C.getCanonicalType(C.getWIntType()).getUnqualifiedType();
|
|
PromoArg = C.getCanonicalType(PromoArg).getUnqualifiedType();
|
|
|
|
// If the promoted argument is the corresponding signed type of the
|
|
// wint_t type, then it should match.
|
|
if (PromoArg->hasSignedIntegerRepresentation() &&
|
|
C.getCorrespondingUnsignedType(PromoArg) == WInt)
|
|
return Match;
|
|
|
|
return WInt == PromoArg ? Match : NoMatch;
|
|
}
|
|
|
|
case CPointerTy:
|
|
if (argTy->isVoidPointerType()) {
|
|
return Match;
|
|
} if (argTy->isPointerType() || argTy->isObjCObjectPointerType() ||
|
|
argTy->isBlockPointerType() || argTy->isNullPtrType()) {
|
|
return NoMatchPedantic;
|
|
} else {
|
|
return NoMatch;
|
|
}
|
|
|
|
case ObjCPointerTy: {
|
|
if (argTy->getAs<ObjCObjectPointerType>() ||
|
|
argTy->getAs<BlockPointerType>())
|
|
return Match;
|
|
|
|
// Handle implicit toll-free bridging.
|
|
if (const PointerType *PT = argTy->getAs<PointerType>()) {
|
|
// Things such as CFTypeRef are really just opaque pointers
|
|
// to C structs representing CF types that can often be bridged
|
|
// to Objective-C objects. Since the compiler doesn't know which
|
|
// structs can be toll-free bridged, we just accept them all.
|
|
QualType pointee = PT->getPointeeType();
|
|
if (pointee->getAsStructureType() || pointee->isVoidType())
|
|
return Match;
|
|
}
|
|
return NoMatch;
|
|
}
|
|
}
|
|
|
|
llvm_unreachable("Invalid ArgType Kind!");
|
|
}
|
|
|
|
QualType ArgType::getRepresentativeType(ASTContext &C) const {
|
|
QualType Res;
|
|
switch (K) {
|
|
case InvalidTy:
|
|
llvm_unreachable("No representative type for Invalid ArgType");
|
|
case UnknownTy:
|
|
llvm_unreachable("No representative type for Unknown ArgType");
|
|
case AnyCharTy:
|
|
Res = C.CharTy;
|
|
break;
|
|
case SpecificTy:
|
|
Res = T;
|
|
break;
|
|
case CStrTy:
|
|
Res = C.getPointerType(C.CharTy);
|
|
break;
|
|
case WCStrTy:
|
|
Res = C.getPointerType(C.getWideCharType());
|
|
break;
|
|
case ObjCPointerTy:
|
|
Res = C.ObjCBuiltinIdTy;
|
|
break;
|
|
case CPointerTy:
|
|
Res = C.VoidPtrTy;
|
|
break;
|
|
case WIntTy: {
|
|
Res = C.getWIntType();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Ptr)
|
|
Res = C.getPointerType(Res);
|
|
return Res;
|
|
}
|
|
|
|
std::string ArgType::getRepresentativeTypeName(ASTContext &C) const {
|
|
std::string S = getRepresentativeType(C).getAsString();
|
|
|
|
std::string Alias;
|
|
if (Name) {
|
|
// Use a specific name for this type, e.g. "size_t".
|
|
Alias = Name;
|
|
if (Ptr) {
|
|
// If ArgType is actually a pointer to T, append an asterisk.
|
|
Alias += (Alias[Alias.size()-1] == '*') ? "*" : " *";
|
|
}
|
|
// If Alias is the same as the underlying type, e.g. wchar_t, then drop it.
|
|
if (S == Alias)
|
|
Alias.clear();
|
|
}
|
|
|
|
if (!Alias.empty())
|
|
return std::string("'") + Alias + "' (aka '" + S + "')";
|
|
return std::string("'") + S + "'";
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Methods on OptionalAmount.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ArgType
|
|
analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const {
|
|
return Ctx.IntTy;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Methods on LengthModifier.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
const char *
|
|
analyze_format_string::LengthModifier::toString() const {
|
|
switch (kind) {
|
|
case AsChar:
|
|
return "hh";
|
|
case AsShort:
|
|
return "h";
|
|
case AsLong: // or AsWideChar
|
|
return "l";
|
|
case AsLongLong:
|
|
return "ll";
|
|
case AsQuad:
|
|
return "q";
|
|
case AsIntMax:
|
|
return "j";
|
|
case AsSizeT:
|
|
return "z";
|
|
case AsPtrDiff:
|
|
return "t";
|
|
case AsInt32:
|
|
return "I32";
|
|
case AsInt3264:
|
|
return "I";
|
|
case AsInt64:
|
|
return "I64";
|
|
case AsLongDouble:
|
|
return "L";
|
|
case AsAllocate:
|
|
return "a";
|
|
case AsMAllocate:
|
|
return "m";
|
|
case AsWide:
|
|
return "w";
|
|
case None:
|
|
return "";
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Methods on ConversionSpecifier.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
const char *ConversionSpecifier::toString() const {
|
|
switch (kind) {
|
|
case dArg: return "d";
|
|
case DArg: return "D";
|
|
case iArg: return "i";
|
|
case oArg: return "o";
|
|
case OArg: return "O";
|
|
case uArg: return "u";
|
|
case UArg: return "U";
|
|
case xArg: return "x";
|
|
case XArg: return "X";
|
|
case fArg: return "f";
|
|
case FArg: return "F";
|
|
case eArg: return "e";
|
|
case EArg: return "E";
|
|
case gArg: return "g";
|
|
case GArg: return "G";
|
|
case aArg: return "a";
|
|
case AArg: return "A";
|
|
case cArg: return "c";
|
|
case sArg: return "s";
|
|
case pArg: return "p";
|
|
case PArg:
|
|
return "P";
|
|
case nArg: return "n";
|
|
case PercentArg: return "%";
|
|
case ScanListArg: return "[";
|
|
case InvalidSpecifier: return nullptr;
|
|
|
|
// POSIX unicode extensions.
|
|
case CArg: return "C";
|
|
case SArg: return "S";
|
|
|
|
// Objective-C specific specifiers.
|
|
case ObjCObjArg: return "@";
|
|
|
|
// FreeBSD kernel specific specifiers.
|
|
case FreeBSDbArg: return "b";
|
|
case FreeBSDDArg: return "D";
|
|
case FreeBSDrArg: return "r";
|
|
case FreeBSDyArg: return "y";
|
|
|
|
// GlibC specific specifiers.
|
|
case PrintErrno: return "m";
|
|
|
|
// MS specific specifiers.
|
|
case ZArg: return "Z";
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Optional<ConversionSpecifier>
|
|
ConversionSpecifier::getStandardSpecifier() const {
|
|
ConversionSpecifier::Kind NewKind;
|
|
|
|
switch (getKind()) {
|
|
default:
|
|
return None;
|
|
case DArg:
|
|
NewKind = dArg;
|
|
break;
|
|
case UArg:
|
|
NewKind = uArg;
|
|
break;
|
|
case OArg:
|
|
NewKind = oArg;
|
|
break;
|
|
}
|
|
|
|
ConversionSpecifier FixedCS(*this);
|
|
FixedCS.setKind(NewKind);
|
|
return FixedCS;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Methods on OptionalAmount.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void OptionalAmount::toString(raw_ostream &os) const {
|
|
switch (hs) {
|
|
case Invalid:
|
|
case NotSpecified:
|
|
return;
|
|
case Arg:
|
|
if (UsesDotPrefix)
|
|
os << ".";
|
|
if (usesPositionalArg())
|
|
os << "*" << getPositionalArgIndex() << "$";
|
|
else
|
|
os << "*";
|
|
break;
|
|
case Constant:
|
|
if (UsesDotPrefix)
|
|
os << ".";
|
|
os << amt;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
|
|
switch (LM.getKind()) {
|
|
case LengthModifier::None:
|
|
return true;
|
|
|
|
// Handle most integer flags
|
|
case LengthModifier::AsShort:
|
|
if (Target.getTriple().isOSMSVCRT()) {
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::cArg:
|
|
case ConversionSpecifier::CArg:
|
|
case ConversionSpecifier::sArg:
|
|
case ConversionSpecifier::SArg:
|
|
case ConversionSpecifier::ZArg:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
// Fall through.
|
|
case LengthModifier::AsChar:
|
|
case LengthModifier::AsLongLong:
|
|
case LengthModifier::AsQuad:
|
|
case LengthModifier::AsIntMax:
|
|
case LengthModifier::AsSizeT:
|
|
case LengthModifier::AsPtrDiff:
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::dArg:
|
|
case ConversionSpecifier::DArg:
|
|
case ConversionSpecifier::iArg:
|
|
case ConversionSpecifier::oArg:
|
|
case ConversionSpecifier::OArg:
|
|
case ConversionSpecifier::uArg:
|
|
case ConversionSpecifier::UArg:
|
|
case ConversionSpecifier::xArg:
|
|
case ConversionSpecifier::XArg:
|
|
case ConversionSpecifier::nArg:
|
|
return true;
|
|
case ConversionSpecifier::FreeBSDrArg:
|
|
case ConversionSpecifier::FreeBSDyArg:
|
|
return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS4();
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// Handle 'l' flag
|
|
case LengthModifier::AsLong: // or AsWideChar
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::dArg:
|
|
case ConversionSpecifier::DArg:
|
|
case ConversionSpecifier::iArg:
|
|
case ConversionSpecifier::oArg:
|
|
case ConversionSpecifier::OArg:
|
|
case ConversionSpecifier::uArg:
|
|
case ConversionSpecifier::UArg:
|
|
case ConversionSpecifier::xArg:
|
|
case ConversionSpecifier::XArg:
|
|
case ConversionSpecifier::aArg:
|
|
case ConversionSpecifier::AArg:
|
|
case ConversionSpecifier::fArg:
|
|
case ConversionSpecifier::FArg:
|
|
case ConversionSpecifier::eArg:
|
|
case ConversionSpecifier::EArg:
|
|
case ConversionSpecifier::gArg:
|
|
case ConversionSpecifier::GArg:
|
|
case ConversionSpecifier::nArg:
|
|
case ConversionSpecifier::cArg:
|
|
case ConversionSpecifier::sArg:
|
|
case ConversionSpecifier::ScanListArg:
|
|
case ConversionSpecifier::ZArg:
|
|
return true;
|
|
case ConversionSpecifier::FreeBSDrArg:
|
|
case ConversionSpecifier::FreeBSDyArg:
|
|
return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS4();
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case LengthModifier::AsLongDouble:
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::aArg:
|
|
case ConversionSpecifier::AArg:
|
|
case ConversionSpecifier::fArg:
|
|
case ConversionSpecifier::FArg:
|
|
case ConversionSpecifier::eArg:
|
|
case ConversionSpecifier::EArg:
|
|
case ConversionSpecifier::gArg:
|
|
case ConversionSpecifier::GArg:
|
|
return true;
|
|
// GNU libc extension.
|
|
case ConversionSpecifier::dArg:
|
|
case ConversionSpecifier::iArg:
|
|
case ConversionSpecifier::oArg:
|
|
case ConversionSpecifier::uArg:
|
|
case ConversionSpecifier::xArg:
|
|
case ConversionSpecifier::XArg:
|
|
return !Target.getTriple().isOSDarwin() &&
|
|
!Target.getTriple().isOSWindows();
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case LengthModifier::AsAllocate:
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::sArg:
|
|
case ConversionSpecifier::SArg:
|
|
case ConversionSpecifier::ScanListArg:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case LengthModifier::AsMAllocate:
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::cArg:
|
|
case ConversionSpecifier::CArg:
|
|
case ConversionSpecifier::sArg:
|
|
case ConversionSpecifier::SArg:
|
|
case ConversionSpecifier::ScanListArg:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
case LengthModifier::AsInt32:
|
|
case LengthModifier::AsInt3264:
|
|
case LengthModifier::AsInt64:
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::dArg:
|
|
case ConversionSpecifier::iArg:
|
|
case ConversionSpecifier::oArg:
|
|
case ConversionSpecifier::uArg:
|
|
case ConversionSpecifier::xArg:
|
|
case ConversionSpecifier::XArg:
|
|
return Target.getTriple().isOSMSVCRT();
|
|
default:
|
|
return false;
|
|
}
|
|
case LengthModifier::AsWide:
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::cArg:
|
|
case ConversionSpecifier::CArg:
|
|
case ConversionSpecifier::sArg:
|
|
case ConversionSpecifier::SArg:
|
|
case ConversionSpecifier::ZArg:
|
|
return Target.getTriple().isOSMSVCRT();
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
llvm_unreachable("Invalid LengthModifier Kind!");
|
|
}
|
|
|
|
bool FormatSpecifier::hasStandardLengthModifier() const {
|
|
switch (LM.getKind()) {
|
|
case LengthModifier::None:
|
|
case LengthModifier::AsChar:
|
|
case LengthModifier::AsShort:
|
|
case LengthModifier::AsLong:
|
|
case LengthModifier::AsLongLong:
|
|
case LengthModifier::AsIntMax:
|
|
case LengthModifier::AsSizeT:
|
|
case LengthModifier::AsPtrDiff:
|
|
case LengthModifier::AsLongDouble:
|
|
return true;
|
|
case LengthModifier::AsAllocate:
|
|
case LengthModifier::AsMAllocate:
|
|
case LengthModifier::AsQuad:
|
|
case LengthModifier::AsInt32:
|
|
case LengthModifier::AsInt3264:
|
|
case LengthModifier::AsInt64:
|
|
case LengthModifier::AsWide:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Invalid LengthModifier Kind!");
|
|
}
|
|
|
|
bool FormatSpecifier::hasStandardConversionSpecifier(
|
|
const LangOptions &LangOpt) const {
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::cArg:
|
|
case ConversionSpecifier::dArg:
|
|
case ConversionSpecifier::iArg:
|
|
case ConversionSpecifier::oArg:
|
|
case ConversionSpecifier::uArg:
|
|
case ConversionSpecifier::xArg:
|
|
case ConversionSpecifier::XArg:
|
|
case ConversionSpecifier::fArg:
|
|
case ConversionSpecifier::FArg:
|
|
case ConversionSpecifier::eArg:
|
|
case ConversionSpecifier::EArg:
|
|
case ConversionSpecifier::gArg:
|
|
case ConversionSpecifier::GArg:
|
|
case ConversionSpecifier::aArg:
|
|
case ConversionSpecifier::AArg:
|
|
case ConversionSpecifier::sArg:
|
|
case ConversionSpecifier::pArg:
|
|
case ConversionSpecifier::nArg:
|
|
case ConversionSpecifier::ObjCObjArg:
|
|
case ConversionSpecifier::ScanListArg:
|
|
case ConversionSpecifier::PercentArg:
|
|
case ConversionSpecifier::PArg:
|
|
return true;
|
|
case ConversionSpecifier::CArg:
|
|
case ConversionSpecifier::SArg:
|
|
return LangOpt.ObjC1 || LangOpt.ObjC2;
|
|
case ConversionSpecifier::InvalidSpecifier:
|
|
case ConversionSpecifier::FreeBSDbArg:
|
|
case ConversionSpecifier::FreeBSDDArg:
|
|
case ConversionSpecifier::FreeBSDrArg:
|
|
case ConversionSpecifier::FreeBSDyArg:
|
|
case ConversionSpecifier::PrintErrno:
|
|
case ConversionSpecifier::DArg:
|
|
case ConversionSpecifier::OArg:
|
|
case ConversionSpecifier::UArg:
|
|
case ConversionSpecifier::ZArg:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Invalid ConversionSpecifier Kind!");
|
|
}
|
|
|
|
bool FormatSpecifier::hasStandardLengthConversionCombination() const {
|
|
if (LM.getKind() == LengthModifier::AsLongDouble) {
|
|
switch(CS.getKind()) {
|
|
case ConversionSpecifier::dArg:
|
|
case ConversionSpecifier::iArg:
|
|
case ConversionSpecifier::oArg:
|
|
case ConversionSpecifier::uArg:
|
|
case ConversionSpecifier::xArg:
|
|
case ConversionSpecifier::XArg:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Optional<LengthModifier> FormatSpecifier::getCorrectedLengthModifier() const {
|
|
if (CS.isAnyIntArg() || CS.getKind() == ConversionSpecifier::nArg) {
|
|
if (LM.getKind() == LengthModifier::AsLongDouble ||
|
|
LM.getKind() == LengthModifier::AsQuad) {
|
|
LengthModifier FixedLM(LM);
|
|
FixedLM.setKind(LengthModifier::AsLongLong);
|
|
return FixedLM;
|
|
}
|
|
}
|
|
|
|
return None;
|
|
}
|
|
|
|
bool FormatSpecifier::namedTypeToLengthModifier(QualType QT,
|
|
LengthModifier &LM) {
|
|
assert(isa<TypedefType>(QT) && "Expected a TypedefType");
|
|
const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl();
|
|
|
|
for (;;) {
|
|
const IdentifierInfo *Identifier = Typedef->getIdentifier();
|
|
if (Identifier->getName() == "size_t") {
|
|
LM.setKind(LengthModifier::AsSizeT);
|
|
return true;
|
|
} else if (Identifier->getName() == "ssize_t") {
|
|
// Not C99, but common in Unix.
|
|
LM.setKind(LengthModifier::AsSizeT);
|
|
return true;
|
|
} else if (Identifier->getName() == "intmax_t") {
|
|
LM.setKind(LengthModifier::AsIntMax);
|
|
return true;
|
|
} else if (Identifier->getName() == "uintmax_t") {
|
|
LM.setKind(LengthModifier::AsIntMax);
|
|
return true;
|
|
} else if (Identifier->getName() == "ptrdiff_t") {
|
|
LM.setKind(LengthModifier::AsPtrDiff);
|
|
return true;
|
|
}
|
|
|
|
QualType T = Typedef->getUnderlyingType();
|
|
if (!isa<TypedefType>(T))
|
|
break;
|
|
|
|
Typedef = cast<TypedefType>(T)->getDecl();
|
|
}
|
|
return false;
|
|
}
|