forked from OSchip/llvm-project
[analyzer] DeadStoresChecker: Treat locals captured by reference in C++ lambdas as escaped.
The analyzer currently reports dead store false positives when a local variable is captured by reference in a C++ lambda. For example: int local = 0; auto lambda = [&local]() { local++; }; local = 7; // False Positive: Value stored to 'local' is never read lambda(); In this case, the assignment setting `local` to 7 is not a dead store because the called lambda will later read that assigned value. This commit silences this source of false positives by treating locals captured by reference in C++ lambdas as escaped, similarly to how the DeadStoresChecker deals with locals whose address is taken. rdar://problem/22165179 llvm-svn: 253630
This commit is contained in:
parent
c85f4ced4d
commit
c7315b3ebf
|
@ -401,6 +401,11 @@ public:
|
||||||
// Check for '&'. Any VarDecl whose address has been taken we treat as
|
// Check for '&'. Any VarDecl whose address has been taken we treat as
|
||||||
// escaped.
|
// escaped.
|
||||||
// FIXME: What about references?
|
// FIXME: What about references?
|
||||||
|
if (auto *LE = dyn_cast<LambdaExpr>(S)) {
|
||||||
|
findLambdaReferenceCaptures(LE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
|
const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
|
||||||
if (!U)
|
if (!U)
|
||||||
return;
|
return;
|
||||||
|
@ -412,6 +417,28 @@ public:
|
||||||
if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
|
if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
|
||||||
Escaped.insert(VD);
|
Escaped.insert(VD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Treat local variables captured by reference in C++ lambdas as escaped.
|
||||||
|
void findLambdaReferenceCaptures(const LambdaExpr *LE) {
|
||||||
|
const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
|
||||||
|
llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
|
||||||
|
FieldDecl *ThisCaptureField;
|
||||||
|
LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
|
||||||
|
|
||||||
|
for (const LambdaCapture &C : LE->captures()) {
|
||||||
|
if (!C.capturesVariable())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
VarDecl *VD = C.getCapturedVar();
|
||||||
|
const FieldDecl *FD = CaptureFields[VD];
|
||||||
|
if (!FD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If the capture field is a reference type, it is capture-by-reference.
|
||||||
|
if (FD->getType()->isReferenceType())
|
||||||
|
Escaped.insert(VD);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
|
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,deadcode,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
|
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
|
||||||
// RUN: FileCheck --input-file=%t %s
|
// RUN: FileCheck --input-file=%t %s
|
||||||
|
|
||||||
|
@ -281,6 +281,49 @@ void captureStructReference(const StructPR24914& s) {
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lambda capture counts as use for dead-store checking.
|
||||||
|
|
||||||
|
int returnsValue();
|
||||||
|
|
||||||
|
void captureByCopyCausesUse() {
|
||||||
|
int local1 = returnsValue(); // no-warning
|
||||||
|
int local2 = returnsValue(); // no-warning
|
||||||
|
int local3 = returnsValue(); // expected-warning{{Value stored to 'local3' during its initialization is never read}}
|
||||||
|
|
||||||
|
(void)[local1, local2]() { }; // Explicit capture by copy counts as use.
|
||||||
|
|
||||||
|
int local4 = returnsValue(); // no-warning
|
||||||
|
int local5 = returnsValue(); // expected-warning{{Value stored to 'local5' during its initialization is never read}}
|
||||||
|
|
||||||
|
(void)[=]() {
|
||||||
|
(void)local4; // Implicit capture by copy counts as use
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void captureByReference() {
|
||||||
|
int local1 = returnsValue(); // no-warning
|
||||||
|
|
||||||
|
auto lambda1 = [&local1]() { // Explicit capture by reference
|
||||||
|
local1++;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't treat as a dead store because local1 was was captured by reference.
|
||||||
|
local1 = 7; // no-warning
|
||||||
|
|
||||||
|
lambda1();
|
||||||
|
|
||||||
|
int local2 = returnsValue(); // no-warning
|
||||||
|
|
||||||
|
auto lambda2 = [&]() {
|
||||||
|
local2++; // Implicit capture by reference
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't treat as a dead store because local2 was was captured by reference.
|
||||||
|
local2 = 7; // no-warning
|
||||||
|
|
||||||
|
lambda2();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// CHECK: [B2 (ENTRY)]
|
// CHECK: [B2 (ENTRY)]
|
||||||
// CHECK: Succs (1): B1
|
// CHECK: Succs (1): B1
|
||||||
|
|
Loading…
Reference in New Issue