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,
|
||||
const ParmVarDecl *Callback,
|
||||
QualType CallbackType,
|
||||
CXXRecordDecl *CallbackDecl,
|
||||
ArrayRef<Expr *> CallArgs) {
|
||||
|
||||
CXXRecordDecl *CallbackDecl = CallbackType->getAsCXXRecordDecl();
|
||||
|
||||
assert(CallbackDecl != nullptr);
|
||||
assert(CallbackDecl->isLambda());
|
||||
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 *Callback = D->getParamDecl(1);
|
||||
QualType CallbackType = Callback->getType().getNonReferenceType();
|
||||
|
||||
// Nullable pointer, non-null iff function is a CXXRecordDecl.
|
||||
CXXRecordDecl *CallbackRecordDecl = CallbackType->getAsCXXRecordDecl();
|
||||
QualType FlagType = Flag->getType().getNonReferenceType();
|
||||
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;
|
||||
}
|
||||
|
||||
bool isLambdaCall = CallbackType->getAsCXXRecordDecl() &&
|
||||
CallbackType->getAsCXXRecordDecl()->isLambda();
|
||||
bool isLambdaCall = CallbackRecordDecl && CallbackRecordDecl->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;
|
||||
const FunctionProtoType *CallbackFunctionType;
|
||||
if (isLambdaCall) {
|
||||
|
||||
if (isLambdaCall)
|
||||
// Lambda requires callback itself inserted as a first parameter.
|
||||
CallArgs.push_back(
|
||||
M.makeDeclRefExpr(Callback,
|
||||
/* 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.
|
||||
for (unsigned int i = 2; i < D->getNumParams(); i++)
|
||||
CallArgs.push_back(
|
||||
M.makeLvalueToRvalue(D->getParamDecl(i),
|
||||
/* RefersToEnclosingVariableOrCapture= */ false));
|
||||
if (!CallbackFunctionType)
|
||||
return nullptr;
|
||||
|
||||
// First two arguments are used for the flag and for the callback.
|
||||
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;
|
||||
if (isLambdaCall) {
|
||||
|
||||
CallbackCall =
|
||||
create_call_once_lambda_call(C, M, Callback, CallbackType, CallArgs);
|
||||
CallbackCall = create_call_once_lambda_call(C, M, Callback,
|
||||
CallbackRecordDecl, CallArgs);
|
||||
} else {
|
||||
|
||||
// Function pointer case.
|
||||
|
|
|
@ -249,3 +249,44 @@ void g();
|
|||
void test_no_segfault_on_different_impl() {
|
||||
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