forked from OSchip/llvm-project
[Analyzer] Synthesize function body for std::call_once
Differential Revision: https://reviews.llvm.org/D37840 llvm-svn: 314571
This commit is contained in:
parent
ac63d63543
commit
657a5896b1
|
@ -14,11 +14,18 @@
|
|||
|
||||
#include "BodyFarm.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/CXXInheritance.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/ExprObjC.h"
|
||||
#include "clang/AST/NestedNameSpecifier.h"
|
||||
#include "clang/Analysis/CodeInjector.h"
|
||||
#include "clang/Basic/OperatorKinds.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
#define DEBUG_TYPE "body-farm"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
|
@ -55,7 +62,9 @@ public:
|
|||
CompoundStmt *makeCompound(ArrayRef<Stmt*>);
|
||||
|
||||
/// Create a new DeclRefExpr for the referenced variable.
|
||||
DeclRefExpr *makeDeclRefExpr(const VarDecl *D);
|
||||
DeclRefExpr *makeDeclRefExpr(const VarDecl *D,
|
||||
bool RefersToEnclosingVariableOrCapture = false,
|
||||
bool GetNonReferenceType = false);
|
||||
|
||||
/// Create a new UnaryOperator representing a dereference.
|
||||
UnaryOperator *makeDereference(const Expr *Arg, QualType Ty);
|
||||
|
@ -66,9 +75,24 @@ public:
|
|||
/// Create an implicit cast to a builtin boolean type.
|
||||
ImplicitCastExpr *makeIntegralCastToBoolean(const Expr *Arg);
|
||||
|
||||
// Create an implicit cast for lvalue-to-rvaluate conversions.
|
||||
/// Create an implicit cast for lvalue-to-rvaluate conversions.
|
||||
ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty);
|
||||
|
||||
/// Create an implicit cast for lvalue-to-rvaluate conversions.
|
||||
ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg,
|
||||
bool GetNonReferenceType = false);
|
||||
|
||||
/// Make RValue out of variable declaration, creating a temporary
|
||||
/// DeclRefExpr in the process.
|
||||
ImplicitCastExpr *
|
||||
makeLvalueToRvalue(const VarDecl *Decl,
|
||||
bool RefersToEnclosingVariableOrCapture = false,
|
||||
bool GetNonReferenceType = false);
|
||||
|
||||
/// Create an implicit cast of the given type.
|
||||
ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty,
|
||||
CastKind CK = CK_LValueToRValue);
|
||||
|
||||
/// Create an Objective-C bool literal.
|
||||
ObjCBoolLiteralExpr *makeObjCBool(bool Val);
|
||||
|
||||
|
@ -78,6 +102,18 @@ public:
|
|||
/// Create a Return statement.
|
||||
ReturnStmt *makeReturn(const Expr *RetVal);
|
||||
|
||||
/// Create an integer literal.
|
||||
IntegerLiteral *makeIntegerLiteral(uint64_t value);
|
||||
|
||||
/// Create a member expression.
|
||||
MemberExpr *makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
|
||||
bool IsArrow = false,
|
||||
ExprValueKind ValueKind = VK_LValue);
|
||||
|
||||
/// Returns a *first* member field of a record declaration with a given name.
|
||||
/// \return an nullptr if no member with such a name exists.
|
||||
NamedDecl *findMemberField(const CXXRecordDecl *RD, StringRef Name);
|
||||
|
||||
private:
|
||||
ASTContext &C;
|
||||
};
|
||||
|
@ -106,16 +142,16 @@ CompoundStmt *ASTMaker::makeCompound(ArrayRef<Stmt *> Stmts) {
|
|||
return new (C) CompoundStmt(C, Stmts, SourceLocation(), SourceLocation());
|
||||
}
|
||||
|
||||
DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) {
|
||||
DeclRefExpr *DR =
|
||||
DeclRefExpr::Create(/* Ctx = */ C,
|
||||
/* QualifierLoc = */ NestedNameSpecifierLoc(),
|
||||
/* TemplateKWLoc = */ SourceLocation(),
|
||||
/* D = */ const_cast<VarDecl*>(D),
|
||||
/* RefersToEnclosingVariableOrCapture = */ false,
|
||||
/* NameLoc = */ SourceLocation(),
|
||||
/* T = */ D->getType(),
|
||||
/* VK = */ VK_LValue);
|
||||
DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D,
|
||||
bool RefersToEnclosingVariableOrCapture,
|
||||
bool GetNonReferenceType) {
|
||||
auto Type = D->getType();
|
||||
if (GetNonReferenceType)
|
||||
Type = Type.getNonReferenceType();
|
||||
|
||||
DeclRefExpr *DR = DeclRefExpr::Create(
|
||||
C, NestedNameSpecifierLoc(), SourceLocation(), const_cast<VarDecl *>(D),
|
||||
RefersToEnclosingVariableOrCapture, SourceLocation(), Type, VK_LValue);
|
||||
return DR;
|
||||
}
|
||||
|
||||
|
@ -125,8 +161,38 @@ UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) {
|
|||
}
|
||||
|
||||
ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) {
|
||||
return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue,
|
||||
const_cast<Expr*>(Arg), nullptr, VK_RValue);
|
||||
return makeImplicitCast(Arg, Ty, CK_LValueToRValue);
|
||||
}
|
||||
|
||||
ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg,
|
||||
bool GetNonReferenceType) {
|
||||
|
||||
QualType Type = Arg->getType();
|
||||
if (GetNonReferenceType)
|
||||
Type = Type.getNonReferenceType();
|
||||
return makeImplicitCast(Arg, Type, CK_LValueToRValue);
|
||||
}
|
||||
|
||||
ImplicitCastExpr *
|
||||
ASTMaker::makeLvalueToRvalue(const VarDecl *Arg,
|
||||
bool RefersToEnclosingVariableOrCapture,
|
||||
bool GetNonReferenceType) {
|
||||
auto Type = Arg->getType();
|
||||
if (GetNonReferenceType)
|
||||
Type = Type.getNonReferenceType();
|
||||
return makeLvalueToRvalue(makeDeclRefExpr(Arg,
|
||||
RefersToEnclosingVariableOrCapture,
|
||||
GetNonReferenceType),
|
||||
Type);
|
||||
}
|
||||
|
||||
ImplicitCastExpr *ASTMaker::makeImplicitCast(const Expr *Arg, QualType Ty,
|
||||
CastKind CK) {
|
||||
return ImplicitCastExpr::Create(C, Ty,
|
||||
/* CastKind= */ CK,
|
||||
/* Expr= */ const_cast<Expr *>(Arg),
|
||||
/* CXXCastPath= */ nullptr,
|
||||
/* ExprValueKind= */ VK_RValue);
|
||||
}
|
||||
|
||||
Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) {
|
||||
|
@ -161,12 +227,196 @@ ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) {
|
|||
nullptr);
|
||||
}
|
||||
|
||||
IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t value) {
|
||||
return IntegerLiteral::Create(C,
|
||||
llvm::APInt(
|
||||
/*numBits=*/C.getTypeSize(C.IntTy), value),
|
||||
/*QualType=*/C.IntTy, SourceLocation());
|
||||
}
|
||||
|
||||
MemberExpr *ASTMaker::makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
|
||||
bool IsArrow,
|
||||
ExprValueKind ValueKind) {
|
||||
|
||||
DeclAccessPair FoundDecl = DeclAccessPair::make(MemberDecl, AS_public);
|
||||
return MemberExpr::Create(
|
||||
C, base, IsArrow, SourceLocation(), NestedNameSpecifierLoc(),
|
||||
SourceLocation(), MemberDecl, FoundDecl,
|
||||
DeclarationNameInfo(MemberDecl->getDeclName(), SourceLocation()),
|
||||
/* TemplateArgumentListInfo= */ nullptr, MemberDecl->getType(), ValueKind,
|
||||
OK_Ordinary);
|
||||
}
|
||||
|
||||
NamedDecl *ASTMaker::findMemberField(const CXXRecordDecl *RD, StringRef Name) {
|
||||
|
||||
CXXBasePaths Paths(
|
||||
/* FindAmbiguities=*/false,
|
||||
/* RecordPaths=*/false,
|
||||
/* DetectVirtual= */ false);
|
||||
const IdentifierInfo &II = C.Idents.get(Name);
|
||||
DeclarationName DeclName = C.DeclarationNames.getIdentifier(&II);
|
||||
|
||||
DeclContextLookupResult Decls = RD->lookup(DeclName);
|
||||
for (NamedDecl *FoundDecl : Decls)
|
||||
if (!FoundDecl->getDeclContext()->isFunctionOrMethod())
|
||||
return FoundDecl;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Creation functions for faux ASTs.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);
|
||||
|
||||
static CallExpr *
|
||||
create_call_once_funcptr_call(ASTContext &C, ASTMaker M,
|
||||
const ParmVarDecl *Callback,
|
||||
SmallVectorImpl<Expr *> &CallArgs) {
|
||||
|
||||
return new (C) CallExpr(
|
||||
/*ASTContext=*/C,
|
||||
/*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Callback),
|
||||
/*args=*/CallArgs,
|
||||
/*QualType=*/C.VoidTy,
|
||||
/*ExprValueType=*/VK_RValue,
|
||||
/*SourceLocation=*/SourceLocation());
|
||||
}
|
||||
|
||||
static CallExpr *
|
||||
create_call_once_lambda_call(ASTContext &C, ASTMaker M,
|
||||
const ParmVarDecl *Callback, QualType CallbackType,
|
||||
SmallVectorImpl<Expr *> &CallArgs) {
|
||||
|
||||
CXXRecordDecl *CallbackDecl = CallbackType->getAsCXXRecordDecl();
|
||||
|
||||
assert(CallbackDecl != nullptr);
|
||||
assert(CallbackDecl->isLambda());
|
||||
FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator();
|
||||
assert(callOperatorDecl != nullptr);
|
||||
|
||||
DeclRefExpr *callOperatorDeclRef =
|
||||
DeclRefExpr::Create(/* Ctx = */ C,
|
||||
/* QualifierLoc = */ NestedNameSpecifierLoc(),
|
||||
/* TemplateKWLoc = */ SourceLocation(),
|
||||
const_cast<FunctionDecl *>(callOperatorDecl),
|
||||
/* RefersToEnclosingVariableOrCapture= */ false,
|
||||
/* NameLoc = */ SourceLocation(),
|
||||
/* T = */ callOperatorDecl->getType(),
|
||||
/* VK = */ VK_LValue);
|
||||
|
||||
CallArgs.insert(
|
||||
CallArgs.begin(),
|
||||
M.makeDeclRefExpr(Callback,
|
||||
/* RefersToEnclosingVariableOrCapture= */ true,
|
||||
/* GetNonReferenceType= */ true));
|
||||
|
||||
return new (C)
|
||||
CXXOperatorCallExpr(/*AstContext=*/C, OO_Call, callOperatorDeclRef,
|
||||
/*args=*/CallArgs,
|
||||
/*QualType=*/C.VoidTy,
|
||||
/*ExprValueType=*/VK_RValue,
|
||||
/*SourceLocation=*/SourceLocation(), FPOptions());
|
||||
}
|
||||
|
||||
/// Create a fake body for std::call_once.
|
||||
/// Emulates the following function body:
|
||||
///
|
||||
/// \code
|
||||
/// typedef struct once_flag_s {
|
||||
/// unsigned long __state = 0;
|
||||
/// } once_flag;
|
||||
/// template<class Callable>
|
||||
/// void call_once(once_flag& o, Callable func) {
|
||||
/// if (!o.__state) {
|
||||
/// func();
|
||||
/// }
|
||||
/// o.__state = 1;
|
||||
/// }
|
||||
/// \endcode
|
||||
static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
|
||||
DEBUG(llvm::dbgs() << "Generating body for call_once\n");
|
||||
|
||||
// We need at least two parameters.
|
||||
if (D->param_size() < 2)
|
||||
return nullptr;
|
||||
|
||||
ASTMaker M(C);
|
||||
|
||||
const ParmVarDecl *Flag = D->getParamDecl(0);
|
||||
const ParmVarDecl *Callback = D->getParamDecl(1);
|
||||
QualType CallbackType = Callback->getType().getNonReferenceType();
|
||||
|
||||
SmallVector<Expr *, 5> CallArgs;
|
||||
|
||||
// All arguments past first two ones are passed to the callback.
|
||||
for (unsigned int i = 2; i < D->getNumParams(); i++)
|
||||
CallArgs.push_back(M.makeLvalueToRvalue(D->getParamDecl(i)));
|
||||
|
||||
CallExpr *CallbackCall;
|
||||
if (CallbackType->getAsCXXRecordDecl() &&
|
||||
CallbackType->getAsCXXRecordDecl()->isLambda()) {
|
||||
|
||||
CallbackCall =
|
||||
create_call_once_lambda_call(C, M, Callback, CallbackType, CallArgs);
|
||||
} else {
|
||||
|
||||
// Function pointer case.
|
||||
CallbackCall = create_call_once_funcptr_call(C, M, Callback, CallArgs);
|
||||
}
|
||||
|
||||
QualType FlagType = Flag->getType().getNonReferenceType();
|
||||
DeclRefExpr *FlagDecl =
|
||||
M.makeDeclRefExpr(Flag,
|
||||
/* RefersToEnclosingVariableOrCapture=*/true,
|
||||
/* GetNonReferenceType=*/true);
|
||||
|
||||
CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl();
|
||||
|
||||
// Note: here we are assuming libc++ implementation of call_once,
|
||||
// which has a struct with a field `__state_`.
|
||||
// Body farming might not work for other `call_once` implementations.
|
||||
NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_");
|
||||
ValueDecl *FieldDecl;
|
||||
if (FoundDecl) {
|
||||
FieldDecl = dyn_cast<ValueDecl>(FoundDecl);
|
||||
} else {
|
||||
DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, "
|
||||
<< "unable to synthesize call_once body, ignoring "
|
||||
<< "the call.\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FieldDecl);
|
||||
assert(Deref->isLValue());
|
||||
QualType DerefType = Deref->getType();
|
||||
|
||||
// Negation predicate.
|
||||
UnaryOperator *FlagCheck = new (C) UnaryOperator(
|
||||
/* input= */
|
||||
M.makeImplicitCast(M.makeLvalueToRvalue(Deref, DerefType), DerefType,
|
||||
CK_IntegralToBoolean),
|
||||
/* opc= */ UO_LNot,
|
||||
/* QualType= */ C.IntTy,
|
||||
/* ExprValueKind= */ VK_RValue,
|
||||
/* ExprObjectKind= */ OK_Ordinary, SourceLocation());
|
||||
|
||||
// Create assignment.
|
||||
BinaryOperator *FlagAssignment = M.makeAssignment(
|
||||
Deref, M.makeIntegralCast(M.makeIntegerLiteral(1), DerefType), DerefType);
|
||||
|
||||
IfStmt *Out = new (C)
|
||||
IfStmt(C, SourceLocation(),
|
||||
/* IsConstexpr= */ false,
|
||||
/* init= */ nullptr,
|
||||
/* var= */ nullptr,
|
||||
/* cond= */ FlagCheck,
|
||||
/* then= */ M.makeCompound({CallbackCall, FlagAssignment}));
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
||||
/// Create a fake body for dispatch_once.
|
||||
static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
|
||||
// Check if we have at least two parameters.
|
||||
|
@ -202,15 +452,17 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
|
|||
ASTMaker M(C);
|
||||
|
||||
// (1) Create the call.
|
||||
DeclRefExpr *DR = M.makeDeclRefExpr(Block);
|
||||
ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);
|
||||
CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue,
|
||||
SourceLocation());
|
||||
CallExpr *CE = new (C) CallExpr(
|
||||
/*ASTContext=*/C,
|
||||
/*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block),
|
||||
/*args=*/None,
|
||||
/*QualType=*/C.VoidTy,
|
||||
/*ExprValueType=*/VK_RValue,
|
||||
/*SourceLocation=*/SourceLocation());
|
||||
|
||||
// (2) Create the assignment to the predicate.
|
||||
IntegerLiteral *IL =
|
||||
IntegerLiteral::Create(C, llvm::APInt(C.getTypeSize(C.IntTy), (uint64_t) 1),
|
||||
C.IntTy, SourceLocation());
|
||||
IntegerLiteral *IL = M.makeIntegerLiteral(1);
|
||||
|
||||
BinaryOperator *B =
|
||||
M.makeAssignment(
|
||||
M.makeDereference(
|
||||
|
@ -234,13 +486,20 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
|
|||
PredicateTy),
|
||||
PredicateTy);
|
||||
|
||||
UnaryOperator *UO = new (C) UnaryOperator(LValToRval, UO_LNot, C.IntTy,
|
||||
VK_RValue, OK_Ordinary,
|
||||
SourceLocation());
|
||||
UnaryOperator *UO = new (C) UnaryOperator(
|
||||
/* input= */ LValToRval,
|
||||
/* opc= */ UO_LNot,
|
||||
/* QualType= */ C.IntTy,
|
||||
/* ExprValueKind= */ VK_RValue,
|
||||
/* ExprObjectKind= */ OK_Ordinary, SourceLocation());
|
||||
|
||||
// (5) Create the 'if' statement.
|
||||
IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr,
|
||||
UO, CS);
|
||||
IfStmt *If = new (C) IfStmt(C, SourceLocation(),
|
||||
/* IsConstexpr= */ false,
|
||||
/* init= */ nullptr,
|
||||
/* var= */ nullptr,
|
||||
/* cond= */ UO,
|
||||
/* then= */ CS);
|
||||
return If;
|
||||
}
|
||||
|
||||
|
@ -370,8 +629,9 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) {
|
|||
if (Name.startswith("OSAtomicCompareAndSwap") ||
|
||||
Name.startswith("objc_atomicCompareAndSwap")) {
|
||||
FF = create_OSAtomicCompareAndSwap;
|
||||
}
|
||||
else {
|
||||
} else if (Name == "call_once" && D->getDeclContext()->isStdNamespace()) {
|
||||
FF = create_call_once;
|
||||
} else {
|
||||
FF = llvm::StringSwitch<FunctionFarmer>(Name)
|
||||
.Case("dispatch_sync", create_dispatch_sync)
|
||||
.Case("dispatch_once", create_dispatch_once)
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -w -verify %s
|
||||
|
||||
void clang_analyzer_eval(bool);
|
||||
|
||||
// Faking std::std::call_once implementation.
|
||||
namespace std {
|
||||
typedef struct once_flag_s {
|
||||
unsigned long __state_ = 0;
|
||||
} once_flag;
|
||||
|
||||
template <class Callable, class... Args>
|
||||
void call_once(once_flag &o, Callable func, Args... args);
|
||||
} // namespace std
|
||||
|
||||
// Check with Lambdas.
|
||||
void test_called_warning() {
|
||||
std::once_flag g_initialize;
|
||||
int z;
|
||||
|
||||
std::call_once(g_initialize, [&] {
|
||||
int *x = nullptr;
|
||||
int y = *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
|
||||
z = 200;
|
||||
});
|
||||
}
|
||||
|
||||
void test_called_on_path_inside_no_warning() {
|
||||
std::once_flag g_initialize;
|
||||
|
||||
int *x = nullptr;
|
||||
int y = 100;
|
||||
int z;
|
||||
|
||||
std::call_once(g_initialize, [&] {
|
||||
z = 200;
|
||||
x = &z;
|
||||
});
|
||||
|
||||
*x = 100; // no-warning
|
||||
clang_analyzer_eval(z == 100); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
void test_called_on_path_no_warning() {
|
||||
std::once_flag g_initialize;
|
||||
|
||||
int *x = nullptr;
|
||||
int y = 100;
|
||||
|
||||
std::call_once(g_initialize, [&] {
|
||||
x = &y;
|
||||
});
|
||||
|
||||
*x = 100; // no-warning
|
||||
}
|
||||
|
||||
void test_called_on_path_warning() {
|
||||
std::once_flag g_initialize;
|
||||
|
||||
int y = 100;
|
||||
int *x = &y;
|
||||
|
||||
std::call_once(g_initialize, [&] {
|
||||
x = nullptr;
|
||||
});
|
||||
|
||||
*x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
|
||||
}
|
||||
|
||||
void test_called_once_warning() {
|
||||
std::once_flag g_initialize;
|
||||
|
||||
int *x = nullptr;
|
||||
int y = 100;
|
||||
|
||||
std::call_once(g_initialize, [&] {
|
||||
x = nullptr;
|
||||
});
|
||||
|
||||
std::call_once(g_initialize, [&] {
|
||||
x = &y;
|
||||
});
|
||||
|
||||
*x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
|
||||
}
|
||||
|
||||
void test_called_once_no_warning() {
|
||||
std::once_flag g_initialize;
|
||||
|
||||
int *x = nullptr;
|
||||
int y = 100;
|
||||
|
||||
std::call_once(g_initialize, [&] {
|
||||
x = &y;
|
||||
});
|
||||
|
||||
std::call_once(g_initialize, [&] {
|
||||
x = nullptr;
|
||||
});
|
||||
|
||||
*x = 100; // no-warning
|
||||
}
|
||||
|
||||
static int global = 0;
|
||||
void funcPointer() {
|
||||
global = 1;
|
||||
}
|
||||
|
||||
void test_func_pointers() {
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, &funcPointer);
|
||||
clang_analyzer_eval(global == 1); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
template <class _Fp>
|
||||
class function; // undefined
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
struct function<_Rp(_ArgTypes...)> {
|
||||
_Rp operator()(_ArgTypes...) const;
|
||||
template <class _Fp>
|
||||
function(_Fp);
|
||||
};
|
||||
|
||||
// Note: currently we do not support calls to std::function,
|
||||
// but the analyzer should not crash either.
|
||||
void test_function_objects_warning() {
|
||||
int x = 0;
|
||||
int *y = &x;
|
||||
|
||||
std::once_flag flag;
|
||||
|
||||
function<void()> func = [&]() {
|
||||
y = nullptr;
|
||||
};
|
||||
|
||||
std::call_once(flag, func);
|
||||
|
||||
func();
|
||||
int z = *y;
|
||||
}
|
||||
|
||||
void test_param_passing_lambda() {
|
||||
std::once_flag flag;
|
||||
int x = 120;
|
||||
int y = 0;
|
||||
|
||||
std::call_once(flag, [&](int p) {
|
||||
y = p;
|
||||
},
|
||||
x);
|
||||
|
||||
clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
void test_param_passing_lambda_false() {
|
||||
std::once_flag flag;
|
||||
int x = 120;
|
||||
|
||||
std::call_once(flag, [&](int p) {
|
||||
x = 0;
|
||||
},
|
||||
x);
|
||||
|
||||
clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
|
||||
}
|
||||
|
||||
void test_param_passing_stored_lambda() {
|
||||
std::once_flag flag;
|
||||
int x = 120;
|
||||
int y = 0;
|
||||
|
||||
auto lambda = [&](int p) {
|
||||
y = p;
|
||||
};
|
||||
|
||||
std::call_once(flag, lambda, x);
|
||||
clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
void test_multiparam_passing_lambda() {
|
||||
std::once_flag flag;
|
||||
int x = 120;
|
||||
|
||||
std::call_once(flag, [&](int a, int b, int c) {
|
||||
x = a + b + c;
|
||||
},
|
||||
1, 2, 3);
|
||||
|
||||
clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
|
||||
clang_analyzer_eval(x == 6); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
static int global2 = 0;
|
||||
void test_param_passing_lambda_global() {
|
||||
std::once_flag flag;
|
||||
global2 = 0;
|
||||
std::call_once(flag, [&](int a, int b, int c) {
|
||||
global2 = a + b + c;
|
||||
},
|
||||
1, 2, 3);
|
||||
clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
static int global3 = 0;
|
||||
void funcptr(int a, int b, int c) {
|
||||
global3 = a + b + c;
|
||||
}
|
||||
|
||||
void test_param_passing_funcptr() {
|
||||
std::once_flag flag;
|
||||
global3 = 0;
|
||||
|
||||
std::call_once(flag, &funcptr, 1, 2, 3);
|
||||
|
||||
clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
void test_blocks() {
|
||||
global3 = 0;
|
||||
std::once_flag flag;
|
||||
std::call_once(flag, ^{
|
||||
global3 = 120;
|
||||
});
|
||||
clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
int call_once() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
void test_non_std_call_once() {
|
||||
int x = call_once();
|
||||
clang_analyzer_eval(x == 5); // expected-warning{{TRUE}}
|
||||
}
|
Loading…
Reference in New Issue