forked from OSchip/llvm-project
Revert "Add support for __builtin_os_log_format[_buffer_size]"
This reverts commit r284990, two opencl test are broken llvm-svn: 285007
This commit is contained in:
parent
6733564e5a
commit
9c39fdceda
|
@ -35,7 +35,7 @@ class OptionalFlag {
|
|||
public:
|
||||
OptionalFlag(const char *Representation)
|
||||
: representation(Representation), flag(false) {}
|
||||
bool isSet() const { return flag; }
|
||||
bool isSet() { return flag; }
|
||||
void set() { flag = true; }
|
||||
void clear() { flag = false; }
|
||||
void setPosition(const char *position) {
|
||||
|
@ -122,13 +122,12 @@ class ConversionSpecifier {
|
|||
public:
|
||||
enum Kind {
|
||||
InvalidSpecifier = 0,
|
||||
// C99 conversion specifiers.
|
||||
// C99 conversion specifiers.
|
||||
cArg,
|
||||
dArg,
|
||||
DArg, // Apple extension
|
||||
iArg,
|
||||
IntArgBeg = dArg,
|
||||
IntArgEnd = iArg,
|
||||
IntArgBeg = dArg, IntArgEnd = iArg,
|
||||
|
||||
oArg,
|
||||
OArg, // Apple extension
|
||||
|
@ -136,8 +135,7 @@ public:
|
|||
UArg, // Apple extension
|
||||
xArg,
|
||||
XArg,
|
||||
UIntArgBeg = oArg,
|
||||
UIntArgEnd = XArg,
|
||||
UIntArgBeg = oArg, UIntArgEnd = XArg,
|
||||
|
||||
fArg,
|
||||
FArg,
|
||||
|
@ -147,8 +145,7 @@ public:
|
|||
GArg,
|
||||
aArg,
|
||||
AArg,
|
||||
DoubleArgBeg = fArg,
|
||||
DoubleArgEnd = AArg,
|
||||
DoubleArgBeg = fArg, DoubleArgEnd = AArg,
|
||||
|
||||
sArg,
|
||||
pArg,
|
||||
|
@ -157,19 +154,13 @@ public:
|
|||
CArg,
|
||||
SArg,
|
||||
|
||||
// Apple extension: P specifies to os_log that the data being pointed to is
|
||||
// to be copied by os_log. The precision indicates the number of bytes to
|
||||
// copy.
|
||||
PArg,
|
||||
|
||||
// ** Printf-specific **
|
||||
|
||||
ZArg, // MS extension
|
||||
|
||||
// Objective-C specific specifiers.
|
||||
ObjCObjArg, // '@'
|
||||
ObjCBeg = ObjCObjArg,
|
||||
ObjCEnd = ObjCObjArg,
|
||||
ObjCObjArg, // '@'
|
||||
ObjCBeg = ObjCObjArg, ObjCEnd = ObjCObjArg,
|
||||
|
||||
// FreeBSD kernel specific specifiers.
|
||||
FreeBSDbArg,
|
||||
|
@ -178,15 +169,13 @@ public:
|
|||
FreeBSDyArg,
|
||||
|
||||
// GlibC specific specifiers.
|
||||
PrintErrno, // 'm'
|
||||
PrintErrno, // 'm'
|
||||
|
||||
PrintfConvBeg = ObjCObjArg,
|
||||
PrintfConvEnd = PrintErrno,
|
||||
PrintfConvBeg = ObjCObjArg, PrintfConvEnd = PrintErrno,
|
||||
|
||||
// ** Scanf-specific **
|
||||
ScanListArg, // '['
|
||||
ScanfConvBeg = ScanListArg,
|
||||
ScanfConvEnd = ScanListArg
|
||||
ScanfConvBeg = ScanListArg, ScanfConvEnd = ScanListArg
|
||||
};
|
||||
|
||||
ConversionSpecifier(bool isPrintf = true)
|
||||
|
@ -448,15 +437,13 @@ class PrintfSpecifier : public analyze_format_string::FormatSpecifier {
|
|||
OptionalFlag HasAlternativeForm; // '#'
|
||||
OptionalFlag HasLeadingZeroes; // '0'
|
||||
OptionalFlag HasObjCTechnicalTerm; // '[tt]'
|
||||
OptionalFlag IsPrivate; // '{private}'
|
||||
OptionalFlag IsPublic; // '{public}'
|
||||
OptionalAmount Precision;
|
||||
public:
|
||||
PrintfSpecifier()
|
||||
: FormatSpecifier(/* isPrintf = */ true), HasThousandsGrouping("'"),
|
||||
IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "),
|
||||
HasAlternativeForm("#"), HasLeadingZeroes("0"),
|
||||
HasObjCTechnicalTerm("tt"), IsPrivate("private"), IsPublic("public") {}
|
||||
PrintfSpecifier() :
|
||||
FormatSpecifier(/* isPrintf = */ true),
|
||||
HasThousandsGrouping("'"), IsLeftJustified("-"), HasPlusPrefix("+"),
|
||||
HasSpacePrefix(" "), HasAlternativeForm("#"), HasLeadingZeroes("0"),
|
||||
HasObjCTechnicalTerm("tt") {}
|
||||
|
||||
static PrintfSpecifier Parse(const char *beg, const char *end);
|
||||
|
||||
|
@ -485,8 +472,6 @@ public:
|
|||
void setHasObjCTechnicalTerm(const char *position) {
|
||||
HasObjCTechnicalTerm.setPosition(position);
|
||||
}
|
||||
void setIsPrivate(const char *position) { IsPrivate.setPosition(position); }
|
||||
void setIsPublic(const char *position) { IsPublic.setPosition(position); }
|
||||
void setUsesPositionalArg() { UsesPositionalArg = true; }
|
||||
|
||||
// Methods for querying the format specifier.
|
||||
|
@ -524,8 +509,6 @@ public:
|
|||
const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; }
|
||||
const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; }
|
||||
const OptionalFlag &hasObjCTechnicalTerm() const { return HasObjCTechnicalTerm; }
|
||||
const OptionalFlag &isPrivate() const { return IsPrivate; }
|
||||
const OptionalFlag &isPublic() const { return IsPublic; }
|
||||
bool usesPositionalArg() const { return UsesPositionalArg; }
|
||||
|
||||
/// Changes the specifier and length according to a QualType, retaining any
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
//= OSLog.h - Analysis of calls to os_log builtins --*- C++ -*-===============//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines APIs for determining the layout of the data buffer for
|
||||
// os_log() and os_trace().
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_OSLOG_H
|
||||
#define LLVM_CLANG_ANALYSIS_ANALYSES_OSLOG_H
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
|
||||
namespace clang {
|
||||
namespace analyze_os_log {
|
||||
|
||||
/// An OSLogBufferItem represents a single item in the data written by a call
|
||||
/// to os_log() or os_trace().
|
||||
class OSLogBufferItem {
|
||||
public:
|
||||
enum Kind {
|
||||
// The item is a scalar (int, float, raw pointer, etc.). No further copying
|
||||
// is required. This is the only kind allowed by os_trace().
|
||||
ScalarKind = 0,
|
||||
|
||||
// The item is a count, which describes the length of the following item to
|
||||
// be copied. A count may only be followed by an item of kind StringKind,
|
||||
// WideStringKind, or PointerKind.
|
||||
CountKind,
|
||||
|
||||
// The item is a pointer to a C string. If preceded by a count 'n',
|
||||
// os_log() will copy at most 'n' bytes from the pointer.
|
||||
StringKind,
|
||||
|
||||
// The item is a pointer to a block of raw data. This item must be preceded
|
||||
// by a count 'n'. os_log() will copy exactly 'n' bytes from the pointer.
|
||||
PointerKind,
|
||||
|
||||
// The item is a pointer to an Objective-C object. os_log() may retain the
|
||||
// object for later processing.
|
||||
ObjCObjKind,
|
||||
|
||||
// The item is a pointer to wide-char string.
|
||||
WideStringKind,
|
||||
|
||||
// The item is corresponding to the '%m' format specifier, no value is
|
||||
// populated in the buffer and the runtime is loading the errno value.
|
||||
ErrnoKind
|
||||
};
|
||||
|
||||
enum {
|
||||
// The item is marked "private" in the format string.
|
||||
IsPrivate = 0x1,
|
||||
|
||||
// The item is marked "public" in the format string.
|
||||
IsPublic = 0x2
|
||||
};
|
||||
|
||||
private:
|
||||
Kind TheKind = ScalarKind;
|
||||
const Expr *TheExpr = nullptr;
|
||||
CharUnits ConstValue;
|
||||
CharUnits Size; // size of the data, not including the header bytes
|
||||
unsigned Flags = 0;
|
||||
|
||||
public:
|
||||
OSLogBufferItem(Kind kind, const Expr *expr, CharUnits size, unsigned flags)
|
||||
: TheKind(kind), TheExpr(expr), Size(size), Flags(flags) {}
|
||||
|
||||
OSLogBufferItem(ASTContext &Ctx, CharUnits value, unsigned flags)
|
||||
: TheKind(CountKind), ConstValue(value),
|
||||
Size(Ctx.getTypeSizeInChars(Ctx.IntTy)), Flags(flags) {}
|
||||
|
||||
unsigned char getDescriptorByte() const {
|
||||
unsigned char result = 0;
|
||||
if (getIsPrivate())
|
||||
result |= IsPrivate;
|
||||
if (getIsPublic())
|
||||
result |= IsPublic;
|
||||
result |= ((unsigned)getKind()) << 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned char getSizeByte() const { return size().getQuantity(); }
|
||||
|
||||
Kind getKind() const { return TheKind; }
|
||||
bool getIsPrivate() const { return (Flags & IsPrivate) != 0; }
|
||||
bool getIsPublic() const { return (Flags & IsPublic) != 0; }
|
||||
|
||||
const Expr *getExpr() const { return TheExpr; }
|
||||
CharUnits getConstValue() const { return ConstValue; }
|
||||
CharUnits size() const { return Size; }
|
||||
};
|
||||
|
||||
class OSLogBufferLayout {
|
||||
public:
|
||||
SmallVector<OSLogBufferItem, 4> Items;
|
||||
|
||||
enum Flags { HasPrivateItems = 1, HasNonScalarItems = 1 << 1 };
|
||||
|
||||
CharUnits size() const {
|
||||
CharUnits result;
|
||||
result += CharUnits::fromQuantity(2); // summary byte, num-args byte
|
||||
for (auto &item : Items) {
|
||||
// descriptor byte, size byte
|
||||
result += item.size() + CharUnits::fromQuantity(2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool hasPrivateItems() const {
|
||||
return llvm::any_of(
|
||||
Items, [](const OSLogBufferItem &Item) { return Item.getIsPrivate(); });
|
||||
}
|
||||
|
||||
bool hasPublicItems() const {
|
||||
return llvm::any_of(
|
||||
Items, [](const OSLogBufferItem &Item) { return Item.getIsPublic(); });
|
||||
}
|
||||
|
||||
bool hasNonScalar() const {
|
||||
return llvm::any_of(Items, [](const OSLogBufferItem &Item) {
|
||||
return Item.getKind() != OSLogBufferItem::ScalarKind;
|
||||
});
|
||||
}
|
||||
|
||||
unsigned char getSummaryByte() const {
|
||||
unsigned char result = 0;
|
||||
if (hasPrivateItems())
|
||||
result |= HasPrivateItems;
|
||||
if (hasNonScalar())
|
||||
result |= HasNonScalarItems;
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned char getNumArgsByte() const { return Items.size(); }
|
||||
};
|
||||
|
||||
// Given a call 'E' to one of the builtins __builtin_os_log_format() or
|
||||
// __builtin_os_log_format_buffer_size(), compute the layout of the buffer that
|
||||
// the call will write into and store it in 'layout'. Returns 'false' if there
|
||||
// was some error encountered while computing the layout, and 'true' otherwise.
|
||||
bool computeOSLogBufferLayout(clang::ASTContext &Ctx, const clang::CallExpr *E,
|
||||
OSLogBufferLayout &layout);
|
||||
|
||||
} // namespace analyze_os_log
|
||||
} // namespace clang
|
||||
#endif
|
|
@ -1384,10 +1384,6 @@ LANGBUILTIN(to_global, "v*v*", "tn", OCLC20_LANG)
|
|||
LANGBUILTIN(to_local, "v*v*", "tn", OCLC20_LANG)
|
||||
LANGBUILTIN(to_private, "v*v*", "tn", OCLC20_LANG)
|
||||
|
||||
// Builtins for os_log/os_trace
|
||||
BUILTIN(__builtin_os_log_format_buffer_size, "zcC*.", "p:0:nut")
|
||||
BUILTIN(__builtin_os_log_format, "v*v*cC*.", "p:0:nt")
|
||||
|
||||
#undef BUILTIN
|
||||
#undef LIBBUILTIN
|
||||
#undef LANGBUILTIN
|
||||
|
|
|
@ -7413,12 +7413,6 @@ def warn_format_non_standard: Warning<
|
|||
def warn_format_non_standard_conversion_spec: Warning<
|
||||
"using length modifier '%0' with conversion specifier '%1' is not supported by ISO C">,
|
||||
InGroup<FormatNonStandard>, DefaultIgnore;
|
||||
def warn_format_invalid_annotation : Warning<
|
||||
"using '%0' format specifier annotation outside of os_log()/os_trace()">,
|
||||
InGroup<Format>;
|
||||
def warn_format_P_no_precision : Warning<
|
||||
"using '%%P' format specifier without precision">,
|
||||
InGroup<Format>;
|
||||
def warn_printf_ignored_flag: Warning<
|
||||
"flag '%0' is ignored when flag '%1' is present">,
|
||||
InGroup<Format>;
|
||||
|
@ -7555,15 +7549,6 @@ def warn_cfstring_truncated : Warning<
|
|||
"belong to the input codeset UTF-8">,
|
||||
InGroup<DiagGroup<"CFString-literal">>;
|
||||
|
||||
// os_log checking
|
||||
// TODO: separate diagnostic for os_trace()
|
||||
def err_os_log_format_not_string_constant : Error<
|
||||
"os_log() format argument is not a string constant">;
|
||||
def err_os_log_argument_too_big : Error<
|
||||
"os_log() argument %d is too big (%d bytes, max %d)">;
|
||||
def warn_os_log_format_narg : Error<
|
||||
"os_log() '%%n' format specifier is not allowed">, DefaultError;
|
||||
|
||||
// Statements.
|
||||
def err_continue_not_in_loop : Error<
|
||||
"'continue' statement not in loop statement">;
|
||||
|
|
|
@ -9679,7 +9679,6 @@ private:
|
|||
VariadicCallType CallType);
|
||||
|
||||
bool CheckObjCString(Expr *Arg);
|
||||
ExprResult CheckOSLogFormatStringArg(Expr *Arg);
|
||||
|
||||
ExprResult CheckBuiltinFunctionCall(FunctionDecl *FDecl,
|
||||
unsigned BuiltinID, CallExpr *TheCall);
|
||||
|
@ -9702,7 +9701,6 @@ private:
|
|||
bool SemaBuiltinVAStartARM(CallExpr *Call);
|
||||
bool SemaBuiltinUnorderedCompare(CallExpr *TheCall);
|
||||
bool SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs);
|
||||
bool SemaBuiltinOSLogFormat(CallExpr *TheCall);
|
||||
|
||||
public:
|
||||
// Used by C++ template instantiation.
|
||||
|
@ -9740,7 +9738,6 @@ public:
|
|||
FST_Kprintf,
|
||||
FST_FreeBSDKPrintf,
|
||||
FST_OSTrace,
|
||||
FST_OSLog,
|
||||
FST_Unknown
|
||||
};
|
||||
static FormatStringType GetFormatStringType(const FormatAttr *Format);
|
||||
|
|
|
@ -16,7 +16,6 @@ add_clang_library(clangAnalysis
|
|||
Dominators.cpp
|
||||
FormatString.cpp
|
||||
LiveVariables.cpp
|
||||
OSLog.cpp
|
||||
ObjCNoReturn.cpp
|
||||
PostOrderCFGView.cpp
|
||||
PrintfFormatString.cpp
|
||||
|
|
|
@ -591,8 +591,6 @@ const char *ConversionSpecifier::toString() const {
|
|||
case cArg: return "c";
|
||||
case sArg: return "s";
|
||||
case pArg: return "p";
|
||||
case PArg:
|
||||
return "P";
|
||||
case nArg: return "n";
|
||||
case PercentArg: return "%";
|
||||
case ScanListArg: return "[";
|
||||
|
@ -868,7 +866,6 @@ bool FormatSpecifier::hasStandardConversionSpecifier(
|
|||
case ConversionSpecifier::ObjCObjArg:
|
||||
case ConversionSpecifier::ScanListArg:
|
||||
case ConversionSpecifier::PercentArg:
|
||||
case ConversionSpecifier::PArg:
|
||||
return true;
|
||||
case ConversionSpecifier::CArg:
|
||||
case ConversionSpecifier::SArg:
|
||||
|
|
|
@ -1,177 +0,0 @@
|
|||
// TODO: header template
|
||||
|
||||
#include "clang/Analysis/Analyses/OSLog.h"
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/ExprObjC.h"
|
||||
#include "clang/Analysis/Analyses/FormatString.h"
|
||||
#include "clang/Basic/Builtins.h"
|
||||
#include "llvm/ADT/SmallBitVector.h"
|
||||
|
||||
using namespace clang;
|
||||
using llvm::APInt;
|
||||
|
||||
using clang::analyze_os_log::OSLogBufferItem;
|
||||
using clang::analyze_os_log::OSLogBufferLayout;
|
||||
|
||||
class OSLogFormatStringHandler
|
||||
: public analyze_format_string::FormatStringHandler {
|
||||
private:
|
||||
struct ArgData {
|
||||
const Expr *E = nullptr;
|
||||
Optional<OSLogBufferItem::Kind> Kind;
|
||||
Optional<unsigned> Size;
|
||||
unsigned char Flags = 0;
|
||||
};
|
||||
SmallVector<ArgData, 4> ArgsData;
|
||||
ArrayRef<const Expr *> Args;
|
||||
|
||||
OSLogBufferItem::Kind
|
||||
getKind(analyze_format_string::ConversionSpecifier::Kind K) {
|
||||
switch (K) {
|
||||
case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s"
|
||||
return OSLogBufferItem::StringKind;
|
||||
case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S"
|
||||
return OSLogBufferItem::WideStringKind;
|
||||
case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
|
||||
return OSLogBufferItem::PointerKind;
|
||||
case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@"
|
||||
return OSLogBufferItem::ObjCObjKind;
|
||||
case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m"
|
||||
return OSLogBufferItem::ErrnoKind;
|
||||
default:
|
||||
return OSLogBufferItem::ScalarKind;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) {
|
||||
ArgsData.reserve(Args.size());
|
||||
}
|
||||
|
||||
virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
|
||||
const char *StartSpecifier,
|
||||
unsigned SpecifierLen) {
|
||||
if (!FS.consumesDataArgument() &&
|
||||
FS.getConversionSpecifier().getKind() !=
|
||||
clang::analyze_format_string::ConversionSpecifier::PrintErrno)
|
||||
return false;
|
||||
|
||||
ArgsData.emplace_back();
|
||||
unsigned ArgIndex = FS.getArgIndex();
|
||||
if (ArgIndex < Args.size())
|
||||
ArgsData.back().E = Args[ArgIndex];
|
||||
|
||||
// First get the Kind
|
||||
ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind());
|
||||
if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind &&
|
||||
!ArgsData.back().E) {
|
||||
// missing argument
|
||||
ArgsData.pop_back();
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (FS.getConversionSpecifier().getKind()) {
|
||||
case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s"
|
||||
case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S"
|
||||
auto &precision = FS.getPrecision();
|
||||
switch (precision.getHowSpecified()) {
|
||||
case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s"
|
||||
break;
|
||||
case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s"
|
||||
ArgsData.back().Size = precision.getConstantAmount();
|
||||
break;
|
||||
case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s"
|
||||
ArgsData.back().Kind = OSLogBufferItem::CountKind;
|
||||
break;
|
||||
case clang::analyze_format_string::OptionalAmount::Invalid:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
|
||||
auto &precision = FS.getPrecision();
|
||||
switch (precision.getHowSpecified()) {
|
||||
case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P"
|
||||
return false; // length must be supplied with pointer format specifier
|
||||
case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P"
|
||||
ArgsData.back().Size = precision.getConstantAmount();
|
||||
break;
|
||||
case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P"
|
||||
ArgsData.back().Kind = OSLogBufferItem::CountKind;
|
||||
break;
|
||||
case clang::analyze_format_string::OptionalAmount::Invalid:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (FS.isPrivate()) {
|
||||
ArgsData.back().Flags |= OSLogBufferItem::IsPrivate;
|
||||
}
|
||||
if (FS.isPublic()) {
|
||||
ArgsData.back().Flags |= OSLogBufferItem::IsPublic;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const {
|
||||
Layout.Items.clear();
|
||||
for (auto &Data : ArgsData) {
|
||||
if (Data.Size)
|
||||
Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size),
|
||||
Data.Flags);
|
||||
if (Data.Kind) {
|
||||
CharUnits Size;
|
||||
if (*Data.Kind == OSLogBufferItem::ErrnoKind)
|
||||
Size = CharUnits::Zero();
|
||||
else
|
||||
Size = Ctx.getTypeSizeInChars(Data.E->getType());
|
||||
Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags);
|
||||
} else {
|
||||
auto Size = Ctx.getTypeSizeInChars(Data.E->getType());
|
||||
Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size,
|
||||
Data.Flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool clang::analyze_os_log::computeOSLogBufferLayout(
|
||||
ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) {
|
||||
ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs());
|
||||
|
||||
const Expr *StringArg;
|
||||
ArrayRef<const Expr *> VarArgs;
|
||||
switch (E->getBuiltinCallee()) {
|
||||
case Builtin::BI__builtin_os_log_format_buffer_size:
|
||||
assert(E->getNumArgs() >= 1 &&
|
||||
"__builtin_os_log_format_buffer_size takes at least 1 argument");
|
||||
StringArg = E->getArg(0);
|
||||
VarArgs = Args.slice(1);
|
||||
break;
|
||||
case Builtin::BI__builtin_os_log_format:
|
||||
assert(E->getNumArgs() >= 2 &&
|
||||
"__builtin_os_log_format takes at least 2 arguments");
|
||||
StringArg = E->getArg(1);
|
||||
VarArgs = Args.slice(2);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout");
|
||||
}
|
||||
|
||||
const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts());
|
||||
assert(Lit && (Lit->isAscii() || Lit->isUTF8()));
|
||||
StringRef Data = Lit->getString();
|
||||
OSLogFormatStringHandler H(VarArgs);
|
||||
ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(),
|
||||
Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false);
|
||||
|
||||
H.computeLayout(Ctx, Layout);
|
||||
return true;
|
||||
}
|
|
@ -119,39 +119,6 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
return true;
|
||||
}
|
||||
|
||||
const char *OSLogVisibilityFlagsStart = nullptr,
|
||||
*OSLogVisibilityFlagsEnd = nullptr;
|
||||
if (*I == '{') {
|
||||
OSLogVisibilityFlagsStart = I++;
|
||||
// Find the end of the modifier.
|
||||
while (I != E && *I != '}') {
|
||||
I++;
|
||||
}
|
||||
if (I == E) {
|
||||
if (Warn)
|
||||
H.HandleIncompleteSpecifier(Start, E - Start);
|
||||
return true;
|
||||
}
|
||||
assert(*I == '}');
|
||||
OSLogVisibilityFlagsEnd = I++;
|
||||
|
||||
// Just see if 'private' or 'public' is the first word. os_log itself will
|
||||
// do any further parsing.
|
||||
const char *P = OSLogVisibilityFlagsStart + 1;
|
||||
while (P < OSLogVisibilityFlagsEnd && isspace(*P))
|
||||
P++;
|
||||
const char *WordStart = P;
|
||||
while (P < OSLogVisibilityFlagsEnd && (isalnum(*P) || *P == '_'))
|
||||
P++;
|
||||
const char *WordEnd = P;
|
||||
StringRef Word(WordStart, WordEnd - WordStart);
|
||||
if (Word == "private") {
|
||||
FS.setIsPrivate(WordStart);
|
||||
} else if (Word == "public") {
|
||||
FS.setIsPublic(WordStart);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for flags (if any).
|
||||
bool hasMore = true;
|
||||
for ( ; I != E; ++I) {
|
||||
|
@ -286,10 +253,6 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
// 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.
|
||||
|
@ -338,7 +301,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
conversionPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PrintfConversionSpecifier CS(conversionPosition, k);
|
||||
FS.setConversionSpecifier(CS);
|
||||
if (CS.consumesDataArgument() && !FS.usesPositionalArg())
|
||||
|
@ -578,7 +541,6 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
|
|||
return Ctx.IntTy;
|
||||
return ArgType(Ctx.WideCharTy, "wchar_t");
|
||||
case ConversionSpecifier::pArg:
|
||||
case ConversionSpecifier::PArg:
|
||||
return ArgType::CPointerTy;
|
||||
case ConversionSpecifier::ObjCObjArg:
|
||||
return ArgType::ObjCPointerTy;
|
||||
|
@ -938,7 +900,7 @@ bool PrintfSpecifier::hasValidPrecision() const {
|
|||
if (Precision.getHowSpecified() == OptionalAmount::NotSpecified)
|
||||
return true;
|
||||
|
||||
// Precision is only valid with the diouxXaAeEfFgGsP conversions
|
||||
// Precision is only valid with the diouxXaAeEfFgGs conversions
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
|
@ -960,7 +922,6 @@ bool PrintfSpecifier::hasValidPrecision() const {
|
|||
case ConversionSpecifier::sArg:
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
case ConversionSpecifier::PArg:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
|
|
@ -11,15 +11,14 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CodeGenFunction.h"
|
||||
#include "CGCXXABI.h"
|
||||
#include "CGObjCRuntime.h"
|
||||
#include "CGOpenCLRuntime.h"
|
||||
#include "CodeGenFunction.h"
|
||||
#include "CodeGenModule.h"
|
||||
#include "TargetInfo.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/Analysis/Analyses/OSLog.h"
|
||||
#include "clang/Basic/TargetBuiltins.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "clang/CodeGen/CGFunctionInfo.h"
|
||||
|
@ -565,18 +564,6 @@ Value *CodeGenFunction::EmitMSVCBuiltinExpr(MSVCIntrin BuiltinID,
|
|||
llvm_unreachable("Incorrect MSVC intrinsic!");
|
||||
}
|
||||
|
||||
namespace {
|
||||
// ARC cleanup for __builtin_os_log_format
|
||||
struct CallObjCArcUse final : EHScopeStack::Cleanup {
|
||||
CallObjCArcUse(llvm::Value *object) : object(object) {}
|
||||
llvm::Value *object;
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
CGF.EmitARCIntrinsicUse(object);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
|
||||
unsigned BuiltinID, const CallExpr *E,
|
||||
ReturnValueSlot ReturnValue) {
|
||||
|
@ -2610,76 +2597,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
|
|||
// Fall through - it's already mapped to the intrinsic by GCCBuiltin.
|
||||
break;
|
||||
}
|
||||
case Builtin::BI__builtin_os_log_format: {
|
||||
assert(E->getNumArgs() >= 2 &&
|
||||
"__builtin_os_log_format takes at least 2 arguments");
|
||||
analyze_os_log::OSLogBufferLayout Layout;
|
||||
analyze_os_log::computeOSLogBufferLayout(CGM.getContext(), E, Layout);
|
||||
Address BufAddr = EmitPointerWithAlignment(E->getArg(0));
|
||||
// Ignore argument 1, the format string. It is not currently used.
|
||||
CharUnits Offset;
|
||||
Builder.CreateStore(
|
||||
Builder.getInt8(Layout.getSummaryByte()),
|
||||
Builder.CreateConstByteGEP(BufAddr, Offset++, "summary"));
|
||||
Builder.CreateStore(
|
||||
Builder.getInt8(Layout.getNumArgsByte()),
|
||||
Builder.CreateConstByteGEP(BufAddr, Offset++, "numArgs"));
|
||||
|
||||
llvm::SmallVector<llvm::Value *, 4> RetainableOperands;
|
||||
for (const auto &Item : Layout.Items) {
|
||||
Builder.CreateStore(
|
||||
Builder.getInt8(Item.getDescriptorByte()),
|
||||
Builder.CreateConstByteGEP(BufAddr, Offset++, "argDescriptor"));
|
||||
Builder.CreateStore(
|
||||
Builder.getInt8(Item.getSizeByte()),
|
||||
Builder.CreateConstByteGEP(BufAddr, Offset++, "argSize"));
|
||||
Address Addr = Builder.CreateConstByteGEP(BufAddr, Offset);
|
||||
if (const Expr *TheExpr = Item.getExpr()) {
|
||||
Addr = Builder.CreateElementBitCast(
|
||||
Addr, ConvertTypeForMem(TheExpr->getType()));
|
||||
// Check if this is a retainable type.
|
||||
if (TheExpr->getType()->isObjCRetainableType()) {
|
||||
assert(getEvaluationKind(TheExpr->getType()) == TEK_Scalar &&
|
||||
"Only scalar can be a ObjC retainable type");
|
||||
llvm::Value *SV = EmitScalarExpr(TheExpr, /*Ignore*/ false);
|
||||
RValue RV = RValue::get(SV);
|
||||
LValue LV = MakeAddrLValue(Addr, TheExpr->getType());
|
||||
EmitStoreThroughLValue(RV, LV);
|
||||
// Check if the object is constant, if not, save it in
|
||||
// RetainableOperands.
|
||||
if (!isa<Constant>(SV))
|
||||
RetainableOperands.push_back(SV);
|
||||
} else {
|
||||
EmitAnyExprToMem(TheExpr, Addr, Qualifiers(), /*isInit*/ true);
|
||||
}
|
||||
} else {
|
||||
Addr = Builder.CreateElementBitCast(Addr, Int32Ty);
|
||||
Builder.CreateStore(
|
||||
Builder.getInt32(Item.getConstValue().getQuantity()), Addr);
|
||||
}
|
||||
Offset += Item.size();
|
||||
}
|
||||
|
||||
// Push a clang.arc.use cleanup for each object in RetainableOperands. The
|
||||
// cleanup will cause the use to appear after the final log call, keeping
|
||||
// the object valid while it’s held in the log buffer. Note that if there’s
|
||||
// a release cleanup on the object, it will already be active; since
|
||||
// cleanups are emitted in reverse order, the use will occur before the
|
||||
// object is released.
|
||||
if (!RetainableOperands.empty() && getLangOpts().ObjCAutoRefCount &&
|
||||
CGM.getCodeGenOpts().OptimizationLevel != 0)
|
||||
for (llvm::Value *object : RetainableOperands)
|
||||
pushFullExprCleanup<CallObjCArcUse>(getARCCleanupKind(), object);
|
||||
|
||||
return RValue::get(BufAddr.getPointer());
|
||||
}
|
||||
|
||||
case Builtin::BI__builtin_os_log_format_buffer_size: {
|
||||
analyze_os_log::OSLogBufferLayout Layout;
|
||||
analyze_os_log::computeOSLogBufferLayout(CGM.getContext(), E, Layout);
|
||||
return RValue::get(ConstantInt::get(ConvertType(E->getType()),
|
||||
Layout.size().getQuantity()));
|
||||
}
|
||||
}
|
||||
|
||||
// If this is an alias for a lib function (e.g. __builtin_sin), emit
|
||||
|
|
|
@ -1065,12 +1065,6 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
|
|||
case Builtin::BIget_kernel_preferred_work_group_size_multiple:
|
||||
if (SemaOpenCLBuiltinKernelWorkGroupSize(*this, TheCall))
|
||||
return ExprError();
|
||||
case Builtin::BI__builtin_os_log_format:
|
||||
case Builtin::BI__builtin_os_log_format_buffer_size:
|
||||
if (SemaBuiltinOSLogFormat(TheCall)) {
|
||||
return ExprError();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Since the target specific builtins for each arch overlap, only check those
|
||||
|
@ -3484,31 +3478,6 @@ bool Sema::CheckObjCString(Expr *Arg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// CheckObjCString - Checks that the format string argument to the os_log()
|
||||
/// and os_trace() functions is correct, and converts it to const char *.
|
||||
ExprResult Sema::CheckOSLogFormatStringArg(Expr *Arg) {
|
||||
Arg = Arg->IgnoreParenCasts();
|
||||
auto *Literal = dyn_cast<StringLiteral>(Arg);
|
||||
if (!Literal) {
|
||||
if (auto *ObjcLiteral = dyn_cast<ObjCStringLiteral>(Arg)) {
|
||||
Literal = ObjcLiteral->getString();
|
||||
}
|
||||
}
|
||||
|
||||
if (!Literal || (!Literal->isAscii() && !Literal->isUTF8())) {
|
||||
return ExprError(
|
||||
Diag(Arg->getLocStart(), diag::err_os_log_format_not_string_constant)
|
||||
<< Arg->getSourceRange());
|
||||
}
|
||||
|
||||
ExprResult Result(Literal);
|
||||
QualType ResultTy = Context.getPointerType(Context.CharTy.withConst());
|
||||
InitializedEntity Entity =
|
||||
InitializedEntity::InitializeParameter(Context, ResultTy, false);
|
||||
Result = PerformCopyInitialization(Entity, SourceLocation(), Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// Check the arguments to '__builtin_va_start' or '__builtin_ms_va_start'
|
||||
/// for validity. Emit an error and return true on failure; return false
|
||||
/// on success.
|
||||
|
@ -3970,86 +3939,6 @@ bool Sema::SemaBuiltinAssumeAligned(CallExpr *TheCall) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) {
|
||||
unsigned BuiltinID =
|
||||
cast<FunctionDecl>(TheCall->getCalleeDecl())->getBuiltinID();
|
||||
bool IsSizeCall = BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size;
|
||||
|
||||
unsigned NumArgs = TheCall->getNumArgs();
|
||||
unsigned NumRequiredArgs = IsSizeCall ? 1 : 2;
|
||||
if (NumArgs < NumRequiredArgs) {
|
||||
return Diag(TheCall->getLocEnd(), diag::err_typecheck_call_too_few_args)
|
||||
<< 0 /* function call */ << NumRequiredArgs << NumArgs
|
||||
<< TheCall->getSourceRange();
|
||||
}
|
||||
if (NumArgs >= NumRequiredArgs + 0x100) {
|
||||
return Diag(TheCall->getLocEnd(),
|
||||
diag::err_typecheck_call_too_many_args_at_most)
|
||||
<< 0 /* function call */ << (NumRequiredArgs + 0xff) << NumArgs
|
||||
<< TheCall->getSourceRange();
|
||||
}
|
||||
unsigned i = 0;
|
||||
|
||||
// For formatting call, check buffer arg.
|
||||
if (!IsSizeCall) {
|
||||
ExprResult Arg(TheCall->getArg(i));
|
||||
InitializedEntity Entity = InitializedEntity::InitializeParameter(
|
||||
Context, Context.VoidPtrTy, false);
|
||||
Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
|
||||
if (Arg.isInvalid())
|
||||
return true;
|
||||
TheCall->setArg(i, Arg.get());
|
||||
i++;
|
||||
}
|
||||
|
||||
// Check string literal arg.
|
||||
unsigned FormatIdx = i;
|
||||
{
|
||||
ExprResult Arg = CheckOSLogFormatStringArg(TheCall->getArg(i));
|
||||
if (Arg.isInvalid())
|
||||
return true;
|
||||
TheCall->setArg(i, Arg.get());
|
||||
i++;
|
||||
}
|
||||
|
||||
// Make sure variadic args are scalar.
|
||||
unsigned FirstDataArg = i;
|
||||
while (i < NumArgs) {
|
||||
ExprResult Arg = DefaultVariadicArgumentPromotion(
|
||||
TheCall->getArg(i), VariadicFunction, nullptr);
|
||||
if (Arg.isInvalid())
|
||||
return true;
|
||||
CharUnits ArgSize = Context.getTypeSizeInChars(Arg.get()->getType());
|
||||
if (ArgSize.getQuantity() >= 0x100) {
|
||||
return Diag(Arg.get()->getLocEnd(), diag::err_os_log_argument_too_big)
|
||||
<< i << (int)ArgSize.getQuantity() << 0xff
|
||||
<< TheCall->getSourceRange();
|
||||
}
|
||||
TheCall->setArg(i, Arg.get());
|
||||
i++;
|
||||
}
|
||||
|
||||
// Check formatting specifiers. NOTE: We're only doing this for the non-size
|
||||
// call to avoid duplicate diagnostics.
|
||||
if (!IsSizeCall) {
|
||||
llvm::SmallBitVector CheckedVarArgs(NumArgs, false);
|
||||
ArrayRef<const Expr *> Args(TheCall->getArgs(), TheCall->getNumArgs());
|
||||
bool Success = CheckFormatArguments(
|
||||
Args, /*HasVAListArg*/ false, FormatIdx, FirstDataArg, FST_OSLog,
|
||||
VariadicFunction, TheCall->getLocStart(), SourceRange(),
|
||||
CheckedVarArgs);
|
||||
if (!Success)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsSizeCall) {
|
||||
TheCall->setType(Context.getSizeType());
|
||||
} else {
|
||||
TheCall->setType(Context.VoidPtrTy);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// SemaBuiltinConstantArg - Handle a check if argument ArgNum of CallExpr
|
||||
/// TheCall is a constant expression.
|
||||
bool Sema::SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
|
||||
|
@ -4680,16 +4569,15 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
|
|||
|
||||
Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) {
|
||||
return llvm::StringSwitch<FormatStringType>(Format->getType()->getName())
|
||||
.Case("scanf", FST_Scanf)
|
||||
.Cases("printf", "printf0", FST_Printf)
|
||||
.Cases("NSString", "CFString", FST_NSString)
|
||||
.Case("strftime", FST_Strftime)
|
||||
.Case("strfmon", FST_Strfmon)
|
||||
.Cases("kprintf", "cmn_err", "vcmn_err", "zcmn_err", FST_Kprintf)
|
||||
.Case("freebsd_kprintf", FST_FreeBSDKPrintf)
|
||||
.Case("os_trace", FST_OSLog)
|
||||
.Case("os_log", FST_OSLog)
|
||||
.Default(FST_Unknown);
|
||||
.Case("scanf", FST_Scanf)
|
||||
.Cases("printf", "printf0", FST_Printf)
|
||||
.Cases("NSString", "CFString", FST_NSString)
|
||||
.Case("strftime", FST_Strftime)
|
||||
.Case("strfmon", FST_Strfmon)
|
||||
.Cases("kprintf", "cmn_err", "vcmn_err", "zcmn_err", FST_Kprintf)
|
||||
.Case("freebsd_kprintf", FST_FreeBSDKPrintf)
|
||||
.Case("os_trace", FST_OSTrace)
|
||||
.Default(FST_Unknown);
|
||||
}
|
||||
|
||||
/// CheckFormatArguments - Check calls to printf and scanf (and similar
|
||||
|
@ -4799,7 +4687,6 @@ protected:
|
|||
Sema &S;
|
||||
const FormatStringLiteral *FExpr;
|
||||
const Expr *OrigFormatExpr;
|
||||
const Sema::FormatStringType FSType;
|
||||
const unsigned FirstDataArg;
|
||||
const unsigned NumDataArgs;
|
||||
const char *Beg; // Start of format string.
|
||||
|
@ -4816,19 +4703,20 @@ protected:
|
|||
|
||||
public:
|
||||
CheckFormatHandler(Sema &s, const FormatStringLiteral *fexpr,
|
||||
const Expr *origFormatExpr,
|
||||
const Sema::FormatStringType type, unsigned firstDataArg,
|
||||
const Expr *origFormatExpr, unsigned firstDataArg,
|
||||
unsigned numDataArgs, const char *beg, bool hasVAListArg,
|
||||
ArrayRef<const Expr *> Args, unsigned formatIdx,
|
||||
bool inFunctionCall, Sema::VariadicCallType callType,
|
||||
ArrayRef<const Expr *> Args,
|
||||
unsigned formatIdx, bool inFunctionCall,
|
||||
Sema::VariadicCallType callType,
|
||||
llvm::SmallBitVector &CheckedVarArgs,
|
||||
UncoveredArgHandler &UncoveredArg)
|
||||
: S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), FSType(type),
|
||||
FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), Beg(beg),
|
||||
HasVAListArg(hasVAListArg), Args(Args), FormatIdx(formatIdx),
|
||||
usesPositionalArgs(false), atFirstArg(true),
|
||||
inFunctionCall(inFunctionCall), CallType(callType),
|
||||
CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg) {
|
||||
: S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr),
|
||||
FirstDataArg(firstDataArg), NumDataArgs(numDataArgs),
|
||||
Beg(beg), HasVAListArg(hasVAListArg),
|
||||
Args(Args), FormatIdx(formatIdx),
|
||||
usesPositionalArgs(false), atFirstArg(true),
|
||||
inFunctionCall(inFunctionCall), CallType(callType),
|
||||
CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg) {
|
||||
CoveredArgs.resize(numDataArgs);
|
||||
CoveredArgs.reset();
|
||||
}
|
||||
|
@ -5251,28 +5139,24 @@ void CheckFormatHandler::EmitFormatDiagnostic(
|
|||
|
||||
namespace {
|
||||
class CheckPrintfHandler : public CheckFormatHandler {
|
||||
bool ObjCContext;
|
||||
|
||||
public:
|
||||
CheckPrintfHandler(Sema &s, const FormatStringLiteral *fexpr,
|
||||
const Expr *origFormatExpr,
|
||||
const Sema::FormatStringType type, unsigned firstDataArg,
|
||||
unsigned numDataArgs, bool isObjC, const char *beg,
|
||||
bool hasVAListArg, ArrayRef<const Expr *> Args,
|
||||
const Expr *origFormatExpr, unsigned firstDataArg,
|
||||
unsigned numDataArgs, bool isObjC,
|
||||
const char *beg, bool hasVAListArg,
|
||||
ArrayRef<const Expr *> Args,
|
||||
unsigned formatIdx, bool inFunctionCall,
|
||||
Sema::VariadicCallType CallType,
|
||||
llvm::SmallBitVector &CheckedVarArgs,
|
||||
UncoveredArgHandler &UncoveredArg)
|
||||
: CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
|
||||
numDataArgs, beg, hasVAListArg, Args, formatIdx,
|
||||
inFunctionCall, CallType, CheckedVarArgs,
|
||||
UncoveredArg) {}
|
||||
|
||||
bool isObjCContext() const { return FSType == Sema::FST_NSString; }
|
||||
|
||||
/// Returns true if '%@' specifiers are allowed in the format string.
|
||||
bool allowsObjCArg() const {
|
||||
return FSType == Sema::FST_NSString || FSType == Sema::FST_OSLog ||
|
||||
FSType == Sema::FST_OSTrace;
|
||||
}
|
||||
: CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
|
||||
numDataArgs, beg, hasVAListArg, Args,
|
||||
formatIdx, inFunctionCall, CallType, CheckedVarArgs,
|
||||
UncoveredArg),
|
||||
ObjCContext(isObjC)
|
||||
{}
|
||||
|
||||
bool HandleInvalidPrintfConversionSpecifier(
|
||||
const analyze_printf::PrintfSpecifier &FS,
|
||||
|
@ -5626,54 +5510,11 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
|
|||
|
||||
// Check for using an Objective-C specific conversion specifier
|
||||
// in a non-ObjC literal.
|
||||
if (!allowsObjCArg() && CS.isObjCArg()) {
|
||||
if (!ObjCContext && CS.isObjCArg()) {
|
||||
return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
|
||||
specifierLen);
|
||||
}
|
||||
|
||||
// %P can only be used with os_log.
|
||||
if (FSType != Sema::FST_OSLog && CS.getKind() == ConversionSpecifier::PArg) {
|
||||
return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
|
||||
specifierLen);
|
||||
}
|
||||
|
||||
// %n is not allowed with os_log.
|
||||
if (FSType == Sema::FST_OSLog && CS.getKind() == ConversionSpecifier::nArg) {
|
||||
EmitFormatDiagnostic(S.PDiag(diag::warn_os_log_format_narg),
|
||||
getLocationOfByte(CS.getStart()),
|
||||
/*IsStringLocation*/ false,
|
||||
getSpecifierRange(startSpecifier, specifierLen));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only scalars are allowed for os_trace.
|
||||
if (FSType == Sema::FST_OSTrace &&
|
||||
(CS.getKind() == ConversionSpecifier::PArg ||
|
||||
CS.getKind() == ConversionSpecifier::sArg ||
|
||||
CS.getKind() == ConversionSpecifier::ObjCObjArg)) {
|
||||
return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
|
||||
specifierLen);
|
||||
}
|
||||
|
||||
// Check for use of public/private annotation outside of os_log().
|
||||
if (FSType != Sema::FST_OSLog) {
|
||||
if (FS.isPublic().isSet()) {
|
||||
EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_annotation)
|
||||
<< "public",
|
||||
getLocationOfByte(FS.isPublic().getPosition()),
|
||||
/*IsStringLocation*/ false,
|
||||
getSpecifierRange(startSpecifier, specifierLen));
|
||||
}
|
||||
if (FS.isPrivate().isSet()) {
|
||||
EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_annotation)
|
||||
<< "private",
|
||||
getLocationOfByte(FS.isPrivate().getPosition()),
|
||||
/*IsStringLocation*/ false,
|
||||
getSpecifierRange(startSpecifier, specifierLen));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for invalid use of field width
|
||||
if (!FS.hasValidFieldWidth()) {
|
||||
HandleInvalidAmount(FS, FS.getFieldWidth(), /* field width */ 0,
|
||||
|
@ -5686,15 +5527,6 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
|
|||
startSpecifier, specifierLen);
|
||||
}
|
||||
|
||||
// Precision is mandatory for %P specifier.
|
||||
if (CS.getKind() == ConversionSpecifier::PArg &&
|
||||
FS.getPrecision().getHowSpecified() == OptionalAmount::NotSpecified) {
|
||||
EmitFormatDiagnostic(S.PDiag(diag::warn_format_P_no_precision),
|
||||
getLocationOfByte(startSpecifier),
|
||||
/*IsStringLocation*/ false,
|
||||
getSpecifierRange(startSpecifier, specifierLen));
|
||||
}
|
||||
|
||||
// Check each flag does not conflict with any other component.
|
||||
if (!FS.hasValidThousandsGroupingPrefix())
|
||||
HandleFlag(FS, FS.hasThousandsGrouping(), startSpecifier, specifierLen);
|
||||
|
@ -5844,7 +5676,8 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
|
|||
using namespace analyze_printf;
|
||||
// Now type check the data expression that matches the
|
||||
// format specifier.
|
||||
const analyze_printf::ArgType &AT = FS.getArgType(S.Context, isObjCContext());
|
||||
const analyze_printf::ArgType &AT = FS.getArgType(S.Context,
|
||||
ObjCContext);
|
||||
if (!AT.isValid())
|
||||
return true;
|
||||
|
||||
|
@ -5899,7 +5732,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
|
|||
// If the argument is an integer of some kind, believe the %C and suggest
|
||||
// a cast instead of changing the conversion specifier.
|
||||
QualType IntendedTy = ExprTy;
|
||||
if (isObjCContext() &&
|
||||
if (ObjCContext &&
|
||||
FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) {
|
||||
if (ExprTy->isIntegralOrUnscopedEnumerationType() &&
|
||||
!ExprTy->isCharType()) {
|
||||
|
@ -5940,8 +5773,8 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
|
|||
|
||||
// We may be able to offer a FixItHint if it is a supported type.
|
||||
PrintfSpecifier fixedFS = FS;
|
||||
bool success =
|
||||
fixedFS.fixType(IntendedTy, S.getLangOpts(), S.Context, isObjCContext());
|
||||
bool success = fixedFS.fixType(IntendedTy, S.getLangOpts(),
|
||||
S.Context, ObjCContext);
|
||||
|
||||
if (success) {
|
||||
// Get the fix string from the fixed format specifier
|
||||
|
@ -6097,18 +5930,19 @@ namespace {
|
|||
class CheckScanfHandler : public CheckFormatHandler {
|
||||
public:
|
||||
CheckScanfHandler(Sema &s, const FormatStringLiteral *fexpr,
|
||||
const Expr *origFormatExpr, Sema::FormatStringType type,
|
||||
unsigned firstDataArg, unsigned numDataArgs,
|
||||
const char *beg, bool hasVAListArg,
|
||||
ArrayRef<const Expr *> Args, unsigned formatIdx,
|
||||
bool inFunctionCall, Sema::VariadicCallType CallType,
|
||||
const Expr *origFormatExpr, unsigned firstDataArg,
|
||||
unsigned numDataArgs, const char *beg, bool hasVAListArg,
|
||||
ArrayRef<const Expr *> Args,
|
||||
unsigned formatIdx, bool inFunctionCall,
|
||||
Sema::VariadicCallType CallType,
|
||||
llvm::SmallBitVector &CheckedVarArgs,
|
||||
UncoveredArgHandler &UncoveredArg)
|
||||
: CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
|
||||
numDataArgs, beg, hasVAListArg, Args, formatIdx,
|
||||
inFunctionCall, CallType, CheckedVarArgs,
|
||||
UncoveredArg) {}
|
||||
|
||||
: CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
|
||||
numDataArgs, beg, hasVAListArg,
|
||||
Args, formatIdx, inFunctionCall, CallType,
|
||||
CheckedVarArgs, UncoveredArg)
|
||||
{}
|
||||
|
||||
bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
|
||||
const char *startSpecifier,
|
||||
unsigned specifierLen) override;
|
||||
|
@ -6318,13 +6152,13 @@ static void CheckFormatString(Sema &S, const FormatStringLiteral *FExpr,
|
|||
}
|
||||
|
||||
if (Type == Sema::FST_Printf || Type == Sema::FST_NSString ||
|
||||
Type == Sema::FST_FreeBSDKPrintf || Type == Sema::FST_OSLog ||
|
||||
Type == Sema::FST_OSTrace) {
|
||||
CheckPrintfHandler H(
|
||||
S, FExpr, OrigFormatExpr, Type, firstDataArg, numDataArgs,
|
||||
(Type == Sema::FST_NSString || Type == Sema::FST_OSTrace), Str,
|
||||
HasVAListArg, Args, format_idx, inFunctionCall, CallType,
|
||||
CheckedVarArgs, UncoveredArg);
|
||||
Type == Sema::FST_FreeBSDKPrintf || Type == Sema::FST_OSTrace) {
|
||||
CheckPrintfHandler H(S, FExpr, OrigFormatExpr, firstDataArg,
|
||||
numDataArgs, (Type == Sema::FST_NSString ||
|
||||
Type == Sema::FST_OSTrace),
|
||||
Str, HasVAListArg, Args, format_idx,
|
||||
inFunctionCall, CallType, CheckedVarArgs,
|
||||
UncoveredArg);
|
||||
|
||||
if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
|
||||
S.getLangOpts(),
|
||||
|
@ -6332,9 +6166,10 @@ static void CheckFormatString(Sema &S, const FormatStringLiteral *FExpr,
|
|||
Type == Sema::FST_FreeBSDKPrintf))
|
||||
H.DoneProcessing();
|
||||
} else if (Type == Sema::FST_Scanf) {
|
||||
CheckScanfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg,
|
||||
numDataArgs, Str, HasVAListArg, Args, format_idx,
|
||||
inFunctionCall, CallType, CheckedVarArgs, UncoveredArg);
|
||||
CheckScanfHandler H(S, FExpr, OrigFormatExpr, firstDataArg, numDataArgs,
|
||||
Str, HasVAListArg, Args, format_idx,
|
||||
inFunctionCall, CallType, CheckedVarArgs,
|
||||
UncoveredArg);
|
||||
|
||||
if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen,
|
||||
S.getLangOpts(),
|
||||
|
|
|
@ -2804,21 +2804,20 @@ enum FormatAttrKind {
|
|||
/// types.
|
||||
static FormatAttrKind getFormatAttrKind(StringRef Format) {
|
||||
return llvm::StringSwitch<FormatAttrKind>(Format)
|
||||
// Check for formats that get handled specially.
|
||||
.Case("NSString", NSStringFormat)
|
||||
.Case("CFString", CFStringFormat)
|
||||
.Case("strftime", StrftimeFormat)
|
||||
// Check for formats that get handled specially.
|
||||
.Case("NSString", NSStringFormat)
|
||||
.Case("CFString", CFStringFormat)
|
||||
.Case("strftime", StrftimeFormat)
|
||||
|
||||
// Otherwise, check for supported formats.
|
||||
.Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat)
|
||||
.Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat)
|
||||
.Case("kprintf", SupportedFormat) // OpenBSD.
|
||||
.Case("freebsd_kprintf", SupportedFormat) // FreeBSD.
|
||||
.Case("os_trace", SupportedFormat)
|
||||
.Case("os_log", SupportedFormat)
|
||||
// Otherwise, check for supported formats.
|
||||
.Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat)
|
||||
.Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat)
|
||||
.Case("kprintf", SupportedFormat) // OpenBSD.
|
||||
.Case("freebsd_kprintf", SupportedFormat) // FreeBSD.
|
||||
.Case("os_trace", SupportedFormat)
|
||||
|
||||
.Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat)
|
||||
.Default(InvalidFormat);
|
||||
.Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat)
|
||||
.Default(InvalidFormat);
|
||||
}
|
||||
|
||||
/// Handle __attribute__((init_priority(priority))) attributes based on
|
||||
|
|
|
@ -355,6 +355,8 @@ void test_float_builtin_ops(float F, double D, long double LD) {
|
|||
|
||||
}
|
||||
|
||||
// __builtin_longjmp isn't supported on all platforms, so only test it on X86.
|
||||
#ifdef __x86_64__
|
||||
// CHECK-LABEL: define void @test_builtin_longjmp
|
||||
void test_builtin_longjmp(void **buffer) {
|
||||
// CHECK: [[BITCAST:%.*]] = bitcast
|
||||
|
@ -362,147 +364,10 @@ void test_builtin_longjmp(void **buffer) {
|
|||
__builtin_longjmp(buffer, 1);
|
||||
// CHECK-NEXT: unreachable
|
||||
}
|
||||
#endif
|
||||
|
||||
// CHECK-LABEL: define i64 @test_builtin_readcyclecounter
|
||||
long long test_builtin_readcyclecounter() {
|
||||
// CHECK: call i64 @llvm.readcyclecounter()
|
||||
return __builtin_readcyclecounter();
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define void @test_builtin_os_log
|
||||
// CHECK: (i8* [[BUF:%.*]], i32 [[I:%.*]], i8* [[DATA:%.*]])
|
||||
void test_builtin_os_log(void *buf, int i, const char *data) {
|
||||
volatile int len;
|
||||
// CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8
|
||||
// CHECK: store i32 [[I]], i32* [[I_ADDR:%.*]], align 4
|
||||
// CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8
|
||||
|
||||
// CHECK: store volatile i32 34
|
||||
len = __builtin_os_log_format_buffer_size("%d %{public}s %{private}.16P", i, data, data);
|
||||
|
||||
// CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]]
|
||||
// CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0
|
||||
// CHECK: store i8 3, i8* [[SUMMARY]]
|
||||
// CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1
|
||||
// CHECK: store i8 4, i8* [[NUM_ARGS]]
|
||||
//
|
||||
// CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2
|
||||
// CHECK: store i8 0, i8* [[ARG1_DESC]]
|
||||
// CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3
|
||||
// CHECK: store i8 4, i8* [[ARG1_SIZE]]
|
||||
// CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4
|
||||
// CHECK: [[ARG1_INT:%.*]] = bitcast i8* [[ARG1]] to i32*
|
||||
// CHECK: [[I2:%.*]] = load i32, i32* [[I_ADDR]]
|
||||
// CHECK: store i32 [[I2]], i32* [[ARG1_INT]]
|
||||
|
||||
// CHECK: [[ARG2_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 8
|
||||
// CHECK: store i8 34, i8* [[ARG2_DESC]]
|
||||
// CHECK: [[ARG2_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 9
|
||||
// CHECK: store i8 8, i8* [[ARG2_SIZE]]
|
||||
// CHECK: [[ARG2:%.*]] = getelementptr i8, i8* [[BUF2]], i64 10
|
||||
// CHECK: [[ARG2_PTR:%.*]] = bitcast i8* [[ARG2]] to i8**
|
||||
// CHECK: [[DATA2:%.*]] = load i8*, i8** [[DATA_ADDR]]
|
||||
// CHECK: store i8* [[DATA2]], i8** [[ARG2_PTR]]
|
||||
|
||||
// CHECK: [[ARG3_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 18
|
||||
// CHECK: store i8 17, i8* [[ARG3_DESC]]
|
||||
// CHECK: [[ARG3_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 19
|
||||
// CHECK: store i8 4, i8* [[ARG3_SIZE]]
|
||||
// CHECK: [[ARG3:%.*]] = getelementptr i8, i8* [[BUF2]], i64 20
|
||||
// CHECK: [[ARG3_INT:%.*]] = bitcast i8* [[ARG3]] to i32*
|
||||
// CHECK: store i32 16, i32* [[ARG3_INT]]
|
||||
|
||||
// CHECK: [[ARG4_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 24
|
||||
// CHECK: store i8 49, i8* [[ARG4_DESC]]
|
||||
// CHECK: [[ARG4_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 25
|
||||
// CHECK: store i8 8, i8* [[ARG4_SIZE]]
|
||||
// CHECK: [[ARG4:%.*]] = getelementptr i8, i8* [[BUF2]], i64 26
|
||||
// CHECK: [[ARG4_PTR:%.*]] = bitcast i8* [[ARG4]] to i8**
|
||||
// CHECK: [[DATA3:%.*]] = load i8*, i8** [[DATA_ADDR]]
|
||||
// CHECK: store i8* [[DATA3]], i8** [[ARG4_PTR]]
|
||||
|
||||
__builtin_os_log_format(buf, "%d %{public}s %{private}.16P", i, data, data);
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define void @test_builtin_os_log_errno
|
||||
// CHECK: (i8* [[BUF:%.*]], i8* [[DATA:%.*]])
|
||||
void test_builtin_os_log_errno(void *buf, const char *data) {
|
||||
volatile int len;
|
||||
// CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8
|
||||
// CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8
|
||||
|
||||
// CHECK: store volatile i32 2
|
||||
len = __builtin_os_log_format_buffer_size("%S");
|
||||
|
||||
// CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]]
|
||||
// CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0
|
||||
// CHECK: store i8 2, i8* [[SUMMARY]]
|
||||
// CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1
|
||||
// CHECK: store i8 1, i8* [[NUM_ARGS]]
|
||||
|
||||
// CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2
|
||||
// CHECK: store i8 96, i8* [[ARG1_DESC]]
|
||||
// CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3
|
||||
// CHECK: store i8 0, i8* [[ARG1_SIZE]]
|
||||
// CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4
|
||||
// CHECK: [[ARG1_INT:%.*]] = bitcast i8* [[ARG1]] to i32*
|
||||
// CHECK: store i32 0, i32* [[ARG1_INT]]
|
||||
|
||||
__builtin_os_log_format(buf, "%m");
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define void @test_builtin_os_log_wide
|
||||
// CHECK: (i8* [[BUF:%.*]], i8* [[DATA:%.*]], i32* [[STR:%.*]])
|
||||
typedef int wchar_t;
|
||||
void test_builtin_os_log_wide(void *buf, const char *data, wchar_t *str) {
|
||||
volatile int len;
|
||||
// CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8
|
||||
// CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8
|
||||
// CHECK: store i32* [[STR]], i32** [[STR_ADDR:%.*]],
|
||||
|
||||
// CHECK: store volatile i32 12
|
||||
len = __builtin_os_log_format_buffer_size("%S", str);
|
||||
|
||||
// CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]]
|
||||
// CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0
|
||||
// CHECK: store i8 2, i8* [[SUMMARY]]
|
||||
// CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1
|
||||
// CHECK: store i8 1, i8* [[NUM_ARGS]]
|
||||
|
||||
// CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2
|
||||
// CHECK: store i8 80, i8* [[ARG1_DESC]]
|
||||
// CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3
|
||||
// CHECK: store i8 8, i8* [[ARG1_SIZE]]
|
||||
// CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4
|
||||
// CHECK: [[ARG1_PTR:%.*]] = bitcast i8* [[ARG1]] to i32**
|
||||
// CHECK: [[STR2:%.*]] = load i32*, i32** [[STR_ADDR]]
|
||||
// CHECK: store i32* [[STR2]], i32** [[ARG1_PTR]]
|
||||
|
||||
__builtin_os_log_format(buf, "%S", str);
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define void @test_builtin_os_log_percent
|
||||
// Check that the %% which does not consume any argument is correctly handled
|
||||
void test_builtin_os_log_percent(void *buf, const char *data) {
|
||||
volatile int len;
|
||||
// CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8
|
||||
// CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8
|
||||
// CHECK: store volatile i32 12
|
||||
len = __builtin_os_log_format_buffer_size("%s %%", data);
|
||||
|
||||
// CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]]
|
||||
// CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0
|
||||
// CHECK: store i8 2, i8* [[SUMMARY]]
|
||||
// CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1
|
||||
// CHECK: store i8 1, i8* [[NUM_ARGS]]
|
||||
//
|
||||
// CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2
|
||||
// CHECK: store i8 32, i8* [[ARG1_DESC]]
|
||||
// CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3
|
||||
// CHECK: store i8 8, i8* [[ARG1_SIZE]]
|
||||
// CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4
|
||||
// CHECK: [[ARG1_PTR:%.*]] = bitcast i8* [[ARG1]] to i8**
|
||||
// CHECK: [[DATA2:%.*]] = load i8*, i8** [[DATA_ADDR]]
|
||||
// CHECK: store i8* [[DATA2]], i8** [[ARG1_PTR]]
|
||||
__builtin_os_log_format(buf, "%s %%", data);
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
// RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-darwin-apple -fobjc-arc -O2 | FileCheck %s
|
||||
|
||||
// Make sure we emit clang.arc.use before calling objc_release as part of the
|
||||
// cleanup. This way we make sure the object will not be released until the
|
||||
// end of the full expression.
|
||||
|
||||
// rdar://problem/24528966
|
||||
|
||||
@class NSString;
|
||||
extern __attribute__((visibility("default"))) NSString *GenString();
|
||||
|
||||
// Behavior of __builtin_os_log differs between platforms, so only test on X86
|
||||
#ifdef __x86_64__
|
||||
// CHECK-LABEL: define i8* @test_builtin_os_log
|
||||
void *test_builtin_os_log(void *buf) {
|
||||
return __builtin_os_log_format(buf, "capabilities: %@", GenString());
|
||||
|
||||
// CHECK: store i8 2, i8*
|
||||
// CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* {{.*}}, i64 1
|
||||
// CHECK: store i8 1, i8* [[NUM_ARGS]]
|
||||
//
|
||||
// CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* {{.*}}, i64 2
|
||||
// CHECK: store i8 64, i8* [[ARG1_DESC]]
|
||||
// CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* {{.*}}, i64 3
|
||||
// CHECK: store i8 8, i8* [[ARG1_SIZE]]
|
||||
// CHECK: [[ARG1:%.*]] = getelementptr i8, i8* {{.*}}, i64 4
|
||||
// CHECK: [[ARG1_CAST:%.*]] = bitcast i8* [[ARG1]] to
|
||||
|
||||
// CHECK: [[STRING:%.*]] = {{.*}} call {{.*}} @GenString()
|
||||
// CHECK: [[STRING_CAST:%.*]] = bitcast {{.*}} [[STRING]] to
|
||||
// CHECK: call {{.*}} @objc_retainAutoreleasedReturnValue(i8* [[STRING_CAST]])
|
||||
// CHECK: store {{.*}} [[STRING]], {{.*}} [[ARG1_CAST]]
|
||||
|
||||
// CHECK: call void (...) @clang.arc.use({{.*}} [[STRING]])
|
||||
// CHECK: call void @objc_release(i8* [[STRING_CAST]])
|
||||
// CHECK: ret i8*
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,62 +0,0 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#define __need_wint_t
|
||||
#include <stddef.h> // For wint_t and wchar_t
|
||||
|
||||
int printf(const char *restrict, ...);
|
||||
|
||||
@interface NSString
|
||||
@end
|
||||
|
||||
void test_os_log_format(const char *pc, int i, void *p, void *buf) {
|
||||
__builtin_os_log_format(buf, "");
|
||||
__builtin_os_log_format(buf, "%d"); // expected-warning {{more '%' conversions than data arguments}}
|
||||
__builtin_os_log_format(buf, "%d", i);
|
||||
__builtin_os_log_format(buf, "%P", p); // expected-warning {{using '%P' format specifier without precision}}
|
||||
__builtin_os_log_format(buf, "%.10P", p);
|
||||
__builtin_os_log_format(buf, "%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
|
||||
__builtin_os_log_format(buf, "%.*P", i, p);
|
||||
__builtin_os_log_format(buf, "%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}}
|
||||
__builtin_os_log_format(buf, "%n"); // expected-error {{os_log() '%n' format specifier is not allowed}}
|
||||
__builtin_os_log_format(buf, pc); // expected-error {{os_log() format argument is not a string constant}}
|
||||
|
||||
printf("%{private}s", pc); // expected-warning {{using 'private' format specifier annotation outside of os_log()/os_trace()}}
|
||||
__builtin_os_log_format(buf, "%{private}s", pc);
|
||||
|
||||
// <rdar://problem/23835805>
|
||||
__builtin_os_log_format_buffer_size("no-args");
|
||||
__builtin_os_log_format(buf, "%s", "hi");
|
||||
|
||||
// <rdar://problem/24828090>
|
||||
wchar_t wc = 'a';
|
||||
__builtin_os_log_format(buf, "%C", wc);
|
||||
printf("%C", wc);
|
||||
wchar_t wcs[] = {'a', 0};
|
||||
__builtin_os_log_format(buf, "%S", wcs);
|
||||
printf("%S", wcs);
|
||||
}
|
||||
|
||||
// Test os_log_format primitive with ObjC string literal format argument.
|
||||
void test_objc(const char *pc, int i, void *p, void *buf, NSString *nss) {
|
||||
__builtin_os_log_format(buf, @"");
|
||||
__builtin_os_log_format(buf, @"%d"); // expected-warning {{more '%' conversions than data arguments}}
|
||||
__builtin_os_log_format(buf, @"%d", i);
|
||||
__builtin_os_log_format(buf, @"%P", p); // expected-warning {{using '%P' format specifier without precision}}
|
||||
__builtin_os_log_format(buf, @"%.10P", p);
|
||||
__builtin_os_log_format(buf, @"%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
|
||||
__builtin_os_log_format(buf, @"%.*P", i, p);
|
||||
__builtin_os_log_format(buf, @"%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}}
|
||||
|
||||
__builtin_os_log_format(buf, @"%{private}s", pc);
|
||||
__builtin_os_log_format(buf, @"%@", nss);
|
||||
}
|
||||
|
||||
// Test the os_log format attribute.
|
||||
void MyOSLog(const char *format, ...) __attribute__((format(os_log, 1, 2)));
|
||||
void test_attribute(void *p) {
|
||||
MyOSLog("%s\n", "Hello");
|
||||
MyOSLog("%d"); // expected-warning {{more '%' conversions than data arguments}}
|
||||
MyOSLog("%P", p); // expected-warning {{using '%P' format specifier without precision}}
|
||||
}
|
Loading…
Reference in New Issue