diff options
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 33 | ||||
-rw-r--r-- | test/Analysis/dtor.cpp | 26 |
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 + } +} |