mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 21:46:05 +00:00

Add the types for the RISC-V V extension builtins. These types will be used by the RISC-V V intrinsics which require types of the form <vscale x 1 x i64>(LMUL=1 element size=64) or <vscale x 4 x i32>(LMUL=2 element size=32), etc. The vector_size attribute does not work for us as it doesn't create a scalable vector type. We want these types to be opaque and have no operators defined for them. We want them to be sizeless. This makes them similar to the ARM SVE builtin types. But we will have quite a bit more types. This patch adds around 60. Later patches will add another 230 or so types representing tuples of these types similar to the x2/x3/x4 types in ARM SVE. But with extra complexity that these types are combined with the LMUL concept that is unique to RISCV. For more background see this RFC http://lists.llvm.org/pipermail/llvm-dev/2020-October/145850.html Authored-by: Roger Ferrer Ibanez <roger.ferrer@bsc.es> Co-Authored-by: Hsiangkai Wang <kai.wang@sifive.com> Differential Revision: https://reviews.llvm.org/D92715
1126 lines
36 KiB
C++
1126 lines
36 KiB
C++
//== PrintfFormatString.cpp - Analysis of printf 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Handling of format string in printf and friends. The structure of format
|
|
// strings for fprintf() are described in C99 7.19.6.1.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "FormatStringParsing.h"
|
|
#include "clang/AST/FormatString.h"
|
|
#include "clang/AST/OSLog.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
#include "llvm/Support/Regex.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;
|
|
}
|
|
|
|
if (*I == '{') {
|
|
++I;
|
|
unsigned char PrivacyFlags = 0;
|
|
StringRef MatchedStr;
|
|
|
|
do {
|
|
StringRef Str(I, E - I);
|
|
std::string Match = "^[[:space:]]*"
|
|
"(private|public|sensitive|mask\\.[^[:space:],}]*)"
|
|
"[[:space:]]*(,|})";
|
|
llvm::Regex R(Match);
|
|
SmallVector<StringRef, 2> Matches;
|
|
|
|
if (R.match(Str, &Matches)) {
|
|
MatchedStr = Matches[1];
|
|
I += Matches[0].size();
|
|
|
|
// Set the privacy flag if the privacy annotation in the
|
|
// comma-delimited segment is at least as strict as the privacy
|
|
// annotations in previous comma-delimited segments.
|
|
if (MatchedStr.startswith("mask")) {
|
|
StringRef MaskType = MatchedStr.substr(sizeof("mask.") - 1);
|
|
unsigned Size = MaskType.size();
|
|
if (Warn && (Size == 0 || Size > 8))
|
|
H.handleInvalidMaskType(MaskType);
|
|
FS.setMaskType(MaskType);
|
|
} else if (MatchedStr.equals("sensitive"))
|
|
PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsSensitive;
|
|
else if (PrivacyFlags !=
|
|
clang::analyze_os_log::OSLogBufferItem::IsSensitive &&
|
|
MatchedStr.equals("private"))
|
|
PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsPrivate;
|
|
else if (PrivacyFlags == 0 && MatchedStr.equals("public"))
|
|
PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsPublic;
|
|
} else {
|
|
size_t CommaOrBracePos =
|
|
Str.find_if([](char c) { return c == ',' || c == '}'; });
|
|
|
|
if (CommaOrBracePos == StringRef::npos) {
|
|
// Neither a comma nor the closing brace was found.
|
|
if (Warn)
|
|
H.HandleIncompleteSpecifier(Start, E - Start);
|
|
return true;
|
|
}
|
|
|
|
I += CommaOrBracePos + 1;
|
|
}
|
|
// Continue until the closing brace is found.
|
|
} while (*(I - 1) == ',');
|
|
|
|
// Set the privacy flag.
|
|
switch (PrivacyFlags) {
|
|
case 0:
|
|
break;
|
|
case clang::analyze_os_log::OSLogBufferItem::IsPrivate:
|
|
FS.setIsPrivate(MatchedStr.data());
|
|
break;
|
|
case clang::analyze_os_log::OSLogBufferItem::IsPublic:
|
|
FS.setIsPublic(MatchedStr.data());
|
|
break;
|
|
case clang::analyze_os_log::OSLogBufferItem::IsSensitive:
|
|
FS.setIsSensitive(MatchedStr.data());
|
|
break;
|
|
default:
|
|
llvm_unreachable("Unexpected privacy flag value");
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
if (ParseVectorModifier(H, FS, I, E, LO))
|
|
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':
|
|
// Not handled, but reserved in OpenCL.
|
|
if (!LO.OpenCL)
|
|
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;
|
|
// Apple extension for os_log
|
|
case 'P':
|
|
k = ConversionSpecifier::PArg;
|
|
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;
|
|
break;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
bool clang::analyze_format_string::parseFormatStringHasFormattingSpecifiers(
|
|
const char *Begin, const char *End, const LangOptions &LO,
|
|
const TargetInfo &Target) {
|
|
unsigned ArgIndex = 0;
|
|
// Keep looking for a formatting specifier until we have exhausted the string.
|
|
FormatStringHandler H;
|
|
while (Begin != End) {
|
|
const PrintfSpecifierResult &FSR =
|
|
ParsePrintfSpecifier(H, Begin, End, ArgIndex, LO, Target, false, false);
|
|
if (FSR.shouldStop())
|
|
break;
|
|
if (FSR.hasValue())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Methods on PrintfSpecifier.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
|
|
bool IsObjCLiteral) const {
|
|
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;
|
|
LLVM_FALLTHROUGH;
|
|
default:
|
|
return ArgType::Invalid();
|
|
}
|
|
|
|
if (CS.isIntArg())
|
|
switch (LM.getKind()) {
|
|
case LengthModifier::AsLongDouble:
|
|
// GNU extension.
|
|
return Ctx.LongLongTy;
|
|
case LengthModifier::None:
|
|
case LengthModifier::AsShortLong:
|
|
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:
|
|
return ArgType::makeSizeT(ArgType(Ctx.getSignedSizeType(), "ssize_t"));
|
|
case LengthModifier::AsInt3264:
|
|
return Ctx.getTargetInfo().getTriple().isArch64Bit()
|
|
? ArgType(Ctx.LongLongTy, "__int64")
|
|
: ArgType(Ctx.IntTy, "__int32");
|
|
case LengthModifier::AsPtrDiff:
|
|
return ArgType::makePtrdiffT(
|
|
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:
|
|
case LengthModifier::AsShortLong:
|
|
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::makeSizeT(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:
|
|
return ArgType::makePtrdiffT(
|
|
ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t"));
|
|
case LengthModifier::AsAllocate:
|
|
case LengthModifier::AsMAllocate:
|
|
case LengthModifier::AsWide:
|
|
return ArgType::Invalid();
|
|
}
|
|
|
|
if (CS.isDoubleArg()) {
|
|
if (!VectorNumElts.isInvalid()) {
|
|
switch (LM.getKind()) {
|
|
case LengthModifier::AsShort:
|
|
return Ctx.HalfTy;
|
|
case LengthModifier::AsShortLong:
|
|
return Ctx.FloatTy;
|
|
case LengthModifier::AsLong:
|
|
default:
|
|
return Ctx.DoubleTy;
|
|
}
|
|
}
|
|
|
|
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::PtrTo(ArgType(Ctx.getSignedSizeType(), "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();
|
|
case LengthModifier::AsShortLong:
|
|
llvm_unreachable("only used for OpenCL which doesn not handle nArg");
|
|
}
|
|
}
|
|
|
|
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:
|
|
case ConversionSpecifier::PArg:
|
|
return ArgType::CPointerTy;
|
|
case ConversionSpecifier::ObjCObjArg:
|
|
return ArgType::ObjCPointerTy;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// FIXME: Handle other cases.
|
|
return ArgType();
|
|
}
|
|
|
|
|
|
ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
|
|
bool IsObjCLiteral) const {
|
|
const PrintfConversionSpecifier &CS = getConversionSpecifier();
|
|
|
|
if (!CS.consumesDataArgument())
|
|
return ArgType::Invalid();
|
|
|
|
ArgType ScalarTy = getScalarArgType(Ctx, IsObjCLiteral);
|
|
if (!ScalarTy.isValid() || VectorNumElts.isInvalid())
|
|
return ScalarTy;
|
|
|
|
return ScalarTy.makeVectorType(Ctx, VectorNumElts.getConstantAmount());
|
|
}
|
|
|
|
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();
|
|
|
|
const BuiltinType *BT = QT->getAs<BuiltinType>();
|
|
if (!BT) {
|
|
const VectorType *VT = QT->getAs<VectorType>();
|
|
if (VT) {
|
|
QT = VT->getElementType();
|
|
BT = QT->getAs<BuiltinType>();
|
|
VectorNumElts = OptionalAmount(VT->getNumElements());
|
|
}
|
|
}
|
|
|
|
// We can only work with builtin types.
|
|
if (!BT)
|
|
return false;
|
|
|
|
// Set length modifier
|
|
switch (BT->getKind()) {
|
|
case BuiltinType::Bool:
|
|
case BuiltinType::WChar_U:
|
|
case BuiltinType::WChar_S:
|
|
case BuiltinType::Char8: // FIXME: Treat like 'char'?
|
|
case BuiltinType::Char16:
|
|
case BuiltinType::Char32:
|
|
case BuiltinType::UInt128:
|
|
case BuiltinType::Int128:
|
|
case BuiltinType::Half:
|
|
case BuiltinType::BFloat16:
|
|
case BuiltinType::Float16:
|
|
case BuiltinType::Float128:
|
|
case BuiltinType::ShortAccum:
|
|
case BuiltinType::Accum:
|
|
case BuiltinType::LongAccum:
|
|
case BuiltinType::UShortAccum:
|
|
case BuiltinType::UAccum:
|
|
case BuiltinType::ULongAccum:
|
|
case BuiltinType::ShortFract:
|
|
case BuiltinType::Fract:
|
|
case BuiltinType::LongFract:
|
|
case BuiltinType::UShortFract:
|
|
case BuiltinType::UFract:
|
|
case BuiltinType::ULongFract:
|
|
case BuiltinType::SatShortAccum:
|
|
case BuiltinType::SatAccum:
|
|
case BuiltinType::SatLongAccum:
|
|
case BuiltinType::SatUShortAccum:
|
|
case BuiltinType::SatUAccum:
|
|
case BuiltinType::SatULongAccum:
|
|
case BuiltinType::SatShortFract:
|
|
case BuiltinType::SatFract:
|
|
case BuiltinType::SatLongFract:
|
|
case BuiltinType::SatUShortFract:
|
|
case BuiltinType::SatUFract:
|
|
case BuiltinType::SatULongFract:
|
|
// 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 EXT_OPAQUE_TYPE(ExtType, Id, Ext) \
|
|
case BuiltinType::Id:
|
|
#include "clang/Basic/OpenCLExtensionTypes.def"
|
|
#define SVE_TYPE(Name, Id, SingletonId) \
|
|
case BuiltinType::Id:
|
|
#include "clang/Basic/AArch64SVEACLETypes.def"
|
|
#define PPC_VECTOR_TYPE(Name, Id, Size) \
|
|
case BuiltinType::Id:
|
|
#include "clang/Basic/PPCTypes.def"
|
|
#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
|
|
#include "clang/Basic/RISCVVTypes.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:
|
|
LM.setKind(VectorNumElts.isInvalid() ?
|
|
LengthModifier::None : LengthModifier::AsShortLong);
|
|
break;
|
|
case BuiltinType::Double:
|
|
LM.setKind(VectorNumElts.isInvalid() ?
|
|
LengthModifier::None : LengthModifier::AsLong);
|
|
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(), LangOpt)) {
|
|
// 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);
|
|
|
|
// Vector modifier
|
|
if (!VectorNumElts.isInvalid())
|
|
os << 'v' << VectorNumElts.getConstantAmount();
|
|
|
|
// 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 diouxXaAeEfFgGsP 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:
|
|
case ConversionSpecifier::PArg:
|
|
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;
|
|
}
|
|
}
|