Permalink
Browse files

[analyzer] Mark symbol values as dead in the environment.

This allows us to properly remove dead bindings at the end of the top-level
stack frame, using the ReturnStmt, if there is one, to keep the return value
live. This in turn removes the need for a check::EndPath callback in leak
checkers.

This does cause some changes in the path notes for leak checkers. Previously,
a leak would be reported at the location of the closing brace in a function.
Now, it gets reported at the last statement. This matches the way leaks are
currently reported for inlined functions, but is less than ideal for both.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@168066 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information...
1 parent 3d145f6 commit 84c484545c5906ba55143e212b4a5275ab55889f @jrose-apple jrose-apple committed Nov 15, 2012
@@ -154,26 +154,33 @@ class ExprEngine : public SubEngine {
const ExplodedGraph& getGraph() const { return G; }
/// \brief Run the analyzer's garbage collection - remove dead symbols and
- /// bindings.
+ /// bindings from the state.
///
- /// \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.)
- /// If the ReferenceStmt is NULL, everything is this and parent contexts is
- /// considered live.
- /// If the stack frame context is NULL, everything on stack is considered
- /// dead.
+ /// Checkers can participate in this process with two callbacks:
+ /// \c checkLiveSymbols and \c checkDeadSymbols. See the CheckerDocumentation
+ /// class for more information.
+ ///
+ /// \param Node The predecessor node, from which the processing should start.
+ /// \param Out The returned set of output nodes.
+ /// \param ReferenceStmt The statement which is about to be processed.
+ /// Everything needed for this statement should be considered live.
+ /// A null statement means that everything in child LocationContexts
+ /// is dead.
+ /// \param LC The location context of the \p ReferenceStmt. A null location
+ /// context means that we have reached the end of analysis and that
+ /// all statements and local variables should be considered dead.
+ /// \param DiagnosticStmt Used as a location for any warnings that should
+ /// occur while removing the dead (e.g. leaks). By default, the
+ /// \p ReferenceStmt is used.
+ /// \param K Denotes whether this is a pre- or post-statement purge. This
+ /// must only be ProgramPoint::PostStmtPurgeDeadSymbolsKind if an
+ /// entire location context is being cleared, in which case the
+ /// \p ReferenceStmt must either be a ReturnStmt or \c NULL. Otherwise,
+ /// it must be ProgramPoint::PreStmtPurgeDeadSymbolsKind (the default)
+ /// and \p ReferenceStmt must be valid (non-null).
void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out,
- const Stmt *ReferenceStmt, const StackFrameContext *LC,
- const Stmt *DiagnosticStmt,
+ const Stmt *ReferenceStmt, const LocationContext *LC,
+ const Stmt *DiagnosticStmt = 0,
ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind);
/// processCFGElement - Called by CoreEngine. Used to generate new successor
@@ -241,6 +241,10 @@ EnvironmentManager::removeDeadBindings(Environment Env,
// Mark all symbols in the block expr's value live.
RSScaner.scan(X);
continue;
+ } else {
+ SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
+ for (; SI != SE; ++SI)
+ SymReaper.maybeDead(*SI);
}
}
@@ -268,22 +268,39 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
const Stmt *ReferenceStmt,
- const StackFrameContext *LC,
+ const LocationContext *LC,
const Stmt *DiagnosticStmt,
ProgramPoint::Kind K) {
assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind ||
- ReferenceStmt == 0)
+ ReferenceStmt == 0 || isa<ReturnStmt>(ReferenceStmt))
&& "PostStmt is not generally supported by the SymbolReaper yet");
+ assert(LC && "Must pass the current (or expiring) LocationContext");
+
+ if (!DiagnosticStmt) {
+ DiagnosticStmt = ReferenceStmt;
+ assert(DiagnosticStmt && "Required for clearing a LocationContext");
+ }
+
NumRemoveDeadBindings++;
CleanedState = Pred->getState();
- SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager());
+
+ // LC is the location context being destroyed, but SymbolReaper wants a
+ // location context that is still live. (If this is the top-level stack
+ // frame, this will be null.)
+ if (!ReferenceStmt) {
+ assert(K == ProgramPoint::PostStmtPurgeDeadSymbolsKind &&
+ "Use PostStmtPurgeDeadSymbolsKind for clearing a LocationContext");
+ LC = LC->getParent();
+ }
+
+ const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : 0;
+ SymbolReaper SymReaper(SFC, 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.
@@ -345,8 +362,7 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
EntryNode = Pred;
ExplodedNodeSet CleanedStates;
if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){
- removeDead(EntryNode, CleanedStates, currStmt,
- Pred->getStackFrame(), currStmt);
+ removeDead(EntryNode, CleanedStates, currStmt, Pred->getLocationContext());
} else
CleanedStates.Add(EntryNode);
@@ -168,27 +168,24 @@ static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy,
void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
- NodeBuilder Bldr(Pred, Dst, BC);
-
// 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(Pred);
if (!Blk || !LastSt) {
+ Dst.Add(Pred);
return;
}
-
- // If the last statement is return, everything it references should stay live.
- if (isa<ReturnStmt>(LastSt))
- return;
// Here, we call the Symbol Reaper with 0 stack context telling it to clean up
// everything on the stack. We use LastStmt as a diagnostic statement, with
- // which the PreStmtPurgeDead point will be associated.
- currBldrCtx = &BC;
- removeDead(Pred, Dst, 0, 0, LastSt,
+ // which the program point will be associated. However, we only want to use
+ // LastStmt as a reference for what to clean up if it's a ReturnStmt;
+ // otherwise, everything is dead.
+ SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC);
+ removeDead(Pred, Dst, dyn_cast<ReturnStmt>(LastSt),
+ Pred->getLocationContext(), LastSt,
ProgramPoint::PostStmtPurgeDeadSymbolsKind);
- currBldrCtx = 0;
}
static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call,
@@ -290,11 +287,11 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode);
currBldrCtx = &Ctx;
- // Here, we call the Symbol Reaper with 0 statement and caller location
+ // Here, we call the Symbol Reaper with 0 statement and callee 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,
+ // (and its children). We use LastSt as a diagnostic statement, which
+ // which the program point will be associated.
+ removeDead(BindedRetNode, CleanedNodes, 0, calleeCtx, LastSt,
ProgramPoint::PostStmtPurgeDeadSymbolsKind);
currBldrCtx = 0;
} else {
Oops, something went wrong.

0 comments on commit 84c4845

Please sign in to comment.