aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Analysis/ProgramPoint.h74
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h4
-rw-r--r--include/clang/StaticAnalyzer/Core/CheckerManager.h3
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h2
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h22
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h6
-rw-r--r--lib/Analysis/ProgramPoint.cpp6
-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
-rw-r--r--test/Analysis/malloc-plist.c7213
16 files changed, 4817 insertions, 2848 deletions
diff --git a/include/clang/Analysis/ProgramPoint.h b/include/clang/Analysis/ProgramPoint.h
index aa7a33c956..377f1bc256 100644
--- a/include/clang/Analysis/ProgramPoint.h
+++ b/include/clang/Analysis/ProgramPoint.h
@@ -45,14 +45,16 @@ public:
PostLoadKind,
PreStoreKind,
PostStoreKind,
- PostPurgeDeadSymbolsKind,
+ PostStmtPurgeDeadSymbolsKind,
+ PreStmtPurgeDeadSymbolsKind,
PostConditionKind,
PostLValueKind,
PostInitializerKind,
CallEnterKind,
- CallExitKind,
+ CallExitBeginKind,
+ CallExitEndKind,
MinPostStmtKind = PostStmtKind,
- MaxPostStmtKind = CallExitKind,
+ MaxPostStmtKind = CallExitEndKind,
EpsilonKind};
private:
@@ -113,6 +115,14 @@ public:
return (Kind) x;
}
+ /// \brief Is this a program point corresponding to purge/removal of dead
+ /// symbols and bindings.
+ bool isPurgeKind() {
+ Kind K = getKind();
+ return (K == PostStmtPurgeDeadSymbolsKind ||
+ K == PreStmtPurgeDeadSymbolsKind);
+ }
+
const ProgramPointTag *getTag() const { return Tag; }
const LocationContext *getLocationContext() const {
@@ -340,14 +350,29 @@ public:
}
};
-class PostPurgeDeadSymbols : public PostStmt {
+/// \class Represents a point after we ran remove dead bindings BEFORE
+/// processing the given statement.
+class PreStmtPurgeDeadSymbols : public PostStmt {
public:
- PostPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
+ PreStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = 0)
- : PostStmt(S, PostPurgeDeadSymbolsKind, L, tag) {}
+ : PostStmt(S, PreStmtPurgeDeadSymbolsKind, L, tag) { }
static bool classof(const ProgramPoint* Location) {
- return Location->getKind() == PostPurgeDeadSymbolsKind;
+ return Location->getKind() == PreStmtPurgeDeadSymbolsKind;
+ }
+};
+
+/// \class Represents a point after we ran remove dead bindings AFTER
+/// processing the given statement.
+class PostStmtPurgeDeadSymbols : public PostStmt {
+public:
+ PostStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
+ const ProgramPointTag *tag = 0)
+ : PostStmt(S, PostStmtPurgeDeadSymbolsKind, L, tag) { }
+
+ static bool classof(const ProgramPoint* Location) {
+ return Location->getKind() == PostStmtPurgeDeadSymbolsKind;
}
};
@@ -383,6 +408,7 @@ public:
}
};
+/// \class Represents a point when we begin processing an inlined call.
class CallEnter : public StmtPoint {
public:
CallEnter(const Stmt *stmt, const StackFrameContext *calleeCtx,
@@ -402,14 +428,38 @@ public:
}
};
-class CallExit : public StmtPoint {
+/// \class Represents a point when we start the call exit sequence (for
+/// inlined call).
+///
+/// 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:
+/// - CallExitBegin
+/// - Bind the return value
+/// - Run Remove dead bindings (to clean up the dead symbols from the callee).
+/// - CallExitEnd
+class CallExitBegin : public StmtPoint {
+public:
+ // CallExitBegin uses the callee's location context.
+ CallExitBegin(const Stmt *S, const LocationContext *L)
+ : StmtPoint(S, 0, CallExitBeginKind, L, 0) {}
+
+ static bool classof(const ProgramPoint *Location) {
+ return Location->getKind() == CallExitBeginKind;
+ }
+};
+
+/// \class Represents a point when we finish the call exit sequence (for
+/// inlined call).
+/// \sa CallExitBegin
+class CallExitEnd : public StmtPoint {
public:
- // CallExit uses the callee's location context.
- CallExit(const Stmt *S, const LocationContext *L)
- : StmtPoint(S, 0, CallExitKind, L, 0) {}
+ // CallExitEnd uses the caller's location context.
+ CallExitEnd(const Stmt *S, const LocationContext *L)
+ : StmtPoint(S, 0, CallExitEndKind, L, 0) {}
static bool classof(const ProgramPoint *Location) {
- return Location->getKind() == CallExitKind;
+ return Location->getKind() == CallExitEndKind;
}
};
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
index 5a8a1c78ca..730a558b74 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
@@ -362,7 +362,7 @@ public:
/// \brief Interface for classes constructing Stack hints.
///
/// If a PathDiagnosticEvent occurs in a different frame than the final
-/// diagnostic the hints can be used to summarise the effect of the call.
+/// diagnostic the hints can be used to summarize the effect of the call.
class StackHintGenerator {
public:
virtual ~StackHintGenerator() = 0;
@@ -510,7 +510,7 @@ public:
}
static PathDiagnosticCallPiece *construct(const ExplodedNode *N,
- const CallExit &CE,
+ const CallExitEnd &CE,
const SourceManager &SM);
static PathDiagnosticCallPiece *construct(PathPieces &pieces,
diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h
index d215f992e6..252d36d69b 100644
--- a/include/clang/StaticAnalyzer/Core/CheckerManager.h
+++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h
@@ -272,7 +272,8 @@ public:
void runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SymbolReaper &SymReaper, const Stmt *S,
- ExprEngine &Eng);
+ ExprEngine &Eng,
+ ProgramPoint::Kind K);
/// \brief True if at least one checker wants to check region changes.
bool wantsRegionChangeUpdate(ProgramStateRef state);
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
index f5984021d8..5bd566ff63 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
@@ -104,7 +104,7 @@ private:
CoreEngine(const CoreEngine&); // Do not implement.
CoreEngine& operator=(const CoreEngine&);
- ExplodedNode *generateCallExitNode(ExplodedNode *N);
+ ExplodedNode *generateCallExitBeginNode(ExplodedNode *N);
public:
/// Construct a CoreEngine object to analyze the provided CFG using
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 2a21a03e9a..73f39b1bce 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -151,6 +151,25 @@ public:
ExplodedGraph& getGraph() { return G; }
const ExplodedGraph& getGraph() const { return G; }
+ /// \brief Run the analyzer's garbage collection - remove dead symbols and
+ /// bindings.
+ ///
+ /// \param Node - The predecessor node, from which the processing should
+ /// start.
+ /// \param Out - The returned set of output nodes.
+ /// \param ReferenceStmt - Run garbage collection using the symbols,
+ /// which are live before the given statement.
+ /// \param LC - The location context of the ReferenceStmt.
+ /// \param DiagnosticStmt - the statement used to associate the diagnostic
+ /// message, if any warnings should occur while removing the dead (leaks
+ /// are usually reported here).
+ /// \param K - In some cases it is possible to use PreStmt kind. (Do
+ /// not use it unless you know what you are doing.)
+ void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out,
+ const Stmt *ReferenceStmt, const LocationContext *LC,
+ const Stmt *DiagnosticStmt,
+ ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind);
+
/// processCFGElement - Called by CoreEngine. Used to generate new successor
/// nodes by processing the 'effects' of a CFG element.
void processCFGElement(const CFGElement E, ExplodedNode *Pred,
@@ -199,7 +218,8 @@ public:
/// Generate the entry node of the callee.
void processCallEnter(CallEnter CE, ExplodedNode *Pred);
- /// Generate the first post callsite node.
+ /// Generate the sequence of nodes that simulate the call exit and the post
+ /// visit for CallExpr.
void processCallExit(ExplodedNode *Pred);
/// Called by CoreEngine when the analysis worklist has terminated.
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
index 69f5e7ca6b..4d329d2f10 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
@@ -553,6 +553,7 @@ public:
BasicValueFactory &getBasicVals() { return BV; }
};
+/// \class A class responsible for cleaning up unused symbols.
class SymbolReaper {
enum SymbolStatus {
NotProcessed,
@@ -576,6 +577,11 @@ class SymbolReaper {
llvm::DenseMap<const MemRegion *, unsigned> includedRegionCache;
public:
+ /// \brief Construct a reaper object, which removes everything which is not
+ /// live before we execute statement s in the given location context.
+ ///
+ /// If the statement is NULL, everything is this and parent contexts is
+ /// considered live.
SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr,
StoreManager &storeMgr)
: LCtx(ctx), Loc(s), SymMgr(symmgr), reapedStore(0, storeMgr) {}
diff --git a/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp
index 3f711b447a..7d67e8a91c 100644
--- a/lib/Analysis/ProgramPoint.cpp
+++ b/lib/Analysis/ProgramPoint.cpp
@@ -36,8 +36,10 @@ ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K,
return PreStore(S, LC, tag);
case ProgramPoint::PostLValueKind:
return PostLValue(S, LC, tag);
- case ProgramPoint::PostPurgeDeadSymbolsKind:
- return PostPurgeDeadSymbols(S, LC, tag);
+ case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
+ return PostStmtPurgeDeadSymbols(S, LC, tag);
+ case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
+ return PreStmtPurgeDeadSymbols(S, LC, tag);
}
}
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(getCoreEngi