forked from OSchip/llvm-project
[analyzer] Remove checker V1 registration and running from ExprEngine.
llvm-svn: 126724
This commit is contained in:
parent
3b1459b810
commit
d665807901
|
@ -74,39 +74,6 @@ class ExprEngine : public SubEngine {
|
|||
Selector* NSExceptionInstanceRaiseSelectors;
|
||||
Selector RaiseSel;
|
||||
|
||||
enum CallbackKind {
|
||||
PreVisitStmtCallback,
|
||||
PostVisitStmtCallback,
|
||||
processAssumeCallback,
|
||||
EvalRegionChangesCallback
|
||||
};
|
||||
|
||||
typedef uint32_t CallbackTag;
|
||||
|
||||
/// GetCallbackTag - Create a tag for a certain kind of callback. The 'Sub'
|
||||
/// argument can be used to differentiate callbacks that depend on another
|
||||
/// value from a small set of possibilities, such as statement classes.
|
||||
static inline CallbackTag GetCallbackTag(CallbackKind K, uint32_t Sub = 0) {
|
||||
assert(Sub == ((Sub << 8) >> 8) && "Tag sub-kind must fit into 24 bits");
|
||||
return K | (Sub << 8);
|
||||
}
|
||||
|
||||
typedef llvm::DenseMap<void *, unsigned> CheckerMap;
|
||||
typedef std::vector<std::pair<void *, Checker*> > CheckersOrdered;
|
||||
typedef llvm::DenseMap<CallbackTag, CheckersOrdered *> CheckersOrderedCache;
|
||||
|
||||
/// A registration map from checker tag to the index into the
|
||||
/// ordered checkers vector.
|
||||
CheckerMap CheckerM;
|
||||
|
||||
/// An ordered vector of checkers that are called when evaluating
|
||||
/// various expressions and statements.
|
||||
CheckersOrdered Checkers;
|
||||
|
||||
/// A map used for caching the checkers that respond to the callback for
|
||||
/// a particular callback tag.
|
||||
CheckersOrderedCache COCache;
|
||||
|
||||
/// The BugReporter associated with this engine. It is important that
|
||||
/// this object be placed at the very end of member variables so that its
|
||||
/// destructor is called before the rest of the ExprEngine is destroyed.
|
||||
|
@ -165,21 +132,6 @@ public:
|
|||
ExplodedGraph& getGraph() { return G; }
|
||||
const ExplodedGraph& getGraph() const { return G; }
|
||||
|
||||
template <typename CHECKER>
|
||||
void registerCheck(CHECKER *check) {
|
||||
unsigned entry = Checkers.size();
|
||||
void *tag = CHECKER::getTag();
|
||||
Checkers.push_back(std::make_pair(tag, check));
|
||||
CheckerM[tag] = entry;
|
||||
}
|
||||
|
||||
Checker *lookupChecker(void *tag) const;
|
||||
|
||||
template <typename CHECKER>
|
||||
CHECKER *getChecker() const {
|
||||
return static_cast<CHECKER*>(lookupChecker(CHECKER::getTag()));
|
||||
}
|
||||
|
||||
/// processCFGElement - Called by CoreEngine. Used to generate new successor
|
||||
/// nodes by processing the 'effects' of a CFG element.
|
||||
void processCFGElement(const CFGElement E, StmtNodeBuilder& builder);
|
||||
|
@ -281,27 +233,6 @@ public:
|
|||
ProgramPoint::Kind K = ProgramPoint::PostStmtKind,
|
||||
const void *tag = 0);
|
||||
|
||||
/// CheckerVisit - Dispatcher for performing checker-specific logic
|
||||
/// at specific statements.
|
||||
void CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
|
||||
CallbackKind Kind);
|
||||
|
||||
void CheckerVisitObjCMessage(const ObjCMessage &msg, ExplodedNodeSet &Dst,
|
||||
ExplodedNodeSet &Src, bool isPrevisit);
|
||||
|
||||
bool CheckerEvalCall(const CallExpr *CE,
|
||||
ExplodedNodeSet &Dst,
|
||||
ExplodedNode *Pred);
|
||||
|
||||
void CheckerEvalNilReceiver(const ObjCMessage &msg,
|
||||
ExplodedNodeSet &Dst,
|
||||
const GRState *state,
|
||||
ExplodedNode *Pred);
|
||||
|
||||
void CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst,
|
||||
ExplodedNodeSet &Src, SVal location, SVal val,
|
||||
bool isPrevisit);
|
||||
|
||||
/// Visit - Transfer function logic for all statements. Dispatches to
|
||||
/// other functions that handle specific kinds of statements.
|
||||
void Visit(const Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst);
|
||||
|
|
|
@ -59,248 +59,6 @@ static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
|
|||
return Ctx.Selectors.getSelector(0, &II);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Checker worklist routines.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst,
|
||||
ExplodedNodeSet &Src, CallbackKind Kind) {
|
||||
|
||||
// Determine if we already have a cached 'CheckersOrdered' vector
|
||||
// specifically tailored for the provided <CallbackKind, Stmt kind>. This
|
||||
// can reduce the number of checkers actually called.
|
||||
CheckersOrdered *CO = &Checkers;
|
||||
llvm::OwningPtr<CheckersOrdered> NewCO;
|
||||
|
||||
// The cache key is made up of the and the callback kind (pre- or post-visit)
|
||||
// and the statement kind.
|
||||
CallbackTag K = GetCallbackTag(Kind, S->getStmtClass());
|
||||
|
||||
CheckersOrdered *& CO_Ref = COCache[K];
|
||||
|
||||
if (!CO_Ref) {
|
||||
// If we have no previously cached CheckersOrdered vector for this
|
||||
// statement kind, then create one.
|
||||
NewCO.reset(new CheckersOrdered);
|
||||
}
|
||||
else {
|
||||
// Use the already cached set.
|
||||
CO = CO_Ref;
|
||||
}
|
||||
|
||||
if (CO->empty()) {
|
||||
// If there are no checkers, just delegate to the checker manager.
|
||||
getCheckerManager().runCheckersForStmt(Kind == PreVisitStmtCallback,
|
||||
Dst, Src, S, *this);
|
||||
return;
|
||||
}
|
||||
|
||||
ExplodedNodeSet CheckersV1Dst;
|
||||
ExplodedNodeSet Tmp;
|
||||
ExplodedNodeSet *PrevSet = &Src;
|
||||
unsigned checkersEvaluated = 0;
|
||||
|
||||
for (CheckersOrdered::iterator I=CO->begin(), E=CO->end(); I!=E; ++I) {
|
||||
// If all nodes are sunk, bail out early.
|
||||
if (PrevSet->empty())
|
||||
break;
|
||||
ExplodedNodeSet *CurrSet = 0;
|
||||
if (I+1 == E)
|
||||
CurrSet = &CheckersV1Dst;
|
||||
else {
|
||||
CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp;
|
||||
CurrSet->clear();
|
||||
}
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
bool respondsToCallback = true;
|
||||
|
||||
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
|
||||
NI != NE; ++NI) {
|
||||
|
||||
checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag,
|
||||
Kind == PreVisitStmtCallback, respondsToCallback);
|
||||
|
||||
}
|
||||
|
||||
PrevSet = CurrSet;
|
||||
|
||||
if (NewCO.get()) {
|
||||
++checkersEvaluated;
|
||||
if (respondsToCallback)
|
||||
NewCO->push_back(*I);
|
||||
}
|
||||
}
|
||||
|
||||
// If we built NewCO, check if we called all the checkers. This is important
|
||||
// so that we know that we accurately determined the entire set of checkers
|
||||
// that responds to this callback. Note that 'checkersEvaluated' might
|
||||
// not be the same as Checkers.size() if one of the Checkers generates
|
||||
// a sink node.
|
||||
if (NewCO.get() && checkersEvaluated == Checkers.size())
|
||||
CO_Ref = NewCO.take();
|
||||
|
||||
// Don't autotransition. The CheckerContext objects should do this
|
||||
// automatically.
|
||||
|
||||
getCheckerManager().runCheckersForStmt(Kind == PreVisitStmtCallback,
|
||||
Dst, CheckersV1Dst, S, *this);
|
||||
}
|
||||
|
||||
void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg,
|
||||
ExplodedNodeSet &Dst,
|
||||
ExplodedNodeSet &Src,
|
||||
bool isPrevisit) {
|
||||
|
||||
if (Checkers.empty()) {
|
||||
getCheckerManager().runCheckersForObjCMessage(isPrevisit, Dst, Src, msg,
|
||||
*this);
|
||||
return;
|
||||
}
|
||||
|
||||
ExplodedNodeSet CheckersV1Dst;
|
||||
ExplodedNodeSet Tmp;
|
||||
ExplodedNodeSet *PrevSet = &Src;
|
||||
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
|
||||
{
|
||||
ExplodedNodeSet *CurrSet = 0;
|
||||
if (I+1 == E)
|
||||
CurrSet = &CheckersV1Dst;
|
||||
else {
|
||||
CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp;
|
||||
CurrSet->clear();
|
||||
}
|
||||
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
|
||||
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
|
||||
NI != NE; ++NI)
|
||||
checker->GR_visitObjCMessage(*CurrSet, *Builder, *this, msg,
|
||||
*NI, tag, isPrevisit);
|
||||
|
||||
// Update which NodeSet is the current one.
|
||||
PrevSet = CurrSet;
|
||||
}
|
||||
|
||||
getCheckerManager().runCheckersForObjCMessage(isPrevisit, Dst, CheckersV1Dst,
|
||||
msg, *this);
|
||||
}
|
||||
|
||||
void ExprEngine::CheckerEvalNilReceiver(const ObjCMessage &msg,
|
||||
ExplodedNodeSet &Dst,
|
||||
const GRState *state,
|
||||
ExplodedNode *Pred) {
|
||||
bool evaluated = false;
|
||||
ExplodedNodeSet DstTmp;
|
||||
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) {
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
|
||||
if (checker->GR_evalNilReceiver(DstTmp, *Builder, *this, msg, Pred, state,
|
||||
tag)) {
|
||||
evaluated = true;
|
||||
break;
|
||||
} else
|
||||
// The checker didn't evaluate the expr. Restore the Dst.
|
||||
DstTmp.clear();
|
||||
}
|
||||
|
||||
if (evaluated)
|
||||
Dst.insert(DstTmp);
|
||||
else
|
||||
Dst.insert(Pred);
|
||||
}
|
||||
|
||||
// CheckerEvalCall returns true if one of the checkers processed the node.
|
||||
// This may return void when all call evaluation logic goes to some checker
|
||||
// in the future.
|
||||
bool ExprEngine::CheckerEvalCall(const CallExpr *CE,
|
||||
ExplodedNodeSet &Dst,
|
||||
ExplodedNode *Pred) {
|
||||
bool evaluated = false;
|
||||
ExplodedNodeSet DstTmp;
|
||||
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) {
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
|
||||
if (checker->GR_evalCallExpr(DstTmp, *Builder, *this, CE, Pred, tag)) {
|
||||
evaluated = true;
|
||||
break;
|
||||
} else
|
||||
// The checker didn't evaluate the expr. Restore the DstTmp set.
|
||||
DstTmp.clear();
|
||||
}
|
||||
|
||||
if (evaluated) {
|
||||
Dst.insert(DstTmp);
|
||||
return evaluated;
|
||||
}
|
||||
|
||||
class DefaultEval : public GraphExpander {
|
||||
bool &Evaluated;
|
||||
public:
|
||||
DefaultEval(bool &evaluated) : Evaluated(evaluated) { }
|
||||
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
|
||||
Evaluated = false;
|
||||
Dst.insert(Pred);
|
||||
}
|
||||
};
|
||||
|
||||
evaluated = true;
|
||||
DefaultEval defaultEval(evaluated);
|
||||
getCheckerManager().runCheckersForEvalCall(Dst, Pred, CE, *this,
|
||||
&defaultEval);
|
||||
return evaluated;
|
||||
}
|
||||
|
||||
// FIXME: This is largely copy-paste from CheckerVisit(). Need to
|
||||
// unify.
|
||||
void ExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst,
|
||||
ExplodedNodeSet &Src, SVal location,
|
||||
SVal val, bool isPrevisit) {
|
||||
|
||||
if (Checkers.empty()) {
|
||||
getCheckerManager().runCheckersForBind(Dst, Src, location, val, StoreE,
|
||||
*this);
|
||||
return;
|
||||
}
|
||||
|
||||
ExplodedNodeSet CheckerV1Tmp;
|
||||
ExplodedNodeSet Tmp;
|
||||
ExplodedNodeSet *PrevSet = &Src;
|
||||
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
|
||||
{
|
||||
ExplodedNodeSet *CurrSet = 0;
|
||||
if (I+1 == E)
|
||||
CurrSet = &CheckerV1Tmp;
|
||||
else {
|
||||
CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp;
|
||||
CurrSet->clear();
|
||||
}
|
||||
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
|
||||
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
|
||||
NI != NE; ++NI)
|
||||
checker->GR_VisitBind(*CurrSet, *Builder, *this, StoreE,
|
||||
*NI, tag, location, val, isPrevisit);
|
||||
|
||||
// Update which NodeSet is the current one.
|
||||
PrevSet = CurrSet;
|
||||
}
|
||||
|
||||
getCheckerManager().runCheckersForBind(Dst, CheckerV1Tmp, location, val,
|
||||
StoreE, *this);
|
||||
|
||||
// Don't autotransition. The CheckerContext objects should do this
|
||||
// automatically.
|
||||
}
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Engine construction and deletion.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -333,14 +91,6 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf)
|
|||
ExprEngine::~ExprEngine() {
|
||||
BR.FlushReports();
|
||||
delete [] NSExceptionInstanceRaiseSelectors;
|
||||
|
||||
// Delete the set of checkers.
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I)
|
||||
delete I->second;
|
||||
|
||||
for (CheckersOrderedCache::iterator I=COCache.begin(), E=COCache.end();
|
||||
I!=E;++I)
|
||||
delete I->second;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -415,52 +165,6 @@ const GRState* ExprEngine::getInitialState(const LocationContext *InitLoc) {
|
|||
/// logic for handling assumptions on symbolic values.
|
||||
const GRState *ExprEngine::processAssume(const GRState *state, SVal cond,
|
||||
bool assumption) {
|
||||
// Determine if we already have a cached 'CheckersOrdered' vector
|
||||
// specifically tailored for processing assumptions. This
|
||||
// can reduce the number of checkers actually called.
|
||||
CheckersOrdered *CO = &Checkers;
|
||||
llvm::OwningPtr<CheckersOrdered> NewCO;
|
||||
|
||||
CallbackTag K = GetCallbackTag(processAssumeCallback);
|
||||
CheckersOrdered *& CO_Ref = COCache[K];
|
||||
|
||||
if (!CO_Ref) {
|
||||
// If we have no previously cached CheckersOrdered vector for this
|
||||
// statement kind, then create one.
|
||||
NewCO.reset(new CheckersOrdered);
|
||||
}
|
||||
else {
|
||||
// Use the already cached set.
|
||||
CO = CO_Ref;
|
||||
}
|
||||
|
||||
if (!CO->empty()) {
|
||||
// Let the checkers have a crack at the assume before the transfer functions
|
||||
// get their turn.
|
||||
for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I!=E; ++I) {
|
||||
|
||||
// If any checker declares the state infeasible (or if it starts that
|
||||
// way), bail out.
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
Checker *C = I->second;
|
||||
bool respondsToCallback = true;
|
||||
|
||||
state = C->evalAssume(state, cond, assumption, &respondsToCallback);
|
||||
|
||||
// Check if we're building the cache of checkers that care about
|
||||
// assumptions.
|
||||
if (NewCO.get() && respondsToCallback)
|
||||
NewCO->push_back(*I);
|
||||
}
|
||||
|
||||
// If we got through all the checkers, and we built a list of those that
|
||||
// care about assumptions, save it.
|
||||
if (NewCO.get())
|
||||
CO_Ref = NewCO.take();
|
||||
}
|
||||
|
||||
state = getCheckerManager().runCheckersForEvalAssume(state, cond, assumption);
|
||||
|
||||
// If the state is infeasible at this point, bail out.
|
||||
|
@ -471,18 +175,6 @@ const GRState *ExprEngine::processAssume(const GRState *state, SVal cond,
|
|||
}
|
||||
|
||||
bool ExprEngine::wantsRegionChangeUpdate(const GRState* state) {
|
||||
CallbackTag K = GetCallbackTag(EvalRegionChangesCallback);
|
||||
CheckersOrdered *CO = COCache[K];
|
||||
|
||||
if (!CO)
|
||||
CO = &Checkers;
|
||||
|
||||
for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) {
|
||||
Checker *C = I->second;
|
||||
if (C->wantsRegionChangeUpdate(state))
|
||||
return true;
|
||||
}
|
||||
|
||||
return getCheckerManager().wantsRegionChangeUpdate(state);
|
||||
}
|
||||
|
||||
|
@ -490,60 +182,10 @@ const GRState *
|
|||
ExprEngine::processRegionChanges(const GRState *state,
|
||||
const MemRegion * const *Begin,
|
||||
const MemRegion * const *End) {
|
||||
// FIXME: Most of this method is copy-pasted from processAssume.
|
||||
|
||||
// Determine if we already have a cached 'CheckersOrdered' vector
|
||||
// specifically tailored for processing region changes. This
|
||||
// can reduce the number of checkers actually called.
|
||||
CheckersOrdered *CO = &Checkers;
|
||||
llvm::OwningPtr<CheckersOrdered> NewCO;
|
||||
|
||||
CallbackTag K = GetCallbackTag(EvalRegionChangesCallback);
|
||||
CheckersOrdered *& CO_Ref = COCache[K];
|
||||
|
||||
if (!CO_Ref) {
|
||||
// If we have no previously cached CheckersOrdered vector for this
|
||||
// callback, then create one.
|
||||
NewCO.reset(new CheckersOrdered);
|
||||
}
|
||||
else {
|
||||
// Use the already cached set.
|
||||
CO = CO_Ref;
|
||||
}
|
||||
|
||||
// If there are no checkers, just delegate to the checker manager.
|
||||
if (CO->empty())
|
||||
return getCheckerManager().runCheckersForRegionChanges(state, Begin, End);
|
||||
|
||||
for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) {
|
||||
// If any checker declares the state infeasible (or if it starts that way),
|
||||
// bail out.
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
Checker *C = I->second;
|
||||
bool respondsToCallback = true;
|
||||
|
||||
state = C->EvalRegionChanges(state, Begin, End, &respondsToCallback);
|
||||
|
||||
// See if we're building a cache of checkers that care about region changes.
|
||||
if (NewCO.get() && respondsToCallback)
|
||||
NewCO->push_back(*I);
|
||||
}
|
||||
|
||||
// If we got through all the checkers, and we built a list of those that
|
||||
// care about region changes, save it.
|
||||
if (NewCO.get())
|
||||
CO_Ref = NewCO.take();
|
||||
|
||||
return getCheckerManager().runCheckersForRegionChanges(state, Begin, End);
|
||||
}
|
||||
|
||||
void ExprEngine::processEndWorklist(bool hasWorkRemaining) {
|
||||
for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end();
|
||||
I != E; ++I) {
|
||||
I->second->VisitEndAnalysis(G, BR, *this);
|
||||
}
|
||||
getCheckerManager().runCheckersForEndAnalysis(G, BR, *this);
|
||||
}
|
||||
|
||||
|
@ -585,13 +227,6 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
|
|||
|
||||
if (AMgr.shouldPurgeDead()) {
|
||||
const GRState *St = EntryNode->getState();
|
||||
|
||||
for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end();
|
||||
I != E; ++I) {
|
||||
Checker *checker = I->second;
|
||||
checker->MarkLiveSymbols(St, SymReaper);
|
||||
}
|
||||
|
||||
getCheckerManager().runCheckersForLiveSymbols(St, SymReaper);
|
||||
|
||||
const StackFrameContext *SFC = LC->getCurrentStackFrame();
|
||||
|
@ -617,33 +252,7 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
|
|||
getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode,
|
||||
CleanedState, SymReaper);
|
||||
|
||||
ExplodedNodeSet checkersV1Tmp;
|
||||
if (Checkers.empty())
|
||||
checkersV1Tmp.insert(Tmp2);
|
||||
else {
|
||||
ExplodedNodeSet Tmp3;
|
||||
ExplodedNodeSet *SrcSet = &Tmp2;
|
||||
for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end();
|
||||
I != E; ++I) {
|
||||
ExplodedNodeSet *DstSet = 0;
|
||||
if (I+1 == E)
|
||||
DstSet = &checkersV1Tmp;
|
||||
else {
|
||||
DstSet = (SrcSet == &Tmp2) ? &Tmp3 : &Tmp2;
|
||||
DstSet->clear();
|
||||
}
|
||||
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end();
|
||||
NI != NE; ++NI)
|
||||
checker->GR_evalDeadSymbols(*DstSet, *Builder, *this, currentStmt,
|
||||
*NI, SymReaper, tag);
|
||||
SrcSet = DstSet;
|
||||
}
|
||||
}
|
||||
|
||||
getCheckerManager().runCheckersForDeadSymbols(Tmp, checkersV1Tmp,
|
||||
getCheckerManager().runCheckersForDeadSymbols(Tmp, Tmp2,
|
||||
SymReaper, currentStmt, *this);
|
||||
|
||||
if (!Builder->BuildSinks && !Builder->hasGeneratedNode)
|
||||
|
@ -1283,12 +892,6 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term,
|
|||
Condition->getLocStart(),
|
||||
"Error evaluating branch");
|
||||
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) {
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
checker->VisitBranchCondition(builder, *this, Condition, tag);
|
||||
}
|
||||
|
||||
getCheckerManager().runCheckersForBranchCondition(Condition, builder, *this);
|
||||
|
||||
// If the branch condition is undefined, return;
|
||||
|
@ -1413,12 +1016,6 @@ void ExprEngine::VisitGuardedExpr(const Expr* Ex, const Expr* L,
|
|||
void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) {
|
||||
getTF().evalEndPath(*this, builder);
|
||||
StateMgr.EndPath(builder.getState());
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
EndOfFunctionNodeBuilder B = builder.withCheckerTag(tag);
|
||||
checker->evalEndPath(B, tag, *this);
|
||||
}
|
||||
getCheckerManager().runCheckersForEndPath(builder, *this);
|
||||
}
|
||||
|
||||
|
@ -1638,7 +1235,7 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
|
|||
ProgramPoint::PostLValueKind);
|
||||
|
||||
// Post-visit the BlockExpr.
|
||||
CheckerVisit(BE, Dst, Tmp, PostVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
|
||||
}
|
||||
|
||||
void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
|
||||
|
@ -1695,7 +1292,7 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr* A,
|
|||
ExplodedNodeSet Tmp2;
|
||||
Visit(Idx, *I1, Tmp2); // Evaluate the index.
|
||||
ExplodedNodeSet Tmp3;
|
||||
CheckerVisit(A, Tmp3, Tmp2, PreVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPreStmt(Tmp3, Tmp2, A, *this);
|
||||
|
||||
for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) {
|
||||
const GRState* state = GetState(*I2);
|
||||
|
@ -1756,7 +1353,8 @@ void ExprEngine::evalBind(ExplodedNodeSet& Dst, const Stmt* StoreE,
|
|||
// Do a previsit of the bind.
|
||||
ExplodedNodeSet CheckedSet, Src;
|
||||
Src.Add(Pred);
|
||||
CheckerVisitBind(StoreE, CheckedSet, Src, location, Val, true);
|
||||
getCheckerManager().runCheckersForBind(CheckedSet, Src, location, Val, StoreE,
|
||||
*this);
|
||||
|
||||
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
|
||||
I!=E; ++I) {
|
||||
|
@ -1929,53 +1527,23 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S,
|
|||
return;
|
||||
}
|
||||
|
||||
if (Checkers.empty()) {
|
||||
ExplodedNodeSet Src;
|
||||
if (Builder->GetState(Pred) == state) {
|
||||
Src.Add(Pred);
|
||||
} else {
|
||||
// Associate this new state with an ExplodedNode.
|
||||
Src.Add(Builder->generateNode(S, state, Pred));
|
||||
}
|
||||
getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S,
|
||||
*this);
|
||||
return;
|
||||
}
|
||||
|
||||
ExplodedNodeSet Src;
|
||||
Src.Add(Pred);
|
||||
ExplodedNodeSet CheckersV1Dst;
|
||||
ExplodedNodeSet Tmp;
|
||||
ExplodedNodeSet *PrevSet = &Src;
|
||||
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
|
||||
{
|
||||
ExplodedNodeSet *CurrSet = 0;
|
||||
if (I+1 == E)
|
||||
CurrSet = &CheckersV1Dst;
|
||||
else {
|
||||
CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp;
|
||||
CurrSet->clear();
|
||||
}
|
||||
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
|
||||
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
|
||||
NI != NE; ++NI) {
|
||||
// Use the 'state' argument only when the predecessor node is the
|
||||
// same as Pred. This allows us to catch updates to the state.
|
||||
checker->GR_visitLocation(*CurrSet, *Builder, *this, S, *NI,
|
||||
*NI == Pred ? state : GetState(*NI),
|
||||
location, tag, isLoad);
|
||||
}
|
||||
|
||||
// Update which NodeSet is the current one.
|
||||
PrevSet = CurrSet;
|
||||
if (Builder->GetState(Pred) == state) {
|
||||
Src.Add(Pred);
|
||||
} else {
|
||||
// Associate this new state with an ExplodedNode.
|
||||
// FIXME: If I pass null tag, the graph is incorrect, e.g for
|
||||
// int *p;
|
||||
// p = 0;
|
||||
// *p = 0xDEADBEEF;
|
||||
// "p = 0" is not noted as "Null pointer value stored to 'p'" but
|
||||
// instead "int *p" is noted as
|
||||
// "Variable 'p' initialized to a null pointer value"
|
||||
ExplodedNode *N = Builder->generateNode(S, state, Pred, this);
|
||||
Src.Add(N ? N : Pred);
|
||||
}
|
||||
|
||||
getCheckerManager().runCheckersForLocation(Dst, CheckersV1Dst, location,
|
||||
isLoad, S, *this);
|
||||
getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S,
|
||||
*this);
|
||||
}
|
||||
|
||||
bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE,
|
||||
|
@ -2044,58 +1612,59 @@ void ExprEngine::VisitCall(const CallExpr* CE, ExplodedNode* Pred,
|
|||
ExplodedNodeSet DstTmp2;
|
||||
Visit(Callee, *NI, DstTmp2);
|
||||
// Perform the previsit of the CallExpr, storing the results in DstTmp.
|
||||
CheckerVisit(CE, DstTmp, DstTmp2, PreVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPreStmt(DstTmp, DstTmp2, CE, *this);
|
||||
}
|
||||
|
||||
class DefaultEval : public GraphExpander {
|
||||
ExprEngine &Eng;
|
||||
const CallExpr *CE;
|
||||
public:
|
||||
bool Inlined;
|
||||
|
||||
DefaultEval(ExprEngine &eng, const CallExpr *ce)
|
||||
: Eng(eng), CE(ce), Inlined(false) { }
|
||||
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
|
||||
if (Eng.getAnalysisManager().shouldInlineCall() &&
|
||||
Eng.InlineCall(Dst, CE, Pred)) {
|
||||
Inlined = true;
|
||||
} else {
|
||||
StmtNodeBuilder &Builder = Eng.getBuilder();
|
||||
assert(&Builder && "StmtNodeBuilder must be defined.");
|
||||
|
||||
// Dispatch to the plug-in transfer function.
|
||||
unsigned oldSize = Dst.size();
|
||||
SaveOr OldHasGen(Builder.hasGeneratedNode);
|
||||
|
||||
// Dispatch to transfer function logic to handle the call itself.
|
||||
const Expr* Callee = CE->getCallee()->IgnoreParens();
|
||||
const GRState* state = Eng.GetState(Pred);
|
||||
SVal L = state->getSVal(Callee);
|
||||
Eng.getTF().evalCall(Dst, Eng, Builder, CE, L, Pred);
|
||||
|
||||
// Handle the case where no nodes where generated. Auto-generate that
|
||||
// contains the updated state if we aren't generating sinks.
|
||||
if (!Builder.BuildSinks && Dst.size() == oldSize &&
|
||||
!Builder.hasGeneratedNode)
|
||||
Eng.MakeNode(Dst, CE, Pred, state);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Finally, evaluate the function call. We try each of the checkers
|
||||
// to see if the can evaluate the function call.
|
||||
ExplodedNodeSet DstTmp3;
|
||||
DefaultEval defEval(*this, CE);
|
||||
|
||||
for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end();
|
||||
DI != DE; ++DI) {
|
||||
getCheckerManager().runCheckersForEvalCall(DstTmp3, DstTmp, CE,
|
||||
*this, &defEval);
|
||||
|
||||
const GRState* state = GetState(*DI);
|
||||
SVal L = state->getSVal(Callee);
|
||||
|
||||
// FIXME: Add support for symbolic function calls (calls involving
|
||||
// function pointer values that are symbolic).
|
||||
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
|
||||
ExplodedNodeSet DstChecker;
|
||||
|
||||
// If the callee is processed by a checker, skip the rest logic.
|
||||
if (CheckerEvalCall(CE, DstChecker, *DI))
|
||||
DstTmp3.insert(DstChecker);
|
||||
else if (AMgr.shouldInlineCall() && InlineCall(Dst, CE, *DI)) {
|
||||
// Callee is inlined. We shouldn't do post call checking.
|
||||
return;
|
||||
}
|
||||
else {
|
||||
for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(),
|
||||
DE_Checker = DstChecker.end();
|
||||
DI_Checker != DE_Checker; ++DI_Checker) {
|
||||
|
||||
// Dispatch to the plug-in transfer function.
|
||||
unsigned oldSize = DstTmp3.size();
|
||||
SaveOr OldHasGen(Builder->hasGeneratedNode);
|
||||
Pred = *DI_Checker;
|
||||
|
||||
// Dispatch to transfer function logic to handle the call itself.
|
||||
// FIXME: Allow us to chain together transfer functions.
|
||||
assert(Builder && "StmtNodeBuilder must be defined.");
|
||||
getTF().evalCall(DstTmp3, *this, *Builder, CE, L, Pred);
|
||||
|
||||
// Handle the case where no nodes where generated. Auto-generate that
|
||||
// contains the updated state if we aren't generating sinks.
|
||||
if (!Builder->BuildSinks && DstTmp3.size() == oldSize &&
|
||||
!Builder->hasGeneratedNode)
|
||||
MakeNode(DstTmp3, CE, Pred, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Callee is inlined. We shouldn't do post call checking.
|
||||
if (defEval.Inlined)
|
||||
return;
|
||||
|
||||
// Finally, perform the post-condition check of the CallExpr and store
|
||||
// the created nodes in 'Dst'.
|
||||
CheckerVisit(CE, Dst, DstTmp3, PostVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPostStmt(Dst, DstTmp3, CE, *this);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -2187,7 +1756,7 @@ void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
|
|||
// Pre-visit the ObjCAtSynchronizedStmt.
|
||||
ExplodedNodeSet Tmp;
|
||||
Tmp.Add(Pred);
|
||||
CheckerVisit(S, Dst, Tmp, PreVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPreStmt(Dst, Tmp, S, *this);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -2218,7 +1787,7 @@ void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr* Ex,
|
|||
|
||||
// Perform the post-condition check of the ObjCIvarRefExpr and store
|
||||
// the created nodes in 'Dst'.
|
||||
CheckerVisit(Ex, Dst, dstIvar, PostVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -2388,7 +1957,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
|
|||
|
||||
// Handle the previsits checks.
|
||||
ExplodedNodeSet DstPrevisit;
|
||||
CheckerVisitObjCMessage(msg, DstPrevisit, Src, /*isPreVisit=*/true);
|
||||
getCheckerManager().runCheckersForPreObjCMessage(DstPrevisit, Src, msg,*this);
|
||||
|
||||
// Proceed with evaluate the message expression.
|
||||
ExplodedNodeSet dstEval;
|
||||
|
@ -2413,9 +1982,9 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
|
|||
llvm::tie(notNilState, nilState) = state->assume(receiverVal);
|
||||
|
||||
// There are three cases: can be nil or non-nil, must be nil, must be
|
||||
// non-nil. We handle must be nil, and merge the rest two into non-nil.
|
||||
// non-nil. We ignore must be nil, and merge the rest two into non-nil.
|
||||
if (nilState && !notNilState) {
|
||||
CheckerEvalNilReceiver(msg, dstEval, nilState, Pred);
|
||||
dstEval.insert(Pred);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2491,7 +2060,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
|
|||
|
||||
// Finally, perform the post-condition check of the ObjCMessageExpr and store
|
||||
// the created nodes in 'Dst'.
|
||||
CheckerVisitObjCMessage(msg, Dst, dstEval, /*isPreVisit=*/false);
|
||||
getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -2504,7 +2073,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
|
|||
ExplodedNodeSet S1;
|
||||
Visit(Ex, Pred, S1);
|
||||
ExplodedNodeSet S2;
|
||||
CheckerVisit(CastE, S2, S1, PreVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPreStmt(S2, S1, CastE, *this);
|
||||
|
||||
if (CastE->getCastKind() == CK_LValueToRValue ||
|
||||
CastE->getCastKind() == CK_GetObjCProperty) {
|
||||
|
@ -2677,7 +2246,7 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
|
|||
Tmp.Add(Pred);
|
||||
|
||||
ExplodedNodeSet Tmp2;
|
||||
CheckerVisit(DS, Tmp2, Tmp, PreVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPreStmt(Tmp2, Tmp, DS, *this);
|
||||
|
||||
for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) {
|
||||
ExplodedNode *N = *I;
|
||||
|
@ -3238,7 +2807,7 @@ void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
|
|||
}
|
||||
|
||||
ExplodedNodeSet CheckedSet;
|
||||
CheckerVisit(RS, CheckedSet, Src, PreVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPreStmt(CheckedSet, Src, RS, *this);
|
||||
|
||||
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
|
||||
I != E; ++I) {
|
||||
|
@ -3280,7 +2849,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
|
|||
Visit(RHS, *I1, Tmp2);
|
||||
|
||||
ExplodedNodeSet CheckedSet;
|
||||
CheckerVisit(B, CheckedSet, Tmp2, PreVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPreStmt(CheckedSet, Tmp2, B, *this);
|
||||
|
||||
// With both the LHS and RHS evaluated, process the operation itself.
|
||||
|
||||
|
@ -3407,16 +2976,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
|
|||
}
|
||||
}
|
||||
|
||||
CheckerVisit(B, Dst, Tmp3, PostVisitStmtCallback);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Checker registration/lookup.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
Checker *ExprEngine::lookupChecker(void *tag) const {
|
||||
CheckerMap::const_iterator I = CheckerM.find(tag);
|
||||
return (I == CheckerM.end()) ? NULL : Checkers[I->second].second;
|
||||
getCheckerManager().runCheckersForPostStmt(Dst, Tmp3, B, *this);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/StaticAnalyzer/Core/CheckerV2.h"
|
||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
|
@ -20,7 +23,6 @@
|
|||
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
|
||||
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
|
||||
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
|
||||
|
@ -3395,19 +3397,15 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst,
|
|||
|
||||
namespace {
|
||||
class RetainReleaseChecker
|
||||
: public CheckerVisitor<RetainReleaseChecker> {
|
||||
CFRefCount *TF;
|
||||
: public CheckerV2< check::PostStmt<BlockExpr> > {
|
||||
public:
|
||||
RetainReleaseChecker(CFRefCount *tf) : TF(tf) {}
|
||||
static void* getTag() { static int x = 0; return &x; }
|
||||
|
||||
void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE);
|
||||
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C,
|
||||
const BlockExpr *BE) {
|
||||
void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE,
|
||||
CheckerContext &C) const {
|
||||
|
||||
// Scan the BlockDecRefExprs for any object the retain/release checker
|
||||
// may be tracking.
|
||||
|
@ -3510,7 +3508,9 @@ void CFRefCount::RegisterChecks(ExprEngine& Eng) {
|
|||
// Register the RetainReleaseChecker with the ExprEngine object.
|
||||
// Functionality in CFRefCount will be migrated to RetainReleaseChecker
|
||||
// over time.
|
||||
Eng.registerCheck(new RetainReleaseChecker(this));
|
||||
// FIXME: HACK! Remove TransferFuncs and turn all of CFRefCount into fully
|
||||
// using the checker mechanism.
|
||||
Eng.getCheckerManager().registerChecker<RetainReleaseChecker>();
|
||||
}
|
||||
|
||||
TransferFuncs* ento::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
|
@ -224,11 +225,11 @@ void ExprEngine::evalMethodCall(const CallExpr *MCE, const CXXMethodDecl *MD,
|
|||
ExplodedNodeSet &Src, ExplodedNodeSet &Dst) {
|
||||
// Allow checkers to pre-visit the member call.
|
||||
ExplodedNodeSet PreVisitChecks;
|
||||
CheckerVisit(MCE, PreVisitChecks, Src, PreVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPreStmt(PreVisitChecks, Src, MCE, *this);
|
||||
|
||||
if (!(MD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) {
|
||||
// FIXME: conservative method call evaluation.
|
||||
CheckerVisit(MCE, Dst, PreVisitChecks, PostVisitStmtCallback);
|
||||
getCheckerManager().runCheckersForPostStmt(Dst, PreVisitChecks, MCE, *this);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue