mirror of https://github.com/microsoft/clang.git
[Analyzer] Correctly handle parameters passed by reference when bodyfarming std::call_once
Explicitly not supporting functor objects. Differential Revision: https://reviews.llvm.org/D39031 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@316249 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
7c9e1b206c
commit
4290aec8a4
|
@ -264,11 +264,8 @@ static CallExpr *create_call_once_funcptr_call(ASTContext &C, ASTMaker M,
|
||||||
|
|
||||||
static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,
|
static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,
|
||||||
const ParmVarDecl *Callback,
|
const ParmVarDecl *Callback,
|
||||||
QualType CallbackType,
|
CXXRecordDecl *CallbackDecl,
|
||||||
ArrayRef<Expr *> CallArgs) {
|
ArrayRef<Expr *> CallArgs) {
|
||||||
|
|
||||||
CXXRecordDecl *CallbackDecl = CallbackType->getAsCXXRecordDecl();
|
|
||||||
|
|
||||||
assert(CallbackDecl != nullptr);
|
assert(CallbackDecl != nullptr);
|
||||||
assert(CallbackDecl->isLambda());
|
assert(CallbackDecl->isLambda());
|
||||||
FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator();
|
FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator();
|
||||||
|
@ -319,6 +316,9 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
|
||||||
const ParmVarDecl *Flag = D->getParamDecl(0);
|
const ParmVarDecl *Flag = D->getParamDecl(0);
|
||||||
const ParmVarDecl *Callback = D->getParamDecl(1);
|
const ParmVarDecl *Callback = D->getParamDecl(1);
|
||||||
QualType CallbackType = Callback->getType().getNonReferenceType();
|
QualType CallbackType = Callback->getType().getNonReferenceType();
|
||||||
|
|
||||||
|
// Nullable pointer, non-null iff function is a CXXRecordDecl.
|
||||||
|
CXXRecordDecl *CallbackRecordDecl = CallbackType->getAsCXXRecordDecl();
|
||||||
QualType FlagType = Flag->getType().getNonReferenceType();
|
QualType FlagType = Flag->getType().getNonReferenceType();
|
||||||
auto *FlagRecordDecl = dyn_cast_or_null<RecordDecl>(FlagType->getAsTagDecl());
|
auto *FlagRecordDecl = dyn_cast_or_null<RecordDecl>(FlagType->getAsTagDecl());
|
||||||
|
|
||||||
|
@ -348,28 +348,58 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isLambdaCall = CallbackType->getAsCXXRecordDecl() &&
|
bool isLambdaCall = CallbackRecordDecl && CallbackRecordDecl->isLambda();
|
||||||
CallbackType->getAsCXXRecordDecl()->isLambda();
|
if (CallbackRecordDecl && !isLambdaCall) {
|
||||||
|
DEBUG(llvm::dbgs() << "Not supported: synthesizing body for functors when "
|
||||||
|
<< "body farming std::call_once, ignoring the call.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
SmallVector<Expr *, 5> CallArgs;
|
SmallVector<Expr *, 5> CallArgs;
|
||||||
|
const FunctionProtoType *CallbackFunctionType;
|
||||||
|
if (isLambdaCall) {
|
||||||
|
|
||||||
if (isLambdaCall)
|
|
||||||
// Lambda requires callback itself inserted as a first parameter.
|
// Lambda requires callback itself inserted as a first parameter.
|
||||||
CallArgs.push_back(
|
CallArgs.push_back(
|
||||||
M.makeDeclRefExpr(Callback,
|
M.makeDeclRefExpr(Callback,
|
||||||
/* RefersToEnclosingVariableOrCapture= */ true));
|
/* RefersToEnclosingVariableOrCapture= */ true));
|
||||||
|
CallbackFunctionType = CallbackRecordDecl->getLambdaCallOperator()
|
||||||
|
->getType()
|
||||||
|
->getAs<FunctionProtoType>();
|
||||||
|
} else {
|
||||||
|
CallbackFunctionType =
|
||||||
|
CallbackType->getPointeeType()->getAs<FunctionProtoType>();
|
||||||
|
}
|
||||||
|
|
||||||
// All arguments past first two ones are passed to the callback.
|
if (!CallbackFunctionType)
|
||||||
for (unsigned int i = 2; i < D->getNumParams(); i++)
|
return nullptr;
|
||||||
CallArgs.push_back(
|
|
||||||
M.makeLvalueToRvalue(D->getParamDecl(i),
|
// First two arguments are used for the flag and for the callback.
|
||||||
/* RefersToEnclosingVariableOrCapture= */ false));
|
if (D->getNumParams() != CallbackFunctionType->getNumParams() + 2) {
|
||||||
|
DEBUG(llvm::dbgs() << "Number of params of the callback does not match "
|
||||||
|
<< "the number of params passed to std::call_once, "
|
||||||
|
<< "ignoring the call");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All arguments past first two ones are passed to the callback,
|
||||||
|
// and we turn lvalues into rvalues if the argument is not passed by
|
||||||
|
// reference.
|
||||||
|
for (unsigned int ParamIdx = 2; ParamIdx < D->getNumParams(); ParamIdx++) {
|
||||||
|
const ParmVarDecl *PDecl = D->getParamDecl(ParamIdx);
|
||||||
|
Expr *ParamExpr = M.makeDeclRefExpr(PDecl);
|
||||||
|
if (!CallbackFunctionType->getParamType(ParamIdx - 2)->isReferenceType()) {
|
||||||
|
QualType PTy = PDecl->getType().getNonReferenceType();
|
||||||
|
ParamExpr = M.makeLvalueToRvalue(ParamExpr, PTy);
|
||||||
|
}
|
||||||
|
CallArgs.push_back(ParamExpr);
|
||||||
|
}
|
||||||
|
|
||||||
CallExpr *CallbackCall;
|
CallExpr *CallbackCall;
|
||||||
if (isLambdaCall) {
|
if (isLambdaCall) {
|
||||||
|
|
||||||
CallbackCall =
|
CallbackCall = create_call_once_lambda_call(C, M, Callback,
|
||||||
create_call_once_lambda_call(C, M, Callback, CallbackType, CallArgs);
|
CallbackRecordDecl, CallArgs);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Function pointer case.
|
// Function pointer case.
|
||||||
|
|
|
@ -249,3 +249,44 @@ void g();
|
||||||
void test_no_segfault_on_different_impl() {
|
void test_no_segfault_on_different_impl() {
|
||||||
std::call_once(g, false); // no-warning
|
std::call_once(g, false); // no-warning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_lambda_refcapture() {
|
||||||
|
static std::once_flag flag;
|
||||||
|
int a = 6;
|
||||||
|
std::call_once(flag, [&](int &a) { a = 42; }, a);
|
||||||
|
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_lambda_refcapture2() {
|
||||||
|
static std::once_flag flag;
|
||||||
|
int a = 6;
|
||||||
|
std::call_once(flag, [=](int &a) { a = 42; }, a);
|
||||||
|
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_lambda_fail_refcapture() {
|
||||||
|
static std::once_flag flag;
|
||||||
|
int a = 6;
|
||||||
|
std::call_once(flag, [=](int a) { a = 42; }, a);
|
||||||
|
clang_analyzer_eval(a == 42); // expected-warning{{FALSE}}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutator(int ¶m) {
|
||||||
|
param = 42;
|
||||||
|
}
|
||||||
|
void test_reftypes_funcptr() {
|
||||||
|
static std::once_flag flag;
|
||||||
|
int a = 6;
|
||||||
|
std::call_once(flag, &mutator, a);
|
||||||
|
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fail_mutator(int param) {
|
||||||
|
param = 42;
|
||||||
|
}
|
||||||
|
void test_mutator_noref() {
|
||||||
|
static std::once_flag flag;
|
||||||
|
int a = 6;
|
||||||
|
std::call_once(flag, &fail_mutator, a);
|
||||||
|
clang_analyzer_eval(a == 42); // expected-warning{{FALSE}}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue