Hard bifurcate the state into nil receiver and non-nil receiver, so that
we don't need to use the DoneEvaluation hack when check for ObjCMessageExpr. PreVisitObjCMessageExpr() only checks for undefined receiver or arguments. Add checker interface EvalNilReceiver(). This is a 'once-and-done' interface. llvm-svn: 90296
This commit is contained in:
parent
d186a02987
commit
af35329f71
|
@ -53,22 +53,10 @@ public:
|
|||
OldTag(B.Tag, tag),
|
||||
OldPointKind(B.PointKind, K),
|
||||
OldHasGen(B.HasGeneratedNode),
|
||||
state(st), statement(stmt), size(Dst.size()),
|
||||
DoneEvaluating(false) {}
|
||||
state(st), statement(stmt), size(Dst.size()) {}
|
||||
|
||||
~CheckerContext();
|
||||
|
||||
// FIXME: This were added to support CallAndMessageChecker to indicating
|
||||
// to GRExprEngine to "stop evaluating" a message expression under certain
|
||||
// cases. This is *not* meant to be a permanent API change, and was added
|
||||
// to aid in the transition of removing logic for checks from GRExprEngine.
|
||||
void setDoneEvaluating() {
|
||||
DoneEvaluating = true;
|
||||
}
|
||||
bool isDoneEvaluating() const {
|
||||
return DoneEvaluating;
|
||||
}
|
||||
|
||||
ConstraintManager &getConstraintManager() {
|
||||
return Eng.getConstraintManager();
|
||||
}
|
||||
|
@ -165,7 +153,7 @@ private:
|
|||
friend class GRExprEngine;
|
||||
|
||||
// FIXME: Remove the 'tag' option.
|
||||
bool GR_Visit(ExplodedNodeSet &Dst,
|
||||
void GR_Visit(ExplodedNodeSet &Dst,
|
||||
GRStmtNodeBuilder &Builder,
|
||||
GRExprEngine &Eng,
|
||||
const Stmt *S,
|
||||
|
@ -177,7 +165,14 @@ private:
|
|||
_PreVisit(C, S);
|
||||
else
|
||||
_PostVisit(C, S);
|
||||
return C.isDoneEvaluating();
|
||||
}
|
||||
|
||||
bool GR_EvalNilReceiver(ExplodedNodeSet &Dst, GRStmtNodeBuilder &Builder,
|
||||
GRExprEngine &Eng, const ObjCMessageExpr *ME,
|
||||
ExplodedNode *Pred, const GRState *state, void *tag) {
|
||||
CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind,
|
||||
ME, state);
|
||||
return EvalNilReceiver(C, ME);
|
||||
}
|
||||
|
||||
// FIXME: Remove the 'tag' option.
|
||||
|
@ -231,6 +226,10 @@ public:
|
|||
virtual void VisitBranchCondition(GRBranchNodeBuilder &Builder,
|
||||
GRExprEngine &Eng,
|
||||
Stmt *Condition, void *tag) {}
|
||||
|
||||
virtual bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // end clang namespace
|
||||
|
||||
|
|
|
@ -209,8 +209,13 @@ public:
|
|||
protected:
|
||||
/// CheckerVisit - Dispatcher for performing checker-specific logic
|
||||
/// at specific statements.
|
||||
bool CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
|
||||
void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
|
||||
bool isPrevisit);
|
||||
|
||||
void CheckerEvalNilReceiver(const ObjCMessageExpr *ME,
|
||||
ExplodedNodeSet &Dst,
|
||||
const GRState *state,
|
||||
ExplodedNode *Pred);
|
||||
|
||||
void CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE,
|
||||
ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
|
||||
|
@ -358,9 +363,10 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
void EvalObjCMessageExpr(ExplodedNodeSet& Dst, ObjCMessageExpr* ME, ExplodedNode* Pred) {
|
||||
void EvalObjCMessageExpr(ExplodedNodeSet& Dst, ObjCMessageExpr* ME,
|
||||
ExplodedNode* Pred, const GRState *state) {
|
||||
assert (Builder && "GRStmtNodeBuilder must be defined.");
|
||||
getTF().EvalObjCMessageExpr(Dst, *this, *Builder, ME, Pred);
|
||||
getTF().EvalObjCMessageExpr(Dst, *this, *Builder, ME, Pred, state);
|
||||
}
|
||||
|
||||
const GRState* MarkBranch(const GRState* St, Stmt* Terminator,
|
||||
|
|
|
@ -47,7 +47,8 @@ public:
|
|||
GRExprEngine& Engine,
|
||||
GRStmtNodeBuilder& Builder,
|
||||
ObjCMessageExpr* ME,
|
||||
ExplodedNode* Pred) {}
|
||||
ExplodedNode* Pred,
|
||||
const GRState *state) {}
|
||||
|
||||
// Stores.
|
||||
|
||||
|
|
|
@ -1985,7 +1985,7 @@ public:
|
|||
Expr* Receiver,
|
||||
const RetainSummary& Summ,
|
||||
ExprIterator arg_beg, ExprIterator arg_end,
|
||||
ExplodedNode* Pred);
|
||||
ExplodedNode* Pred, const GRState *state);
|
||||
|
||||
virtual void EvalCall(ExplodedNodeSet& Dst,
|
||||
GRExprEngine& Eng,
|
||||
|
@ -1998,7 +1998,8 @@ public:
|
|||
GRExprEngine& Engine,
|
||||
GRStmtNodeBuilder& Builder,
|
||||
ObjCMessageExpr* ME,
|
||||
ExplodedNode* Pred);
|
||||
ExplodedNode* Pred,
|
||||
const GRState *state);
|
||||
|
||||
bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst,
|
||||
GRExprEngine& Engine,
|
||||
|
@ -2777,10 +2778,7 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
|
|||
Expr* Receiver,
|
||||
const RetainSummary& Summ,
|
||||
ExprIterator arg_beg, ExprIterator arg_end,
|
||||
ExplodedNode* Pred) {
|
||||
|
||||
// Get the state.
|
||||
const GRState *state = Builder.GetState(Pred);
|
||||
ExplodedNode* Pred, const GRState *state) {
|
||||
|
||||
// Evaluate the effect of the arguments.
|
||||
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
||||
|
@ -3013,34 +3011,23 @@ void CFRefCount::EvalCall(ExplodedNodeSet& Dst,
|
|||
|
||||
assert(Summ);
|
||||
EvalSummary(Dst, Eng, Builder, CE, 0, *Summ,
|
||||
CE->arg_begin(), CE->arg_end(), Pred);
|
||||
CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred));
|
||||
}
|
||||
|
||||
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst,
|
||||
GRExprEngine& Eng,
|
||||
GRStmtNodeBuilder& Builder,
|
||||
ObjCMessageExpr* ME,
|
||||
ExplodedNode* Pred) {
|
||||
// FIXME: Since we moved the nil check into a checker, we could get nil
|
||||
// receiver here. Need a better way to check such case.
|
||||
if (Expr* Receiver = ME->getReceiver()) {
|
||||
const GRState *state = Pred->getState();
|
||||
DefinedOrUnknownSVal L=cast<DefinedOrUnknownSVal>(state->getSVal(Receiver));
|
||||
if (!state->Assume(L, true)) {
|
||||
Dst.Add(Pred);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ExplodedNode* Pred,
|
||||
const GRState *state) {
|
||||
RetainSummary *Summ =
|
||||
ME->getReceiver()
|
||||
? Summaries.getInstanceMethodSummary(ME, Builder.GetState(Pred),
|
||||
Pred->getLocationContext())
|
||||
? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext())
|
||||
: Summaries.getClassMethodSummary(ME);
|
||||
|
||||
assert(Summ && "RetainSummary is null");
|
||||
EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ,
|
||||
ME->arg_begin(), ME->arg_end(), Pred);
|
||||
ME->arg_begin(), ME->arg_end(), Pred, state);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
|
||||
void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
|
||||
void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
|
||||
bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME);
|
||||
|
||||
private:
|
||||
void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
|
||||
|
@ -148,28 +149,12 @@ void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C,
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the receiver was nil and then returns a value that may
|
||||
// be garbage.
|
||||
if (const Expr *Receiver = ME->getReceiver()) {
|
||||
DefinedOrUnknownSVal receiverVal =
|
||||
cast<DefinedOrUnknownSVal>(state->getSVal(Receiver));
|
||||
|
||||
const GRState *notNullState, *nullState;
|
||||
llvm::tie(notNullState, nullState) = state->Assume(receiverVal);
|
||||
|
||||
if (nullState && !notNullState) {
|
||||
HandleNilReceiver(C, nullState, ME);
|
||||
C.setDoneEvaluating(); // FIXME: eventually remove.
|
||||
return;
|
||||
}
|
||||
|
||||
assert(notNullState);
|
||||
state = notNullState;
|
||||
}
|
||||
|
||||
// Add a state transition if the state has changed.
|
||||
C.addTransition(state);
|
||||
bool CallAndMessageChecker::EvalNilReceiver(CheckerContext &C,
|
||||
const ObjCMessageExpr *ME) {
|
||||
HandleNilReceiver(C, C.getState(), ME);
|
||||
return true; // Nil receiver is not handled elsewhere.
|
||||
}
|
||||
|
||||
void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C,
|
||||
|
|
|
@ -116,20 +116,18 @@ public:
|
|||
// Checker worklist routines.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
|
||||
void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
|
||||
ExplodedNodeSet &Src, bool isPrevisit) {
|
||||
|
||||
if (Checkers.empty()) {
|
||||
Dst.insert(Src);
|
||||
return false;
|
||||
Dst = Src;
|
||||
return;
|
||||
}
|
||||
|
||||
ExplodedNodeSet Tmp;
|
||||
ExplodedNodeSet *PrevSet = &Src;
|
||||
bool stopProcessingAfterCurrentChecker = false;
|
||||
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
|
||||
{
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){
|
||||
ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst
|
||||
: (PrevSet == &Tmp) ? &Src : &Tmp;
|
||||
|
||||
|
@ -138,31 +136,26 @@ bool GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
|
|||
Checker *checker = I->second;
|
||||
|
||||
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
|
||||
NI != NE; ++NI) {
|
||||
// FIXME: Halting evaluation of the checkers is something we may
|
||||
// not support later. The design is still evolving.
|
||||
if (checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI,
|
||||
tag, isPrevisit)) {
|
||||
if (CurrSet != &Dst)
|
||||
Dst.insert(*CurrSet);
|
||||
|
||||
stopProcessingAfterCurrentChecker = true;
|
||||
continue;
|
||||
}
|
||||
assert(stopProcessingAfterCurrentChecker == false &&
|
||||
"Inconsistent setting of 'stopProcessingAfterCurrentChecker'");
|
||||
}
|
||||
|
||||
if (stopProcessingAfterCurrentChecker)
|
||||
return true;
|
||||
|
||||
// Continue on to the next checker. Update the current NodeSet.
|
||||
NI != NE; ++NI)
|
||||
checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit);
|
||||
PrevSet = CurrSet;
|
||||
}
|
||||
|
||||
// Don't autotransition. The CheckerContext objects should do this
|
||||
// automatically.
|
||||
return false;
|
||||
}
|
||||
|
||||
void GRExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME,
|
||||
ExplodedNodeSet &Dst,
|
||||
const GRState *state,
|
||||
ExplodedNode *Pred) {
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) {
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
|
||||
if (checker->GR_EvalNilReceiver(Dst, *Builder, *this, ME, Pred, state, tag))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This is largely copy-paste from CheckerVisit(). Need to
|
||||
|
@ -1922,10 +1915,7 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME,
|
|||
ExplodedNodeSet Src, DstTmp;
|
||||
Src.Add(Pred);
|
||||
|
||||
if (CheckerVisit(ME, DstTmp, Src, true)) {
|
||||
Dst.insert(DstTmp);
|
||||
return;
|
||||
}
|
||||
CheckerVisit(ME, DstTmp, Src, true);
|
||||
|
||||
unsigned size = Dst.size();
|
||||
|
||||
|
@ -1934,10 +1924,38 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME,
|
|||
Pred = *DI;
|
||||
bool RaisesException = false;
|
||||
|
||||
if (ME->getReceiver()) {
|
||||
if (const Expr *Receiver = ME->getReceiver()) {
|
||||
const GRState *state = Pred->getState();
|
||||
|
||||
// Bifurcate the state into nil and non-nil ones.
|
||||
DefinedOrUnknownSVal receiverVal =
|
||||
cast<DefinedOrUnknownSVal>(state->getSVal(Receiver));
|
||||
|
||||
const GRState *notNilState, *nilState;
|
||||
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.
|
||||
if (nilState && !notNilState) {
|
||||
CheckerEvalNilReceiver(ME, Dst, nilState, Pred);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(notNilState);
|
||||
|
||||
// Check if the "raise" message was sent.
|
||||
if (ME->getSelector() == RaiseSel)
|
||||
RaisesException = true;
|
||||
|
||||
// Check if we raise an exception. For now treat these as sinks.
|
||||
// Eventually we will want to handle exceptions properly.
|
||||
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
|
||||
if (RaisesException)
|
||||
Builder->BuildSinks = true;
|
||||
|
||||
// Dispatch to plug-in transfer function.
|
||||
SaveOr OldHasGen(Builder->HasGeneratedNode);
|
||||
EvalObjCMessageExpr(Dst, ME, Pred, notNilState);
|
||||
}
|
||||
else {
|
||||
|
||||
|
@ -1984,17 +2002,17 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME,
|
|||
RaisesException = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we raise an exception. For now treat these as sinks.
|
||||
// Eventually we will want to handle exceptions properly.
|
||||
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
|
||||
if (RaisesException)
|
||||
Builder->BuildSinks = true;
|
||||
|
||||
// Dispatch to plug-in transfer function.
|
||||
SaveOr OldHasGen(Builder->HasGeneratedNode);
|
||||
EvalObjCMessageExpr(Dst, ME, Pred, Builder->GetState(Pred));
|
||||
}
|
||||
|
||||
// Check if we raise an exception. For now treat these as sinks. Eventually
|
||||
// we will want to handle exceptions properly.
|
||||
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
|
||||
if (RaisesException)
|
||||
Builder->BuildSinks = true;
|
||||
|
||||
// Dispatch to plug-in transfer function.
|
||||
SaveOr OldHasGen(Builder->HasGeneratedNode);
|
||||
EvalObjCMessageExpr(Dst, ME, Pred);
|
||||
}
|
||||
|
||||
// Handle the case where no nodes where generated. Auto-generate that
|
||||
|
|
Loading…
Reference in New Issue