aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp33
-rw-r--r--test/Analysis/dtor.cpp26
2 files changed, 46 insertions, 13 deletions
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 94136c7d61..0f29735dc9 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -66,22 +66,31 @@ static std::pair<const Stmt*,
const StackFrameContext *SF =
Node->getLocation().getLocationContext()->getCurrentStackFrame();
- // Back up through the ExplodedGraph until we reach a statement node.
+ // Back up through the ExplodedGraph until we reach a statement node in this
+ // stack frame.
while (Node) {
const ProgramPoint &PP = Node->getLocation();
- if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) {
- S = SP->getStmt();
- break;
- } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) {
- S = CEE->getCalleeContext()->getCallSite();
- if (S)
+ if (PP.getLocationContext()->getCurrentStackFrame() == SF) {
+ if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) {
+ S = SP->getStmt();
break;
- // If we have an implicit call, we'll probably end up with a
- // StmtPoint inside the callee, which is acceptable.
- // (It's possible a function ONLY contains implicit calls -- such as an
- // implicitly-generated destructor -- so we shouldn't just skip back to
- // the CallEnter node and keep going.)
+ } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) {
+ S = CEE->getCalleeContext()->getCallSite();
+ if (S)
+ break;
+
+ // If there is no statement, this is an implicitly-generated call.
+ // We'll walk backwards over it and then continue the loop to find
+ // an actual statement.
+ const CallEnter *CE;
+ do {
+ Node = Node->getFirstPred();
+ CE = Node->getLocationAs<CallEnter>();
+ } while (!CE || CE->getCalleeContext() != CEE->getCalleeContext());
+
+ // Continue searching the graph.
+ }
} else if (const CallEnter *CE = dyn_cast<CallEnter>(&PP)) {
// If we reached the CallEnter for this function, it has no statements.
if (CE->getCalleeContext() == SF)
diff --git a/test/Analysis/dtor.cpp b/test/Analysis/dtor.cpp
index 1f45925561..d6f66c3845 100644
--- a/test/Analysis/dtor.cpp
+++ b/test/Analysis/dtor.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-store region -analyzer-ipa=inlining -cfg-add-implicit-dtors -Wno-null-dereference -verify %s
void clang_analyzer_eval(bool);
+void clang_analyzer_checkInlined(bool);
class A {
public:
@@ -102,7 +103,7 @@ void testMultipleInheritance3() {
// Remove dead bindings...
doSomething();
// destructor called here
- // expected-warning@27 {{Attempt to free released memory}}
+ // expected-warning@28 {{Attempt to free released memory}}
}
}
@@ -227,3 +228,26 @@ namespace DestructorVirtualCalls {
clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
}
}
+
+
+namespace DestructorsShouldNotAffectReturnValues {
+ class Dtor {
+ public:
+ ~Dtor() {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ }
+ };
+
+ void *allocate() {
+ Dtor d;
+ return malloc(4); // no-warning
+ }
+
+ void test() {
+ // At one point we had an issue where the statements inside an
+ // inlined destructor kept us from finding the return statement,
+ // leading the analyzer to believe that the malloc'd memory had leaked.
+ void *p = allocate();
+ free(p); // no-warning
+ }
+}