aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Core')
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp10
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp17
-rw-r--r--lib/StaticAnalyzer/Core/CoreEngine.cpp13
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp5
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp125
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp147
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp11
-rw-r--r--lib/StaticAnalyzer/Core/SymbolManager.cpp7
8 files changed, 220 insertions, 115 deletions
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index a2642120b7..4330d2ed47 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -427,7 +427,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
- if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
+ if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) {
PathDiagnosticCallPiece *C =
PathDiagnosticCallPiece::construct(N, *CE, SMgr);
PD.getActivePath().push_front(C);
@@ -440,7 +440,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
PD.popActivePath();
// The current active path should never be empty. Either we
// just added a bunch of stuff to the top-level path, or
- // we have a previous CallExit. If the front of the active
+ // we have a previous CallExitEnd. If the front of the active
// path is not a PathDiagnosticCallPiece, it means that the
// path terminated within a function call. We must then take the
// current contents of the active path and place it within
@@ -1066,10 +1066,10 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
do {
- if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
+ if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) {
const StackFrameContext *LCtx =
CE->getLocationContext()->getCurrentStackFrame();
- PathDiagnosticLocation Loc(LCtx->getCallSite(),
+ PathDiagnosticLocation Loc(CE->getStmt(),
PDB.getSourceManager(),
LCtx);
EB.addEdge(Loc, true);
@@ -1099,7 +1099,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
// The current active path should never be empty. Either we
// just added a bunch of stuff to the top-level path, or
- // we have a previous CallExit. If the front of the active
+ // we have a previous CallExitEnd. If the front of the active
// path is not a PathDiagnosticCallPiece, it means that the
// path terminated within a function call. We must then take the
// current contents of the active path and place it within
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 0bcc343fba..8a6ea1d0f8 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -381,21 +381,25 @@ namespace {
SymbolReaper &SR;
const Stmt *S;
ExprEngine &Eng;
+ ProgramPoint::Kind ProgarmPointKind;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
- const Stmt *s, ExprEngine &eng)
- : Checkers(checkers), SR(sr), S(s), Eng(eng) { }
+ const Stmt *s, ExprEngine &eng,
+ ProgramPoint::Kind K)
+ : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { }
void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind;
- const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind,
Pred->getLocationContext(), checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
+ // Note, do not pass the statement to the checkers without letting them
+ // differentiate if we ran remove dead bindings before or after the
+ // statement.
checkFn(SR, C);
}
};
@@ -406,8 +410,9 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SymbolReaper &SymReaper,
const Stmt *S,
- ExprEngine &Eng) {
- CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng);
+ ExprEngine &Eng,
+ ProgramPoint::Kind K) {
+ CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K);
expandGraphWithCheckers(C, Dst, Src);
}
diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp
index ca662c7992..c9de20e500 100644
--- a/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -248,7 +248,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
break;
}
- case ProgramPoint::CallExitKind:
+ case ProgramPoint::CallExitBeginKind:
SubEng.processCallExit(Pred);
break;
@@ -531,14 +531,14 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N,
WList->enqueue(Succ, Block, Idx+1);
}
-ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) {
- // Create a CallExit node and enqueue it.
+ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) {
+ // Create a CallExitBegin node and enqueue it.
const StackFrameContext *LocCtx
= cast<StackFrameContext>(N->getLocationContext());
const Stmt *CE = LocCtx->getCallSite();
// Use the the callee location context.
- CallExit Loc(CE, LocCtx);
+ CallExitBegin Loc(CE, LocCtx);
bool isNew;
ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew);
@@ -565,12 +565,13 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set,
void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) {
for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) {
ExplodedNode *N = *I;
- // If we are in an inlined call, generate CallExit node.
+ // If we are in an inlined call, generate CallExitBegin node.
if (N->getLocationContext()->getParent()) {
- N = generateCallExitNode(N);
+ N = generateCallExitBeginNode(N);
if (N)
WList->enqueue(N);
} else {
+ // TODO: We should run remove dead bindings here.
G->addEndOfPath(N);
NumPathsExplored++;
}
diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index 0dcbe1ff4a..e433c353c1 100644
--- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -57,7 +57,7 @@ ExplodedGraph::~ExplodedGraph() {}
//===----------------------------------------------------------------------===//
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
- // Reclaimn all nodes that match *all* the following criteria:
+ // Reclaim all nodes that match *all* the following criteria:
//
// (1) 1 predecessor (that has one successor)
// (2) 1 successor (that has one predecessor)
@@ -83,7 +83,8 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Condition 3.
ProgramPoint progPoint = node->getLocation();
if (!isa<PostStmt>(progPoint) ||
- (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint)))
+ (isa<CallEnter>(progPoint) ||
+ isa<CallExitBegin>(progPoint) || isa<CallExitEnd>(progPoint)))
return false;
// Condition 4.
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index abc6316538..1fd4dcf712 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -42,8 +42,6 @@ using llvm::APSInt;
STATISTIC(NumRemoveDeadBindings,
"The # of times RemoveDeadBindings is called");
-STATISTIC(NumRemoveDeadBindingsSkipped,
- "The # of times RemoveDeadBindings is skipped");
STATISTIC(NumMaxBlockCountReached,
"The # of aborted paths due to reaching the maximum block count in "
"a top level function");
@@ -231,6 +229,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred);
return;
}
+ currentBuilderContext = 0;
}
static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
@@ -260,62 +259,47 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
return !PM.isConsumedExpr(cast<Expr>(S.getStmt()));
}
-void ExprEngine::ProcessStmt(const CFGStmt S,
- ExplodedNode *Pred) {
- // Reclaim any unnecessary nodes in the ExplodedGraph.
- G.reclaimRecentlyAllocatedNodes();
-
- currentStmt = S.getStmt();
- PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
- currentStmt->getLocStart(),
- "Error evaluating statement");
-
- EntryNode = Pred;
-
- ProgramStateRef EntryState = EntryNode->getState();
- CleanedState = EntryState;
-
- // Create the cleaned state.
- const LocationContext *LC = EntryNode->getLocationContext();
- SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
-
- if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) {
- NumRemoveDeadBindings++;
- getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
-
- const StackFrameContext *SFC = LC->getCurrentStackFrame();
-
- // Create a state in which dead bindings are removed from the environment
- // and the store. TODO: The function should just return new env and store,
- // not a new state.
- CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
- } else {
- NumRemoveDeadBindingsSkipped++;
- }
+void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
+ const Stmt *ReferenceStmt,
+ const LocationContext *LC,
+ const Stmt *DiagnosticStmt,
+ ProgramPoint::Kind K) {
+ assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind ||
+ ReferenceStmt == 0) && "PreStmt is not generally supported by "
+ "the SymbolReaper yet");
+ NumRemoveDeadBindings++;
+ CleanedState = Pred->getState();
+ SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager());
+
+ getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
+
+ // Create a state in which dead bindings are removed from the environment
+ // and the store. TODO: The function should just return new env and store,
+ // not a new state.
+ const StackFrameContext *SFC = LC->getCurrentStackFrame();
+ CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
// Process any special transfer function for dead symbols.
- ExplodedNodeSet Tmp;
// A tag to track convenience transitions, which can be removed at cleanup.
static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node");
-
if (!SymReaper.hasDeadSymbols()) {
// Generate a CleanedNode that has the environment and store cleaned
// up. Since no symbols are dead, we can optimize and not clean out
// the constraint manager.
- StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
- Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag);
+ StmtNodeBuilder Bldr(Pred, Out, *currentBuilderContext);
+ Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, false, &cleanupTag,K);
} else {
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
ExplodedNodeSet CheckedSet;
- getCheckerManager().runCheckersForDeadSymbols(CheckedSet, EntryNode,
- SymReaper, currentStmt, *this);
+ getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper,
+ DiagnosticStmt, *this, K);
// For each node in CheckedSet, generate CleanedNodes that have the
// environment, the store, and the constraints cleaned up but have the
// user-supplied states as the predecessors.
- StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext);
+ StmtNodeBuilder Bldr(CheckedSet, Out, *currentBuilderContext);
for (ExplodedNodeSet::const_iterator
I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) {
ProgramStateRef CheckerState = (*I)->getState();
@@ -324,10 +308,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
SymReaper);
- assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) &&
+ assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) &&
"Checkers are not allowed to modify the Environment as a part of "
"checkDeadSymbols processing.");
- assert(StateMgr.haveEqualStores(CheckerState, EntryState) &&
+ assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) &&
"Checkers are not allowed to modify the Store as a part of "
"checkDeadSymbols processing.");
@@ -335,13 +319,35 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
// generate a transition to that state.
ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
- Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag,
- ProgramPoint::PostPurgeDeadSymbolsKind);
+ Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, false,
+ &cleanupTag, K);
}
}
+}
+
+void ExprEngine::ProcessStmt(const CFGStmt S,
+ ExplodedNode *Pred) {
+ // Reclaim any unnecessary nodes in the ExplodedGraph.
+ G.reclaimRecentlyAllocatedNodes();
+
+ currentStmt = S.getStmt();
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ currentStmt->getLocStart(),
+ "Error evaluating statement");
+ // Remove dead bindings and symbols.
+ EntryNode = Pred;
+ ExplodedNodeSet CleanedStates;
+ if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){
+ removeDead(EntryNode, CleanedStates, currentStmt,
+ Pred->getLocationContext(), currentStmt);
+ } else
+ CleanedStates.Add(EntryNode);
+
+ // Visit the statement.
ExplodedNodeSet Dst;
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
+ for (ExplodedNodeSet::iterator I = CleanedStates.begin(),
+ E = CleanedStates.end(); I != E; ++I) {
ExplodedNodeSet DstI;
// Visit the statement.
Visit(currentStmt, *I, DstI);
@@ -994,7 +1000,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
continue;
// We reached the caller. Find the node right before we started
// processing the CallExpr.
- if (isa<PostPurgeDeadSymbols>(L))
+ if (L.isPurgeKind())
continue;
if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L))
if (SP->getStmt() == CalleeSF->getCallSite())
@@ -1861,10 +1867,17 @@ struct DOTGraphTraits<ExplodedNode*> :
ProgramPoint Loc = N->getLocation();
switch (Loc.getKind()) {
- case ProgramPoint::BlockEntranceKind:
+ case ProgramPoint::BlockEntranceKind: {
Out << "Block Entrance: B"
<< cast<BlockEntrance>(Loc).getBlock()->getBlockID();
+ if (const NamedDecl *ND =
+ dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) {
+ Out << " (";
+ ND->printName(Out);
+ Out << ")";
+ }
break;
+ }
case ProgramPoint::BlockExitKind:
assert (false);
@@ -1874,8 +1887,20 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << "CallEnter";
break;
- case ProgramPoint::CallExitKind:
- Out << "CallExit";
+ case ProgramPoint::CallExitBeginKind:
+ Out << "CallExitBegin";
+ break;
+
+ case ProgramPoint::CallExitEndKind:
+ Out << "CallExitEnd";
+ break;
+
+ case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
+ Out << "PostStmtPurgeDeadSymbols";
+ break;
+
+ case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
+ Out << "PreStmtPurgeDeadSymbols";
break;
case ProgramPoint::EpsilonKind:
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index b9f4e153d7..4d0da06427 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -51,71 +51,140 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
Engine.getWorkList()->enqueue(Node);
}
-static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) {
+// Find the last statement on the path to the exploded node and the
+// corresponding Block.
+static std::pair<const Stmt*,
+ const CFGBlock*> getLastStmt(const ExplodedNode *Node) {
+ const Stmt *S = 0;
+ const CFGBlock *Blk = 0;
+ const StackFrameContext *SF =
+ Node->getLocation().getLocationContext()->getCurrentStackFrame();
while (Node) {
const ProgramPoint &PP = Node->getLocation();
- // Skip any BlockEdges.
- if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) {
+ // Skip any BlockEdges, empty blocks, and the CallExitBegin node.
+ if (isa<BlockEdge>(PP) || isa<CallExitBegin>(PP) || isa<BlockEntrance>(PP)){
assert(Node->pred_size() == 1);
Node = *Node->pred_begin();
continue;
- }
+ }
+ // If we reached the CallEnter, the function has no statements.
+ if (isa<CallEnter>(PP))
+ break;
if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) {
- const Stmt *S = SP->getStmt();
- return dyn_cast<ReturnStmt>(S);
+ S = SP->getStmt();
+ // Now, get the enclosing basic block.
+ while (Node && Node->pred_size() >=1 ) {
+ const ProgramPoint &PP = Node->getLocation();
+ if (isa<BlockEdge>(PP) &&
+ (PP.getLocationContext()->getCurrentStackFrame() == SF)) {
+ BlockEdge &EPP = cast<BlockEdge>(PP);
+ Blk = EPP.getDst();
+ break;
+ }
+ Node = *Node->pred_begin();
+ }
+ break;
}
break;
}
- return 0;
+ return std::pair<const Stmt*, const CFGBlock*>(S, Blk);
}
-void ExprEngine::processCallExit(ExplodedNode *Pred) {
- ProgramStateRef state = Pred->getState();
- const StackFrameContext *calleeCtx =
- Pred->getLocationContext()->getCurrentStackFrame();
+/// The call exit is simulated with a sequence of nodes, which occur between
+/// CallExitBegin and CallExitEnd. The following operations occur between the
+/// two program points:
+/// 1. CallExitBegin (triggers the start of call exit sequence)
+/// 2. Bind the return value
+/// 3. Run Remove dead bindings to clean up the dead symbols from the callee.
+/// 4. CallExitEnd (switch to the caller context)
+/// 5. PostStmt<CallExpr>
+void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
+ // Step 1 CEBNode was generated before the call.
+
+ const StackFrameContext *calleeCtx =
+ CEBNode->getLocationContext()->getCurrentStackFrame();
const LocationContext *callerCtx = calleeCtx->getParent();
const Stmt *CE = calleeCtx->getCallSite();
-
+ ProgramStateRef state = CEBNode->getState();
+ // Find the last statement in the function and the corresponding basic block.
+ const Stmt *LastSt = 0;
+ const CFGBlock *Blk = 0;
+ llvm::tie(LastSt, Blk) = getLastStmt(CEBNode);
+
+ // Step 2: generate node with binded return value: CEBNode -> BindedRetNode.
+
// If the callee returns an expression, bind its value to CallExpr.
- if (const ReturnStmt *RS = getReturnStmt(Pred)) {
- const LocationContext *LCtx = Pred->getLocationContext();
+ if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
+ const LocationContext *LCtx = CEBNode->getLocationContext();
SVal V = state->getSVal(RS, LCtx);
state = state->BindExpr(CE, callerCtx, V);
}
-
+
// Bind the constructed object value to CXXConstructExpr.
if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
const CXXThisRegion *ThisR =
- getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
-
+ getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
+
SVal ThisV = state->getSVal(ThisR);
// Always bind the region to the CXXConstructExpr.
- state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV);
+ state = state->BindExpr(CCE, CEBNode->getLocationContext(), ThisV);
}
-
- static SimpleProgramPointTag returnTag("ExprEngine : Call Return");
- PostStmt Loc(CE, callerCtx, &returnTag);
+
+ static SimpleProgramPointTag retValBindTag("ExprEngine : Bind Return Value");
+ PostStmt Loc(LastSt, calleeCtx, &retValBindTag);
bool isNew;
- ExplodedNode *N = G.getNode(Loc, state, false, &isNew);
- N->addPredecessor(Pred, G);
+ ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew);
+ BindedRetNode->addPredecessor(CEBNode, G);
if (!isNew)
return;
-
- // Perform the post-condition check of the CallExpr.
- ExplodedNodeSet Dst;
- NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N);
- SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
- &Ctx);
- SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
-
- getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this,
- /* wasInlined */ true);
-
- // Enqueue the next element in the block.
- for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) {
- Engine.getWorkList()->enqueue(*I,
- calleeCtx->getCallSiteBlock(),
- calleeCtx->getIndex()+1);
+
+ // Step 3: BindedRetNode -> CleanedNodes
+ // If we can find a statement and a block in the inlined function, run remove
+ // dead bindings before returning from the call. This is important to ensure
+ // that we report the issues such as leaks in the stack contexts in which
+ // they occurred.
+ ExplodedNodeSet CleanedNodes;
+ if (LastSt && Blk) {
+ NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode);
+ currentBuilderContext = &Ctx;
+ // Here, we call the Symbol Reaper with 0 statement and caller location
+ // context, telling it to clean up everything in the callee's context
+ // (and it's children). We use LastStmt as a diagnostic statement, which
+ // which the PreStmtPurge Dead point will be associated.
+ removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt,
+ ProgramPoint::PostStmtPurgeDeadSymbolsKind);
+ currentBuilderContext = 0;
+ }
+
+ for (ExplodedNodeSet::iterator I = CleanedNodes.begin(),
+ E = CleanedNodes.end(); I != E; ++I) {
+
+ // Step 4: Generate the CallExit and leave the callee's context.
+ // CleanedNodes -> CEENode
+ CallExitEnd Loc(CE, callerCtx);
+ bool isNew;
+ ExplodedNode *CEENode = G.getNode(Loc, (*I)->getState(), false, &isNew);
+ CEENode->addPredecessor(*I, G);
+ if (!isNew)
+ return;
+
+ // Step 5: Perform the post-condition check of the CallExpr and enqueue the
+ // result onto the work list.
+ // CEENode -> Dst -> WorkList
+ ExplodedNodeSet Dst;
+ NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
+ SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
+ &Ctx);
+ SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
+
+ getCheckerManager().runCheckersForPostStmt(Dst, CEENode, CE, *this, true);
+
+ // Enqueue the next element in the block.
+ for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();
+ PSI != PSE; ++PSI) {
+ Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(),
+ calleeCtx->getIndex()+1);
+ }
}
}
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index 01dd965ac5..a1e662f129 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -510,9 +510,9 @@ static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N,
PathDiagnosticCallPiece *
PathDiagnosticCallPiece::construct(const ExplodedNode *N,
- const CallExit &CE,
+ const CallExitEnd &CE,
const SourceManager &SM) {
- const Decl *caller = CE.getLocationContext()->getParent()->getDecl();
+ const Decl *caller = CE.getLocationContext()->getDecl();
PathDiagnosticLocation pos = getLastStmtLoc(N, SM);
return new PathDiagnosticCallPiece(caller, pos);
}
@@ -667,16 +667,13 @@ StackHintGenerator::~StackHintGenerator() {}
std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
ProgramPoint P = N->getLocation();
- const CallExit *CExit = dyn_cast<CallExit>(&P);
- assert(CExit && "Stack Hints should be constructed at CallExit points.");
+ const CallExitEnd *CExit = dyn_cast<CallExitEnd>(&P);
+ assert(CExit && "Stack Hints should be constructed at CallExitEnd points.");
const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt());
if (!CE)
return "";
- // Get the successor node to make sure the return statement is evaluated and
- // CE is set to the result value.
- N = *N->succ_begin();
if (!N)
return getMessageForSymbolNotFound();
diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp
index adefb5858e..5fee9afc08 100644
--- a/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -501,6 +501,9 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
return false;
return true;
}
+ // If no statement is provided, everything is this and parent contexts is live.
+ if (!Loc)
+ return true;
return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal);
}
@@ -510,6 +513,10 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame();
if (VarContext == CurrentContext) {
+ // If no statemetnt is provided, everything is live.
+ if (!Loc)
+ return true;
+
if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
return true;