mirror of
https://github.com/opnsense/src.git
synced 2026-06-04 14:26:03 -04:00
This commit merges the latest LLVM sources from the vendor space. It also updates the build glue to match the new sources. Clang's version number is changed to match LLVM's, which means /usr/include/clang/2.0 has been renamed to /usr/include/clang/2.8. Obtained from: projects/clangbsd
1040 lines
30 KiB
C++
1040 lines
30 KiB
C++
//= PrintfFormatStrings.cpp - Analysis of printf format strings --*- C++ -*-==//
|
||
//
|
||
// The LLVM Compiler Infrastructure
|
||
//
|
||
// This file is distributed under the University of Illinois Open Source
|
||
// License. See LICENSE.TXT for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
//
|
||
// Handling of format string in printf and friends. The structure of format
|
||
// strings for fprintf() are described in C99 7.19.6.1.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "clang/Analysis/Analyses/PrintfFormatString.h"
|
||
#include "clang/AST/ASTContext.h"
|
||
#include "clang/AST/Type.h"
|
||
#include "llvm/Support/raw_ostream.h"
|
||
|
||
using clang::analyze_printf::ArgTypeResult;
|
||
using clang::analyze_printf::FormatSpecifier;
|
||
using clang::analyze_printf::FormatStringHandler;
|
||
using clang::analyze_printf::OptionalAmount;
|
||
using clang::analyze_printf::PositionContext;
|
||
using clang::analyze_printf::ConversionSpecifier;
|
||
using clang::analyze_printf::LengthModifier;
|
||
|
||
using namespace clang;
|
||
|
||
namespace {
|
||
class FormatSpecifierResult {
|
||
FormatSpecifier FS;
|
||
const char *Start;
|
||
bool Stop;
|
||
public:
|
||
FormatSpecifierResult(bool stop = false)
|
||
: Start(0), Stop(stop) {}
|
||
FormatSpecifierResult(const char *start,
|
||
const FormatSpecifier &fs)
|
||
: FS(fs), Start(start), Stop(false) {}
|
||
|
||
const char *getStart() const { return Start; }
|
||
bool shouldStop() const { return Stop; }
|
||
bool hasValue() const { return Start != 0; }
|
||
const FormatSpecifier &getValue() const {
|
||
assert(hasValue());
|
||
return FS;
|
||
}
|
||
const FormatSpecifier &getValue() { return FS; }
|
||
};
|
||
} // end anonymous namespace
|
||
|
||
template <typename T>
|
||
class UpdateOnReturn {
|
||
T &ValueToUpdate;
|
||
const T &ValueToCopy;
|
||
public:
|
||
UpdateOnReturn(T &valueToUpdate, const T &valueToCopy)
|
||
: ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {}
|
||
|
||
~UpdateOnReturn() {
|
||
ValueToUpdate = ValueToCopy;
|
||
}
|
||
};
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// Methods for parsing format strings.
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
static OptionalAmount 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();
|
||
}
|
||
|
||
static OptionalAmount ParseNonPositionAmount(const char *&Beg, const char *E,
|
||
unsigned &argIndex) {
|
||
if (*Beg == '*') {
|
||
++Beg;
|
||
return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false);
|
||
}
|
||
|
||
return ParseAmount(Beg, E);
|
||
}
|
||
|
||
static OptionalAmount 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.HandleIncompleteFormatSpecifier(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);
|
||
}
|
||
|
||
static bool ParsePrecision(FormatStringHandler &H, FormatSpecifier &FS,
|
||
const char *Start, const char *&Beg, const char *E,
|
||
unsigned *argIndex) {
|
||
if (argIndex) {
|
||
FS.setPrecision(ParseNonPositionAmount(Beg, E, *argIndex));
|
||
}
|
||
else {
|
||
const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E,
|
||
analyze_printf::PrecisionPos);
|
||
if (Amt.isInvalid())
|
||
return true;
|
||
FS.setPrecision(Amt);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static bool ParseFieldWidth(FormatStringHandler &H, FormatSpecifier &FS,
|
||
const char *Start, const char *&Beg, const char *E,
|
||
unsigned *argIndex) {
|
||
// FIXME: Support negative field widths.
|
||
if (argIndex) {
|
||
FS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex));
|
||
}
|
||
else {
|
||
const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E,
|
||
analyze_printf::FieldWidthPos);
|
||
if (Amt.isInvalid())
|
||
return true;
|
||
FS.setFieldWidth(Amt);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static bool ParseArgPosition(FormatStringHandler &H,
|
||
FormatSpecifier &FS, const char *Start,
|
||
const char *&Beg, const char *E) {
|
||
|
||
using namespace clang::analyze_printf;
|
||
const char *I = Beg;
|
||
|
||
const OptionalAmount &Amt = ParseAmount(I, E);
|
||
|
||
if (I == E) {
|
||
// No more characters left?
|
||
H.HandleIncompleteFormatSpecifier(Start, E - Start);
|
||
return true;
|
||
}
|
||
|
||
if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') {
|
||
// 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;
|
||
}
|
||
|
||
static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H,
|
||
const char *&Beg,
|
||
const char *E,
|
||
unsigned &argIndex,
|
||
bool FormatExtensions) {
|
||
|
||
using namespace clang::analyze_printf;
|
||
|
||
const char *I = Beg;
|
||
const char *Start = 0;
|
||
UpdateOnReturn <const char*> UpdateBeg(Beg, I);
|
||
|
||
// Look for a '%' character that indicates the start of a format specifier.
|
||
for ( ; I != E ; ++I) {
|
||
char c = *I;
|
||
if (c == '\0') {
|
||
// Detect spurious null characters, which are likely errors.
|
||
H.HandleNullChar(I);
|
||
return true;
|
||
}
|
||
if (c == '%') {
|
||
Start = I++; // Record the start of the format specifier.
|
||
break;
|
||
}
|
||
}
|
||
|
||
// No format specifier found?
|
||
if (!Start)
|
||
return false;
|
||
|
||
if (I == E) {
|
||
// No more characters left?
|
||
H.HandleIncompleteFormatSpecifier(Start, E - Start);
|
||
return true;
|
||
}
|
||
|
||
FormatSpecifier FS;
|
||
if (ParseArgPosition(H, FS, Start, I, E))
|
||
return true;
|
||
|
||
if (I == E) {
|
||
// No more characters left?
|
||
H.HandleIncompleteFormatSpecifier(Start, E - Start);
|
||
return true;
|
||
}
|
||
|
||
// Look for flags (if any).
|
||
bool hasMore = true;
|
||
for ( ; I != E; ++I) {
|
||
switch (*I) {
|
||
default: hasMore = false; break;
|
||
case '-': FS.setIsLeftJustified(I); break;
|
||
case '+': FS.setHasPlusPrefix(I); break;
|
||
case ' ': FS.setHasSpacePrefix(I); break;
|
||
case '#': FS.setHasAlternativeForm(I); break;
|
||
case '0': FS.setHasLeadingZeros(I); break;
|
||
}
|
||
if (!hasMore)
|
||
break;
|
||
}
|
||
|
||
if (I == E) {
|
||
// No more characters left?
|
||
H.HandleIncompleteFormatSpecifier(Start, E - Start);
|
||
return true;
|
||
}
|
||
|
||
// Look for the field width (if any).
|
||
if (ParseFieldWidth(H, FS, Start, I, E,
|
||
FS.usesPositionalArg() ? 0 : &argIndex))
|
||
return true;
|
||
|
||
if (I == E) {
|
||
// No more characters left?
|
||
H.HandleIncompleteFormatSpecifier(Start, E - Start);
|
||
return true;
|
||
}
|
||
|
||
// Look for the precision (if any).
|
||
if (*I == '.') {
|
||
++I;
|
||
if (I == E) {
|
||
H.HandleIncompleteFormatSpecifier(Start, E - Start);
|
||
return true;
|
||
}
|
||
|
||
if (ParsePrecision(H, FS, Start, I, E,
|
||
FS.usesPositionalArg() ? 0 : &argIndex))
|
||
return true;
|
||
|
||
if (I == E) {
|
||
// No more characters left?
|
||
H.HandleIncompleteFormatSpecifier(Start, E - Start);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// Look for the length modifier.
|
||
LengthModifier::Kind lmKind = LengthModifier::None;
|
||
const char *lmPosition = I;
|
||
switch (*I) {
|
||
default:
|
||
break;
|
||
case 'h':
|
||
++I;
|
||
lmKind = (I != E && *I == 'h') ?
|
||
++I, LengthModifier::AsChar : LengthModifier::AsShort;
|
||
break;
|
||
case 'l':
|
||
++I;
|
||
lmKind = (I != E && *I == 'l') ?
|
||
++I, LengthModifier::AsLongLong : 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::AsLongLong; ++I; break;
|
||
}
|
||
LengthModifier lm(lmPosition, lmKind);
|
||
FS.setLengthModifier(lm);
|
||
|
||
if (I == E) {
|
||
// No more characters left?
|
||
H.HandleIncompleteFormatSpecifier(Start, E - Start);
|
||
return true;
|
||
}
|
||
|
||
if (*I == '\0') {
|
||
// Detect spurious null characters, which are likely errors.
|
||
H.HandleNullChar(I);
|
||
return true;
|
||
}
|
||
|
||
// Finally, look for the conversion specifier.
|
||
const char *conversionPosition = I++;
|
||
ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier;
|
||
switch (*conversionPosition) {
|
||
default:
|
||
break;
|
||
// C99: 7.19.6.1 (section 8).
|
||
case '%': k = ConversionSpecifier::PercentArg; break;
|
||
case 'A': k = ConversionSpecifier::AArg; break;
|
||
case 'E': k = ConversionSpecifier::EArg; break;
|
||
case 'F': k = ConversionSpecifier::FArg; break;
|
||
case 'G': k = ConversionSpecifier::GArg; break;
|
||
case 'X': k = ConversionSpecifier::XArg; break;
|
||
case 'a': k = ConversionSpecifier::aArg; break;
|
||
case 'c': k = ConversionSpecifier::IntAsCharArg; break;
|
||
case 'd': k = ConversionSpecifier::dArg; break;
|
||
case 'e': k = ConversionSpecifier::eArg; break;
|
||
case 'f': k = ConversionSpecifier::fArg; break;
|
||
case 'g': k = ConversionSpecifier::gArg; break;
|
||
case 'i': k = ConversionSpecifier::iArg; break;
|
||
case 'n': k = ConversionSpecifier::OutIntPtrArg; break;
|
||
case 'o': k = ConversionSpecifier::oArg; break;
|
||
case 'p': k = ConversionSpecifier::VoidPtrArg; break;
|
||
case 's': k = ConversionSpecifier::CStrArg; break;
|
||
case 'u': k = ConversionSpecifier::uArg; break;
|
||
case 'x': k = ConversionSpecifier::xArg; break;
|
||
// Mac OS X (unicode) specific
|
||
case 'C': k = ConversionSpecifier::CArg; break;
|
||
case 'S': k = ConversionSpecifier::UnicodeStrArg; break;
|
||
// Objective-C.
|
||
case '@': k = ConversionSpecifier::ObjCObjArg; break;
|
||
// Glibc specific.
|
||
case 'm': k = ConversionSpecifier::PrintErrno; break;
|
||
// FreeBSD format extensions
|
||
case 'b': if (FormatExtensions) k = ConversionSpecifier::bArg; break; /* check for int and then char * */
|
||
case 'r': if (FormatExtensions) k = ConversionSpecifier::xArg; break;
|
||
case 'y': if (FormatExtensions) k = ConversionSpecifier::iArg; break;
|
||
case 'D': if (FormatExtensions) k = ConversionSpecifier::DArg; break; /* check for u_char * pointer and a char * string */
|
||
}
|
||
ConversionSpecifier CS(conversionPosition, k);
|
||
FS.setConversionSpecifier(CS);
|
||
if (CS.consumesDataArgument() && !FS.usesPositionalArg())
|
||
FS.setArgIndex(argIndex++);
|
||
// FreeBSD extension
|
||
if (k == ConversionSpecifier::bArg || k == ConversionSpecifier::DArg)
|
||
argIndex++;
|
||
|
||
if (k == ConversionSpecifier::InvalidSpecifier) {
|
||
// Assume the conversion takes one argument.
|
||
return !H.HandleInvalidConversionSpecifier(FS, Beg, I - Beg);
|
||
}
|
||
return FormatSpecifierResult(Start, FS);
|
||
}
|
||
|
||
bool clang::analyze_printf::ParseFormatString(FormatStringHandler &H,
|
||
const char *I, const char *E, bool FormatExtensions) {
|
||
|
||
unsigned argIndex = 0;
|
||
|
||
// Keep looking for a format specifier until we have exhausted the string.
|
||
while (I != E) {
|
||
const FormatSpecifierResult &FSR = ParseFormatSpecifier(H, I, E, argIndex, FormatExtensions);
|
||
// Did a fail-stop error of any kind occur when parsing the specifier?
|
||
// If so, don't do any more processing.
|
||
if (FSR.shouldStop())
|
||
return true;;
|
||
// Did we exhaust the string or encounter an error that
|
||
// we can recover from?
|
||
if (!FSR.hasValue())
|
||
continue;
|
||
// We have a format specifier. Pass it to the callback.
|
||
if (!H.HandleFormatSpecifier(FSR.getValue(), FSR.getStart(),
|
||
I - FSR.getStart()))
|
||
return true;
|
||
}
|
||
assert(I == E && "Format string not exhausted");
|
||
return false;
|
||
}
|
||
|
||
FormatStringHandler::~FormatStringHandler() {}
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// Methods on ArgTypeResult.
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const {
|
||
switch (K) {
|
||
case InvalidTy:
|
||
assert(false && "ArgTypeResult must be valid");
|
||
return true;
|
||
|
||
case UnknownTy:
|
||
return true;
|
||
|
||
case SpecificTy: {
|
||
argTy = C.getCanonicalType(argTy).getUnqualifiedType();
|
||
if (T == argTy)
|
||
return true;
|
||
if (const BuiltinType *BT = argTy->getAs<BuiltinType>())
|
||
switch (BT->getKind()) {
|
||
default:
|
||
break;
|
||
case BuiltinType::Char_S:
|
||
case BuiltinType::SChar:
|
||
return T == C.UnsignedCharTy;
|
||
case BuiltinType::Char_U:
|
||
case BuiltinType::UChar:
|
||
return T == C.SignedCharTy;
|
||
case BuiltinType::Short:
|
||
return T == C.UnsignedShortTy;
|
||
case BuiltinType::UShort:
|
||
return T == C.ShortTy;
|
||
case BuiltinType::Int:
|
||
return T == C.UnsignedIntTy;
|
||
case BuiltinType::UInt:
|
||
return T == C.IntTy;
|
||
case BuiltinType::Long:
|
||
return T == C.UnsignedLongTy;
|
||
case BuiltinType::ULong:
|
||
return T == C.LongTy;
|
||
case BuiltinType::LongLong:
|
||
return T == C.UnsignedLongLongTy;
|
||
case BuiltinType::ULongLong:
|
||
return T == C.LongLongTy;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
case CStrTy: {
|
||
const PointerType *PT = argTy->getAs<PointerType>();
|
||
if (!PT)
|
||
return false;
|
||
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 true;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
case WCStrTy: {
|
||
const PointerType *PT = argTy->getAs<PointerType>();
|
||
if (!PT)
|
||
return false;
|
||
QualType pointeeTy =
|
||
C.getCanonicalType(PT->getPointeeType()).getUnqualifiedType();
|
||
return pointeeTy == C.getWCharType();
|
||
}
|
||
|
||
case CPointerTy:
|
||
return argTy->getAs<PointerType>() != NULL ||
|
||
argTy->getAs<ObjCObjectPointerType>() != NULL;
|
||
|
||
case ObjCPointerTy:
|
||
return argTy->getAs<ObjCObjectPointerType>() != NULL;
|
||
}
|
||
|
||
// FIXME: Should be unreachable, but Clang is currently emitting
|
||
// a warning.
|
||
return false;
|
||
}
|
||
|
||
QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const {
|
||
switch (K) {
|
||
case InvalidTy:
|
||
assert(false && "No representative type for Invalid ArgTypeResult");
|
||
// Fall-through.
|
||
case UnknownTy:
|
||
return QualType();
|
||
case SpecificTy:
|
||
return T;
|
||
case CStrTy:
|
||
return C.getPointerType(C.CharTy);
|
||
case WCStrTy:
|
||
return C.getPointerType(C.getWCharType());
|
||
case ObjCPointerTy:
|
||
return C.ObjCBuiltinIdTy;
|
||
case CPointerTy:
|
||
return C.VoidPtrTy;
|
||
}
|
||
|
||
// FIXME: Should be unreachable, but Clang is currently emitting
|
||
// a warning.
|
||
return QualType();
|
||
}
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// Methods on OptionalAmount.
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
ArgTypeResult OptionalAmount::getArgType(ASTContext &Ctx) const {
|
||
return Ctx.IntTy;
|
||
}
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// Methods on ConversionSpecifier.
|
||
//===----------------------------------------------------------------------===//
|
||
const char *ConversionSpecifier::toString() const {
|
||
switch (kind) {
|
||
case bArg: return "b";
|
||
case dArg: return "d";
|
||
case iArg: return "i";
|
||
case oArg: return "o";
|
||
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 IntAsCharArg: return "c";
|
||
case CStrArg: return "s";
|
||
case VoidPtrArg: return "p";
|
||
case OutIntPtrArg: return "n";
|
||
case PercentArg: return "%";
|
||
case InvalidSpecifier: return NULL;
|
||
|
||
// MacOS X unicode extensions.
|
||
case CArg: return "C";
|
||
case UnicodeStrArg: return "S";
|
||
|
||
// Objective-C specific specifiers.
|
||
case ObjCObjArg: return "@";
|
||
|
||
// GlibC specific specifiers.
|
||
case PrintErrno: return "m";
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// Methods on LengthModifier.
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
const char *LengthModifier::toString() const {
|
||
switch (kind) {
|
||
case AsChar:
|
||
return "hh";
|
||
case AsShort:
|
||
return "h";
|
||
case AsLong: // or AsWideChar
|
||
return "l";
|
||
case AsLongLong:
|
||
return "ll";
|
||
case AsIntMax:
|
||
return "j";
|
||
case AsSizeT:
|
||
return "z";
|
||
case AsPtrDiff:
|
||
return "t";
|
||
case AsLongDouble:
|
||
return "L";
|
||
case None:
|
||
return "";
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// Methods on OptionalAmount.
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
void OptionalAmount::toString(llvm::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;
|
||
}
|
||
}
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// Methods on FormatSpecifier.
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const {
|
||
if (!CS.consumesDataArgument())
|
||
return ArgTypeResult::Invalid();
|
||
|
||
if (CS.isIntArg())
|
||
switch (LM.getKind()) {
|
||
case LengthModifier::AsLongDouble:
|
||
return ArgTypeResult::Invalid();
|
||
case LengthModifier::None: return Ctx.IntTy;
|
||
case LengthModifier::AsChar: return Ctx.SignedCharTy;
|
||
case LengthModifier::AsShort: return Ctx.ShortTy;
|
||
case LengthModifier::AsLong: return Ctx.LongTy;
|
||
case LengthModifier::AsLongLong: return Ctx.LongLongTy;
|
||
case LengthModifier::AsIntMax:
|
||
// FIXME: Return unknown for now.
|
||
return ArgTypeResult();
|
||
case LengthModifier::AsSizeT: return Ctx.getSizeType();
|
||
case LengthModifier::AsPtrDiff: return Ctx.getPointerDiffType();
|
||
}
|
||
|
||
if (CS.isUIntArg())
|
||
switch (LM.getKind()) {
|
||
case LengthModifier::AsLongDouble:
|
||
return ArgTypeResult::Invalid();
|
||
case LengthModifier::None: return Ctx.UnsignedIntTy;
|
||
case LengthModifier::AsChar: return Ctx.UnsignedCharTy;
|
||
case LengthModifier::AsShort: return Ctx.UnsignedShortTy;
|
||
case LengthModifier::AsLong: return Ctx.UnsignedLongTy;
|
||
case LengthModifier::AsLongLong: return Ctx.UnsignedLongLongTy;
|
||
case LengthModifier::AsIntMax:
|
||
// FIXME: Return unknown for now.
|
||
return ArgTypeResult();
|
||
case LengthModifier::AsSizeT:
|
||
// FIXME: How to get the corresponding unsigned
|
||
// version of size_t?
|
||
return ArgTypeResult();
|
||
case LengthModifier::AsPtrDiff:
|
||
// FIXME: How to get the corresponding unsigned
|
||
// version of ptrdiff_t?
|
||
return ArgTypeResult();
|
||
}
|
||
|
||
if (CS.isDoubleArg()) {
|
||
if (LM.getKind() == LengthModifier::AsLongDouble)
|
||
return Ctx.LongDoubleTy;
|
||
return Ctx.DoubleTy;
|
||
}
|
||
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::CStrArg:
|
||
return ArgTypeResult(LM.getKind() == LengthModifier::AsWideChar ?
|
||
ArgTypeResult::WCStrTy : ArgTypeResult::CStrTy);
|
||
case ConversionSpecifier::UnicodeStrArg:
|
||
// FIXME: This appears to be Mac OS X specific.
|
||
return ArgTypeResult::WCStrTy;
|
||
case ConversionSpecifier::CArg:
|
||
return Ctx.WCharTy;
|
||
case ConversionSpecifier::VoidPtrArg:
|
||
return ArgTypeResult::CPointerTy;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
// FIXME: Handle other cases.
|
||
return ArgTypeResult();
|
||
}
|
||
|
||
bool FormatSpecifier::fixType(QualType QT) {
|
||
// Handle strings first (char *, wchar_t *)
|
||
if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) {
|
||
CS.setKind(ConversionSpecifier::CStrArg);
|
||
|
||
// Disable irrelevant flags
|
||
HasAlternativeForm = 0;
|
||
HasLeadingZeroes = 0;
|
||
|
||
// Set the long length modifier for wide characters
|
||
if (QT->getPointeeType()->isWideCharType())
|
||
LM.setKind(LengthModifier::AsWideChar);
|
||
|
||
return true;
|
||
}
|
||
|
||
// We can only work with builtin types.
|
||
if (!QT->isBuiltinType())
|
||
return false;
|
||
|
||
// Everything else should be a base type
|
||
const BuiltinType *BT = QT->getAs<BuiltinType>();
|
||
|
||
// Set length modifier
|
||
switch (BT->getKind()) {
|
||
default:
|
||
// The rest of the conversions are either optional or for non-builtin types
|
||
LM.setKind(LengthModifier::None);
|
||
break;
|
||
|
||
case BuiltinType::WChar:
|
||
case BuiltinType::Long:
|
||
case BuiltinType::ULong:
|
||
LM.setKind(LengthModifier::AsLong);
|
||
break;
|
||
|
||
case BuiltinType::LongLong:
|
||
case BuiltinType::ULongLong:
|
||
LM.setKind(LengthModifier::AsLongLong);
|
||
break;
|
||
|
||
case BuiltinType::LongDouble:
|
||
LM.setKind(LengthModifier::AsLongDouble);
|
||
break;
|
||
}
|
||
|
||
// Set conversion specifier and disable any flags which do not apply to it.
|
||
if (QT->isAnyCharacterType()) {
|
||
CS.setKind(ConversionSpecifier::IntAsCharArg);
|
||
Precision.setHowSpecified(OptionalAmount::NotSpecified);
|
||
HasAlternativeForm = 0;
|
||
HasLeadingZeroes = 0;
|
||
HasPlusPrefix = 0;
|
||
}
|
||
// Test for Floating type first as LongDouble can pass isUnsignedIntegerType
|
||
else if (QT->isRealFloatingType()) {
|
||
CS.setKind(ConversionSpecifier::fArg);
|
||
}
|
||
else if (QT->isPointerType()) {
|
||
CS.setKind(ConversionSpecifier::VoidPtrArg);
|
||
Precision.setHowSpecified(OptionalAmount::NotSpecified);
|
||
HasAlternativeForm = 0;
|
||
HasLeadingZeroes = 0;
|
||
HasPlusPrefix = 0;
|
||
}
|
||
else if (QT->isSignedIntegerType()) {
|
||
CS.setKind(ConversionSpecifier::dArg);
|
||
HasAlternativeForm = 0;
|
||
}
|
||
else if (QT->isUnsignedIntegerType()) {
|
||
CS.setKind(ConversionSpecifier::uArg);
|
||
HasAlternativeForm = 0;
|
||
HasPlusPrefix = 0;
|
||
}
|
||
else {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
void FormatSpecifier::toString(llvm::raw_ostream &os) const {
|
||
// Whilst some features have no defined order, we are using the order
|
||
// appearing in the C99 standard (ISO/IEC 9899:1999 (E) <20>7.19.6.1)
|
||
os << "%";
|
||
|
||
// Positional args
|
||
if (usesPositionalArg()) {
|
||
os << getPositionalArgIndex() << "$";
|
||
}
|
||
|
||
// Conversion flags
|
||
if (IsLeftJustified) os << "-";
|
||
if (HasPlusPrefix) os << "+";
|
||
if (HasSpacePrefix) os << " ";
|
||
if (HasAlternativeForm) os << "#";
|
||
if (HasLeadingZeroes) os << "0";
|
||
|
||
// Minimum field width
|
||
FieldWidth.toString(os);
|
||
// Precision
|
||
Precision.toString(os);
|
||
// Length modifier
|
||
os << LM.toString();
|
||
// Conversion specifier
|
||
os << CS.toString();
|
||
}
|
||
|
||
bool FormatSpecifier::hasValidPlusPrefix() const {
|
||
if (!HasPlusPrefix)
|
||
return true;
|
||
|
||
// The plus prefix only makes sense for signed conversions
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::dArg:
|
||
case ConversionSpecifier::iArg:
|
||
case ConversionSpecifier::fArg:
|
||
case ConversionSpecifier::FArg:
|
||
case ConversionSpecifier::eArg:
|
||
case ConversionSpecifier::EArg:
|
||
case ConversionSpecifier::gArg:
|
||
case ConversionSpecifier::GArg:
|
||
case ConversionSpecifier::aArg:
|
||
case ConversionSpecifier::AArg:
|
||
return true;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool FormatSpecifier::hasValidAlternativeForm() const {
|
||
if (!HasAlternativeForm)
|
||
return true;
|
||
|
||
// Alternate form flag only valid with the oxaAeEfFgG conversions
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::oArg:
|
||
case ConversionSpecifier::xArg:
|
||
case ConversionSpecifier::aArg:
|
||
case ConversionSpecifier::AArg:
|
||
case ConversionSpecifier::eArg:
|
||
case ConversionSpecifier::EArg:
|
||
case ConversionSpecifier::fArg:
|
||
case ConversionSpecifier::FArg:
|
||
case ConversionSpecifier::gArg:
|
||
case ConversionSpecifier::GArg:
|
||
return true;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool FormatSpecifier::hasValidLeadingZeros() const {
|
||
if (!HasLeadingZeroes)
|
||
return true;
|
||
|
||
// Leading zeroes flag only valid with the diouxXaAeEfFgG conversions
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::dArg:
|
||
case ConversionSpecifier::iArg:
|
||
case ConversionSpecifier::oArg:
|
||
case ConversionSpecifier::uArg:
|
||
case ConversionSpecifier::xArg:
|
||
case ConversionSpecifier::XArg:
|
||
case ConversionSpecifier::aArg:
|
||
case ConversionSpecifier::AArg:
|
||
case ConversionSpecifier::eArg:
|
||
case ConversionSpecifier::EArg:
|
||
case ConversionSpecifier::fArg:
|
||
case ConversionSpecifier::FArg:
|
||
case ConversionSpecifier::gArg:
|
||
case ConversionSpecifier::GArg:
|
||
return true;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool FormatSpecifier::hasValidSpacePrefix() const {
|
||
if (!HasSpacePrefix)
|
||
return true;
|
||
|
||
// The space prefix only makes sense for signed conversions
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::dArg:
|
||
case ConversionSpecifier::iArg:
|
||
case ConversionSpecifier::fArg:
|
||
case ConversionSpecifier::FArg:
|
||
case ConversionSpecifier::eArg:
|
||
case ConversionSpecifier::EArg:
|
||
case ConversionSpecifier::gArg:
|
||
case ConversionSpecifier::GArg:
|
||
case ConversionSpecifier::aArg:
|
||
case ConversionSpecifier::AArg:
|
||
return true;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool FormatSpecifier::hasValidLeftJustified() const {
|
||
if (!IsLeftJustified)
|
||
return true;
|
||
|
||
// The left justified flag is valid for all conversions except n
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::OutIntPtrArg:
|
||
return false;
|
||
|
||
default:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
bool FormatSpecifier::hasValidLengthModifier() const {
|
||
switch (LM.getKind()) {
|
||
case LengthModifier::None:
|
||
return true;
|
||
|
||
// Handle most integer flags
|
||
case LengthModifier::AsChar:
|
||
case LengthModifier::AsShort:
|
||
case LengthModifier::AsLongLong:
|
||
case LengthModifier::AsIntMax:
|
||
case LengthModifier::AsSizeT:
|
||
case LengthModifier::AsPtrDiff:
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::dArg:
|
||
case ConversionSpecifier::iArg:
|
||
case ConversionSpecifier::oArg:
|
||
case ConversionSpecifier::uArg:
|
||
case ConversionSpecifier::xArg:
|
||
case ConversionSpecifier::XArg:
|
||
case ConversionSpecifier::OutIntPtrArg:
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
|
||
// Handle 'l' flag
|
||
case LengthModifier::AsLong:
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::dArg:
|
||
case ConversionSpecifier::iArg:
|
||
case ConversionSpecifier::oArg:
|
||
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::OutIntPtrArg:
|
||
case ConversionSpecifier::IntAsCharArg:
|
||
case ConversionSpecifier::CStrArg:
|
||
return true;
|
||
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;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool FormatSpecifier::hasValidPrecision() const {
|
||
if (Precision.getHowSpecified() == OptionalAmount::NotSpecified)
|
||
return true;
|
||
|
||
// Precision is only valid with the diouxXaAeEfFgGs conversions
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::dArg:
|
||
case ConversionSpecifier::iArg:
|
||
case ConversionSpecifier::oArg:
|
||
case ConversionSpecifier::uArg:
|
||
case ConversionSpecifier::xArg:
|
||
case ConversionSpecifier::XArg:
|
||
case ConversionSpecifier::aArg:
|
||
case ConversionSpecifier::AArg:
|
||
case ConversionSpecifier::eArg:
|
||
case ConversionSpecifier::EArg:
|
||
case ConversionSpecifier::fArg:
|
||
case ConversionSpecifier::FArg:
|
||
case ConversionSpecifier::gArg:
|
||
case ConversionSpecifier::GArg:
|
||
case ConversionSpecifier::CStrArg:
|
||
return true;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
bool FormatSpecifier::hasValidFieldWidth() const {
|
||
if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified)
|
||
return true;
|
||
|
||
// The field width is valid for all conversions except n
|
||
switch (CS.getKind()) {
|
||
case ConversionSpecifier::OutIntPtrArg:
|
||
return false;
|
||
|
||
default:
|
||
return true;
|
||
}
|
||
}
|