This patch corresponds to reviews: http://reviews.llvm.org/D15120 http://reviews.llvm.org/D19125 It adds support for the __float128 keyword, literals and target feature to enable it. Based on the latter of the two aforementioned reviews, this feature is enabled on Linux on i386/X86 as well as SystemZ. This is also the second attempt in commiting this feature. The first attempt did not enable it on required platforms which caused failures when compiling type_traits with -std=gnu++11. If you see failures with compiling this header on your platform after this commit, it is likely that your platform needs to have this feature enabled. llvm-svn: 268898
944 lines
30 KiB
C++
944 lines
30 KiB
C++
//== PrintfFormatString.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/FormatString.h"
|
|
#include "FormatStringParsing.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
|
|
using clang::analyze_format_string::ArgType;
|
|
using clang::analyze_format_string::FormatStringHandler;
|
|
using clang::analyze_format_string::LengthModifier;
|
|
using clang::analyze_format_string::OptionalAmount;
|
|
using clang::analyze_format_string::ConversionSpecifier;
|
|
using clang::analyze_printf::PrintfSpecifier;
|
|
|
|
using namespace clang;
|
|
|
|
typedef clang::analyze_format_string::SpecifierResult<PrintfSpecifier>
|
|
PrintfSpecifierResult;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Methods for parsing format strings.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
using analyze_format_string::ParseNonPositionAmount;
|
|
|
|
static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &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_format_string::PrecisionPos);
|
|
if (Amt.isInvalid())
|
|
return true;
|
|
FS.setPrecision(Amt);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS,
|
|
const char *FlagBeg, const char *E, bool Warn) {
|
|
StringRef Flag(FlagBeg, E - FlagBeg);
|
|
// Currently there is only one flag.
|
|
if (Flag == "tt") {
|
|
FS.setHasObjCTechnicalTerm(FlagBeg);
|
|
return false;
|
|
}
|
|
// Handle either the case of no flag or an invalid flag.
|
|
if (Warn) {
|
|
if (Flag == "")
|
|
H.HandleEmptyObjCModifierFlag(FlagBeg, E - FlagBeg);
|
|
else
|
|
H.HandleInvalidObjCModifierFlag(FlagBeg, E - FlagBeg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|
const char *&Beg,
|
|
const char *E,
|
|
unsigned &argIndex,
|
|
const LangOptions &LO,
|
|
const TargetInfo &Target,
|
|
bool Warn,
|
|
bool isFreeBSDKPrintf) {
|
|
|
|
using namespace clang::analyze_format_string;
|
|
using namespace clang::analyze_printf;
|
|
|
|
const char *I = Beg;
|
|
const char *Start = nullptr;
|
|
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?
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
|
|
PrintfSpecifier FS;
|
|
if (ParseArgPosition(H, FS, Start, I, E))
|
|
return true;
|
|
|
|
if (I == E) {
|
|
// No more characters left?
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
|
|
// Look for flags (if any).
|
|
bool hasMore = true;
|
|
for ( ; I != E; ++I) {
|
|
switch (*I) {
|
|
default: hasMore = false; break;
|
|
case '\'':
|
|
// FIXME: POSIX specific. Always accept?
|
|
FS.setHasThousandsGrouping(I);
|
|
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?
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
|
|
// Look for the field width (if any).
|
|
if (ParseFieldWidth(H, FS, Start, I, E,
|
|
FS.usesPositionalArg() ? nullptr : &argIndex))
|
|
return true;
|
|
|
|
if (I == E) {
|
|
// No more characters left?
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
|
|
// Look for the precision (if any).
|
|
if (*I == '.') {
|
|
++I;
|
|
if (I == E) {
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
|
|
if (ParsePrecision(H, FS, Start, I, E,
|
|
FS.usesPositionalArg() ? nullptr : &argIndex))
|
|
return true;
|
|
|
|
if (I == E) {
|
|
// No more characters left?
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Look for the length modifier.
|
|
if (ParseLengthModifier(FS, I, E, LO) && I == E) {
|
|
// No more characters left?
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
|
|
// Look for the Objective-C modifier flags, if any.
|
|
// We parse these here, even if they don't apply to
|
|
// the conversion specifier, and then emit an error
|
|
// later if the conversion specifier isn't '@'. This
|
|
// enables better recovery, and we don't know if
|
|
// these flags are applicable until later.
|
|
const char *ObjCModifierFlagsStart = nullptr,
|
|
*ObjCModifierFlagsEnd = nullptr;
|
|
if (*I == '[') {
|
|
ObjCModifierFlagsStart = I;
|
|
++I;
|
|
auto flagStart = I;
|
|
for (;; ++I) {
|
|
ObjCModifierFlagsEnd = I;
|
|
if (I == E) {
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
// Did we find the closing ']'?
|
|
if (*I == ']') {
|
|
if (ParseObjCFlags(H, FS, flagStart, I, Warn))
|
|
return true;
|
|
++I;
|
|
break;
|
|
}
|
|
// There are no separators defined yet for multiple
|
|
// Objective-C modifier flags. When those are
|
|
// defined, this is the place to check.
|
|
}
|
|
}
|
|
|
|
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::cArg; 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::nArg; break;
|
|
case 'o': k = ConversionSpecifier::oArg; break;
|
|
case 'p': k = ConversionSpecifier::pArg; break;
|
|
case 's': k = ConversionSpecifier::sArg; break;
|
|
case 'u': k = ConversionSpecifier::uArg; break;
|
|
case 'x': k = ConversionSpecifier::xArg; break;
|
|
// POSIX specific.
|
|
case 'C': k = ConversionSpecifier::CArg; break;
|
|
case 'S': k = ConversionSpecifier::SArg; break;
|
|
// Objective-C.
|
|
case '@': k = ConversionSpecifier::ObjCObjArg; break;
|
|
// Glibc specific.
|
|
case 'm': k = ConversionSpecifier::PrintErrno; break;
|
|
// FreeBSD kernel specific.
|
|
case 'b':
|
|
if (isFreeBSDKPrintf)
|
|
k = ConversionSpecifier::FreeBSDbArg; // int followed by char *
|
|
break;
|
|
case 'r':
|
|
if (isFreeBSDKPrintf)
|
|
k = ConversionSpecifier::FreeBSDrArg; // int
|
|
break;
|
|
case 'y':
|
|
if (isFreeBSDKPrintf)
|
|
k = ConversionSpecifier::FreeBSDyArg; // int
|
|
break;
|
|
// Apple-specific.
|
|
case 'D':
|
|
if (isFreeBSDKPrintf)
|
|
k = ConversionSpecifier::FreeBSDDArg; // void * followed by char *
|
|
else if (Target.getTriple().isOSDarwin())
|
|
k = ConversionSpecifier::DArg;
|
|
break;
|
|
case 'O':
|
|
if (Target.getTriple().isOSDarwin())
|
|
k = ConversionSpecifier::OArg;
|
|
break;
|
|
case 'U':
|
|
if (Target.getTriple().isOSDarwin())
|
|
k = ConversionSpecifier::UArg;
|
|
break;
|
|
// MS specific.
|
|
case 'Z':
|
|
if (Target.getTriple().isOSMSVCRT())
|
|
k = ConversionSpecifier::ZArg;
|
|
}
|
|
|
|
// Check to see if we used the Objective-C modifier flags with
|
|
// a conversion specifier other than '@'.
|
|
if (k != ConversionSpecifier::ObjCObjArg &&
|
|
k != ConversionSpecifier::InvalidSpecifier &&
|
|
ObjCModifierFlagsStart) {
|
|
H.HandleObjCFlagsWithNonObjCConversion(ObjCModifierFlagsStart,
|
|
ObjCModifierFlagsEnd + 1,
|
|
conversionPosition);
|
|
return true;
|
|
}
|
|
|
|
PrintfConversionSpecifier CS(conversionPosition, k);
|
|
FS.setConversionSpecifier(CS);
|
|
if (CS.consumesDataArgument() && !FS.usesPositionalArg())
|
|
FS.setArgIndex(argIndex++);
|
|
// FreeBSD kernel specific.
|
|
if (k == ConversionSpecifier::FreeBSDbArg ||
|
|
k == ConversionSpecifier::FreeBSDDArg)
|
|
argIndex++;
|
|
|
|
if (k == ConversionSpecifier::InvalidSpecifier) {
|
|
unsigned Len = I - Start;
|
|
if (ParseUTF8InvalidSpecifier(Start, E, Len)) {
|
|
CS.setEndScanList(Start + Len);
|
|
FS.setConversionSpecifier(CS);
|
|
}
|
|
// Assume the conversion takes one argument.
|
|
return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, Len);
|
|
}
|
|
return PrintfSpecifierResult(Start, FS);
|
|
}
|
|
|
|
bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H,
|
|
const char *I,
|
|
const char *E,
|
|
const LangOptions &LO,
|
|
const TargetInfo &Target,
|
|
bool isFreeBSDKPrintf) {
|
|
|
|
unsigned argIndex = 0;
|
|
|
|
// Keep looking for a format specifier until we have exhausted the string.
|
|
while (I != E) {
|
|
const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
|
|
LO, Target, true,
|
|
isFreeBSDKPrintf);
|
|
// 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.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(),
|
|
I - FSR.getStart()))
|
|
return true;
|
|
}
|
|
assert(I == E && "Format string not exhausted");
|
|
return false;
|
|
}
|
|
|
|
bool clang::analyze_format_string::ParseFormatStringHasSArg(const char *I,
|
|
const char *E,
|
|
const LangOptions &LO,
|
|
const TargetInfo &Target) {
|
|
|
|
unsigned argIndex = 0;
|
|
|
|
// Keep looking for a %s format specifier until we have exhausted the string.
|
|
FormatStringHandler H;
|
|
while (I != E) {
|
|
const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
|
|
LO, Target, false,
|
|
false);
|
|
// 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 false;
|
|
// Did we exhaust the string or encounter an error that
|
|
// we can recover from?
|
|
if (!FSR.hasValue())
|
|
continue;
|
|
const analyze_printf::PrintfSpecifier &FS = FSR.getValue();
|
|
// Return true if this a %s format specifier.
|
|
if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::Kind::sArg)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Methods on PrintfSpecifier.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
|
|
bool IsObjCLiteral) const {
|
|
const PrintfConversionSpecifier &CS = getConversionSpecifier();
|
|
|
|
if (!CS.consumesDataArgument())
|
|
return ArgType::Invalid();
|
|
|
|
if (CS.getKind() == ConversionSpecifier::cArg)
|
|
switch (LM.getKind()) {
|
|
case LengthModifier::None:
|
|
return Ctx.IntTy;
|
|
case LengthModifier::AsLong:
|
|
case LengthModifier::AsWide:
|
|
return ArgType(ArgType::WIntTy, "wint_t");
|
|
case LengthModifier::AsShort:
|
|
if (Ctx.getTargetInfo().getTriple().isOSMSVCRT())
|
|
return Ctx.IntTy;
|
|
default:
|
|
return ArgType::Invalid();
|
|
}
|
|
|
|
if (CS.isIntArg())
|
|
switch (LM.getKind()) {
|
|
case LengthModifier::AsLongDouble:
|
|
// GNU extension.
|
|
return Ctx.LongLongTy;
|
|
case LengthModifier::None:
|
|
return Ctx.IntTy;
|
|
case LengthModifier::AsInt32:
|
|
return ArgType(Ctx.IntTy, "__int32");
|
|
case LengthModifier::AsChar: return ArgType::AnyCharTy;
|
|
case LengthModifier::AsShort: return Ctx.ShortTy;
|
|
case LengthModifier::AsLong: return Ctx.LongTy;
|
|
case LengthModifier::AsLongLong:
|
|
case LengthModifier::AsQuad:
|
|
return Ctx.LongLongTy;
|
|
case LengthModifier::AsInt64:
|
|
return ArgType(Ctx.LongLongTy, "__int64");
|
|
case LengthModifier::AsIntMax:
|
|
return ArgType(Ctx.getIntMaxType(), "intmax_t");
|
|
case LengthModifier::AsSizeT:
|
|
// FIXME: How to get the corresponding signed version of size_t?
|
|
return ArgType();
|
|
case LengthModifier::AsInt3264:
|
|
return Ctx.getTargetInfo().getTriple().isArch64Bit()
|
|
? ArgType(Ctx.LongLongTy, "__int64")
|
|
: ArgType(Ctx.IntTy, "__int32");
|
|
case LengthModifier::AsPtrDiff:
|
|
return ArgType(Ctx.getPointerDiffType(), "ptrdiff_t");
|
|
case LengthModifier::AsAllocate:
|
|
case LengthModifier::AsMAllocate:
|
|
case LengthModifier::AsWide:
|
|
return ArgType::Invalid();
|
|
}
|
|
|
|
if (CS.isUIntArg())
|
|
switch (LM.getKind()) {
|
|
case LengthModifier::AsLongDouble:
|
|
// GNU extension.
|
|
return Ctx.UnsignedLongLongTy;
|
|
case LengthModifier::None:
|
|
return Ctx.UnsignedIntTy;
|
|
case LengthModifier::AsInt32:
|
|
return ArgType(Ctx.UnsignedIntTy, "unsigned __int32");
|
|
case LengthModifier::AsChar: return Ctx.UnsignedCharTy;
|
|
case LengthModifier::AsShort: return Ctx.UnsignedShortTy;
|
|
case LengthModifier::AsLong: return Ctx.UnsignedLongTy;
|
|
case LengthModifier::AsLongLong:
|
|
case LengthModifier::AsQuad:
|
|
return Ctx.UnsignedLongLongTy;
|
|
case LengthModifier::AsInt64:
|
|
return ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64");
|
|
case LengthModifier::AsIntMax:
|
|
return ArgType(Ctx.getUIntMaxType(), "uintmax_t");
|
|
case LengthModifier::AsSizeT:
|
|
return ArgType(Ctx.getSizeType(), "size_t");
|
|
case LengthModifier::AsInt3264:
|
|
return Ctx.getTargetInfo().getTriple().isArch64Bit()
|
|
? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64")
|
|
: ArgType(Ctx.UnsignedIntTy, "unsigned __int32");
|
|
case LengthModifier::AsPtrDiff:
|
|
// FIXME: How to get the corresponding unsigned
|
|
// version of ptrdiff_t?
|
|
return ArgType();
|
|
case LengthModifier::AsAllocate:
|
|
case LengthModifier::AsMAllocate:
|
|
case LengthModifier::AsWide:
|
|
return ArgType::Invalid();
|
|
}
|
|
|
|
if (CS.isDoubleArg()) {
|
|
if (LM.getKind() == LengthModifier::AsLongDouble)
|
|
return Ctx.LongDoubleTy;
|
|
return Ctx.DoubleTy;
|
|
}
|
|
|
|
if (CS.getKind() == ConversionSpecifier::nArg) {
|
|
switch (LM.getKind()) {
|
|
case LengthModifier::None:
|
|
return ArgType::PtrTo(Ctx.IntTy);
|
|
case LengthModifier::AsChar:
|
|
return ArgType::PtrTo(Ctx.SignedCharTy);
|
|
case LengthModifier::AsShort:
|
|
return ArgType::PtrTo(Ctx.ShortTy);
|
|
case LengthModifier::AsLong:
|
|
return ArgType::PtrTo(Ctx.LongTy);
|
|
case LengthModifier::AsLongLong:
|
|
case LengthModifier::AsQuad:
|
|
return ArgType::PtrTo(Ctx.LongLongTy);
|
|
case LengthModifier::AsIntMax:
|
|
return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t"));
|
|
case LengthModifier::AsSizeT:
|
|
return ArgType(); // FIXME: ssize_t
|
|
case LengthModifier::AsPtrDiff:
|
|
return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
|
|
case LengthModifier::AsLongDouble:
|
|
return ArgType(); // FIXME: Is this a known extension?
|
|
case LengthModifier::AsAllocate:
|
|
case LengthModifier::AsMAllocate:
|
|
case LengthModifier::AsInt32:
|
|
case LengthModifier::AsInt3264:
|
|
case LengthModifier::AsInt64:
|
|
case LengthModifier::AsWide:
|
|
return ArgType::Invalid();
|
|
}
|
|
}
|
|
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::sArg:
|
|
if (LM.getKind() == LengthModifier::AsWideChar) {
|
|
if (IsObjCLiteral)
|
|
return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
|
|
"const unichar *");
|
|
return ArgType(ArgType::WCStrTy, "wchar_t *");
|
|
}
|
|
if (LM.getKind() == LengthModifier::AsWide)
|
|
return ArgType(ArgType::WCStrTy, "wchar_t *");
|
|
return ArgType::CStrTy;
|
|
case ConversionSpecifier::SArg:
|
|
if (IsObjCLiteral)
|
|
return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
|
|
"const unichar *");
|
|
if (Ctx.getTargetInfo().getTriple().isOSMSVCRT() &&
|
|
LM.getKind() == LengthModifier::AsShort)
|
|
return ArgType::CStrTy;
|
|
return ArgType(ArgType::WCStrTy, "wchar_t *");
|
|
case ConversionSpecifier::CArg:
|
|
if (IsObjCLiteral)
|
|
return ArgType(Ctx.UnsignedShortTy, "unichar");
|
|
if (Ctx.getTargetInfo().getTriple().isOSMSVCRT() &&
|
|
LM.getKind() == LengthModifier::AsShort)
|
|
return Ctx.IntTy;
|
|
return ArgType(Ctx.WideCharTy, "wchar_t");
|
|
case ConversionSpecifier::pArg:
|
|
return ArgType::CPointerTy;
|
|
case ConversionSpecifier::ObjCObjArg:
|
|
return ArgType::ObjCPointerTy;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// FIXME: Handle other cases.
|
|
return ArgType();
|
|
}
|
|
|
|
bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
|
|
ASTContext &Ctx, bool IsObjCLiteral) {
|
|
// %n is different from other conversion specifiers; don't try to fix it.
|
|
if (CS.getKind() == ConversionSpecifier::nArg)
|
|
return false;
|
|
|
|
// Handle Objective-C objects first. Note that while the '%@' specifier will
|
|
// not warn for structure pointer or void pointer arguments (because that's
|
|
// how CoreFoundation objects are implemented), we only show a fixit for '%@'
|
|
// if we know it's an object (block, id, class, or __attribute__((NSObject))).
|
|
if (QT->isObjCRetainableType()) {
|
|
if (!IsObjCLiteral)
|
|
return false;
|
|
|
|
CS.setKind(ConversionSpecifier::ObjCObjArg);
|
|
|
|
// Disable irrelevant flags
|
|
HasThousandsGrouping = false;
|
|
HasPlusPrefix = false;
|
|
HasSpacePrefix = false;
|
|
HasAlternativeForm = false;
|
|
HasLeadingZeroes = false;
|
|
Precision.setHowSpecified(OptionalAmount::NotSpecified);
|
|
LM.setKind(LengthModifier::None);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Handle strings next (char *, wchar_t *)
|
|
if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) {
|
|
CS.setKind(ConversionSpecifier::sArg);
|
|
|
|
// Disable irrelevant flags
|
|
HasAlternativeForm = 0;
|
|
HasLeadingZeroes = 0;
|
|
|
|
// Set the long length modifier for wide characters
|
|
if (QT->getPointeeType()->isWideCharType())
|
|
LM.setKind(LengthModifier::AsWideChar);
|
|
else
|
|
LM.setKind(LengthModifier::None);
|
|
|
|
return true;
|
|
}
|
|
|
|
// If it's an enum, get its underlying type.
|
|
if (const EnumType *ETy = QT->getAs<EnumType>())
|
|
QT = ETy->getDecl()->getIntegerType();
|
|
|
|
// We can only work with builtin types.
|
|
const BuiltinType *BT = QT->getAs<BuiltinType>();
|
|
if (!BT)
|
|
return false;
|
|
|
|
// Set length modifier
|
|
switch (BT->getKind()) {
|
|
case BuiltinType::Bool:
|
|
case BuiltinType::WChar_U:
|
|
case BuiltinType::WChar_S:
|
|
case BuiltinType::Char16:
|
|
case BuiltinType::Char32:
|
|
case BuiltinType::UInt128:
|
|
case BuiltinType::Int128:
|
|
case BuiltinType::Half:
|
|
case BuiltinType::Float128:
|
|
// Various types which are non-trivial to correct.
|
|
return false;
|
|
|
|
#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
|
|
case BuiltinType::Id:
|
|
#include "clang/Basic/OpenCLImageTypes.def"
|
|
#define SIGNED_TYPE(Id, SingletonId)
|
|
#define UNSIGNED_TYPE(Id, SingletonId)
|
|
#define FLOATING_TYPE(Id, SingletonId)
|
|
#define BUILTIN_TYPE(Id, SingletonId) \
|
|
case BuiltinType::Id:
|
|
#include "clang/AST/BuiltinTypes.def"
|
|
// Misc other stuff which doesn't make sense here.
|
|
return false;
|
|
|
|
case BuiltinType::UInt:
|
|
case BuiltinType::Int:
|
|
case BuiltinType::Float:
|
|
case BuiltinType::Double:
|
|
LM.setKind(LengthModifier::None);
|
|
break;
|
|
|
|
case BuiltinType::Char_U:
|
|
case BuiltinType::UChar:
|
|
case BuiltinType::Char_S:
|
|
case BuiltinType::SChar:
|
|
LM.setKind(LengthModifier::AsChar);
|
|
break;
|
|
|
|
case BuiltinType::Short:
|
|
case BuiltinType::UShort:
|
|
LM.setKind(LengthModifier::AsShort);
|
|
break;
|
|
|
|
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;
|
|
}
|
|
|
|
// Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
|
|
if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus11))
|
|
namedTypeToLengthModifier(QT, LM);
|
|
|
|
// If fixing the length modifier was enough, we might be done.
|
|
if (hasValidLengthModifier(Ctx.getTargetInfo())) {
|
|
// If we're going to offer a fix anyway, make sure the sign matches.
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::uArg:
|
|
case ConversionSpecifier::UArg:
|
|
if (QT->isSignedIntegerType())
|
|
CS.setKind(clang::analyze_format_string::ConversionSpecifier::dArg);
|
|
break;
|
|
case ConversionSpecifier::dArg:
|
|
case ConversionSpecifier::DArg:
|
|
case ConversionSpecifier::iArg:
|
|
if (QT->isUnsignedIntegerType() && !HasPlusPrefix)
|
|
CS.setKind(clang::analyze_format_string::ConversionSpecifier::uArg);
|
|
break;
|
|
default:
|
|
// Other specifiers do not have signed/unsigned variants.
|
|
break;
|
|
}
|
|
|
|
const analyze_printf::ArgType &ATR = getArgType(Ctx, IsObjCLiteral);
|
|
if (ATR.isValid() && ATR.matchesType(Ctx, QT))
|
|
return true;
|
|
}
|
|
|
|
// Set conversion specifier and disable any flags which do not apply to it.
|
|
// Let typedefs to char fall through to int, as %c is silly for uint8_t.
|
|
if (!isa<TypedefType>(QT) && QT->isCharType()) {
|
|
CS.setKind(ConversionSpecifier::cArg);
|
|
LM.setKind(LengthModifier::None);
|
|
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->isSignedIntegerType()) {
|
|
CS.setKind(ConversionSpecifier::dArg);
|
|
HasAlternativeForm = 0;
|
|
}
|
|
else if (QT->isUnsignedIntegerType()) {
|
|
CS.setKind(ConversionSpecifier::uArg);
|
|
HasAlternativeForm = 0;
|
|
HasPlusPrefix = 0;
|
|
} else {
|
|
llvm_unreachable("Unexpected type");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PrintfSpecifier::toString(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) 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 PrintfSpecifier::hasValidPlusPrefix() const {
|
|
if (!HasPlusPrefix)
|
|
return true;
|
|
|
|
// The plus prefix only makes sense for signed conversions
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::dArg:
|
|
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:
|
|
case ConversionSpecifier::FreeBSDrArg:
|
|
case ConversionSpecifier::FreeBSDyArg:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool PrintfSpecifier::hasValidAlternativeForm() const {
|
|
if (!HasAlternativeForm)
|
|
return true;
|
|
|
|
// Alternate form flag only valid with the oxXaAeEfFgG conversions
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::oArg:
|
|
case ConversionSpecifier::OArg:
|
|
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::FreeBSDrArg:
|
|
case ConversionSpecifier::FreeBSDyArg:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool PrintfSpecifier::hasValidLeadingZeros() const {
|
|
if (!HasLeadingZeroes)
|
|
return true;
|
|
|
|
// Leading zeroes flag only valid with the diouxXaAeEfFgG conversions
|
|
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::eArg:
|
|
case ConversionSpecifier::EArg:
|
|
case ConversionSpecifier::fArg:
|
|
case ConversionSpecifier::FArg:
|
|
case ConversionSpecifier::gArg:
|
|
case ConversionSpecifier::GArg:
|
|
case ConversionSpecifier::FreeBSDrArg:
|
|
case ConversionSpecifier::FreeBSDyArg:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool PrintfSpecifier::hasValidSpacePrefix() const {
|
|
if (!HasSpacePrefix)
|
|
return true;
|
|
|
|
// The space prefix only makes sense for signed conversions
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::dArg:
|
|
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:
|
|
case ConversionSpecifier::FreeBSDrArg:
|
|
case ConversionSpecifier::FreeBSDyArg:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool PrintfSpecifier::hasValidLeftJustified() const {
|
|
if (!IsLeftJustified)
|
|
return true;
|
|
|
|
// The left justified flag is valid for all conversions except n
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::nArg:
|
|
return false;
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool PrintfSpecifier::hasValidThousandsGroupingPrefix() const {
|
|
if (!HasThousandsGrouping)
|
|
return true;
|
|
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::dArg:
|
|
case ConversionSpecifier::DArg:
|
|
case ConversionSpecifier::iArg:
|
|
case ConversionSpecifier::uArg:
|
|
case ConversionSpecifier::UArg:
|
|
case ConversionSpecifier::fArg:
|
|
case ConversionSpecifier::FArg:
|
|
case ConversionSpecifier::gArg:
|
|
case ConversionSpecifier::GArg:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool PrintfSpecifier::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::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::eArg:
|
|
case ConversionSpecifier::EArg:
|
|
case ConversionSpecifier::fArg:
|
|
case ConversionSpecifier::FArg:
|
|
case ConversionSpecifier::gArg:
|
|
case ConversionSpecifier::GArg:
|
|
case ConversionSpecifier::sArg:
|
|
case ConversionSpecifier::FreeBSDrArg:
|
|
case ConversionSpecifier::FreeBSDyArg:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
bool PrintfSpecifier::hasValidFieldWidth() const {
|
|
if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified)
|
|
return true;
|
|
|
|
// The field width is valid for all conversions except n
|
|
switch (CS.getKind()) {
|
|
case ConversionSpecifier::nArg:
|
|
return false;
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
}
|