зеркало из https://github.com/microsoft/clang-1.git
[analyzer] Refactor VisitObjCMessage and VisitCallExpr to rely on the
same implementation for call evaluation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160530 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
d23ec89dcc
Коммит
e81ce256b6
|
@ -467,9 +467,17 @@ public:
|
|||
ExplodedNode *Pred, ProgramStateRef St, SVal TargetLV, SVal Val,
|
||||
const ProgramPointTag *tag = 0);
|
||||
|
||||
/// \brief Create a new state in which the call return value is binded to the
|
||||
/// call origin expression.
|
||||
ProgramStateRef bindReturnValue(const CallEvent &Call,
|
||||
const LocationContext *LCtx,
|
||||
ProgramStateRef State);
|
||||
|
||||
void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
|
||||
const SimpleCall &Call);
|
||||
void defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
|
||||
|
||||
/// \bried Default implementation of call evaluation.
|
||||
void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred,
|
||||
const CallEvent &Call);
|
||||
private:
|
||||
void evalLoadCommon(ExplodedNodeSet &Dst,
|
||||
|
@ -491,8 +499,7 @@ private:
|
|||
const ProgramPointTag *tag, bool isLoad);
|
||||
|
||||
bool shouldInlineDecl(const Decl *D, ExplodedNode *Pred);
|
||||
bool inlineCall(ExplodedNodeSet &Dst, const CallEvent &Call,
|
||||
ExplodedNode *Pred);
|
||||
bool inlineCall(const CallEvent &Call, ExplodedNode *Pred);
|
||||
|
||||
bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC);
|
||||
};
|
||||
|
|
|
@ -568,8 +568,10 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
|
|||
}
|
||||
|
||||
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
|
||||
if (!anyEvaluated)
|
||||
Eng.defaultEvalCall(Dst, Pred, Call);
|
||||
if (!anyEvaluated) {
|
||||
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
|
||||
Eng.defaultEvalCall(B, Pred, Call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,9 +54,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
|
|||
Call, *this);
|
||||
|
||||
ExplodedNodeSet DstInvalidated;
|
||||
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
|
||||
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
|
||||
I != E; ++I)
|
||||
defaultEvalCall(DstInvalidated, *I, Call);
|
||||
defaultEvalCall(Bldr, *I, Call);
|
||||
|
||||
ExplodedNodeSet DstPostCall;
|
||||
getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated,
|
||||
|
@ -77,9 +78,10 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
|
|||
Call, *this);
|
||||
|
||||
ExplodedNodeSet DstInvalidated;
|
||||
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
|
||||
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
|
||||
I != E; ++I)
|
||||
defaultEvalCall(DstInvalidated, *I, Call);
|
||||
defaultEvalCall(Bldr, *I, Call);
|
||||
|
||||
ExplodedNodeSet DstPostCall;
|
||||
getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
|
||||
|
|
|
@ -266,8 +266,7 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ExprEngine::inlineCall(ExplodedNodeSet &Dst,
|
||||
const CallEvent &Call,
|
||||
bool ExprEngine::inlineCall(const CallEvent &Call,
|
||||
ExplodedNode *Pred) {
|
||||
if (!getAnalysisManager().shouldInlineCall())
|
||||
return false;
|
||||
|
@ -342,16 +341,16 @@ bool ExprEngine::inlineCall(ExplodedNodeSet &Dst,
|
|||
return true;
|
||||
}
|
||||
|
||||
static ProgramStateRef getInlineFailedState(ExplodedNode *&N,
|
||||
static ProgramStateRef getInlineFailedState(ProgramStateRef State,
|
||||
const Stmt *CallE) {
|
||||
void *ReplayState = N->getState()->get<ReplayWithoutInlining>();
|
||||
void *ReplayState = State->get<ReplayWithoutInlining>();
|
||||
if (!ReplayState)
|
||||
return 0;
|
||||
|
||||
assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call.");
|
||||
(void)CallE;
|
||||
|
||||
return N->getState()->remove<ReplayWithoutInlining>();
|
||||
return State->remove<ReplayWithoutInlining>();
|
||||
}
|
||||
|
||||
void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
|
||||
|
@ -419,38 +418,69 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
|
|||
Call, *this);
|
||||
}
|
||||
|
||||
void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
|
||||
ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
|
||||
const LocationContext *LCtx,
|
||||
ProgramStateRef State) {
|
||||
const Expr *E = Call.getOriginExpr();
|
||||
if (!E)
|
||||
return State;
|
||||
|
||||
// Some method families have known return values.
|
||||
if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
|
||||
switch (Msg->getMethodFamily()) {
|
||||
default:
|
||||
break;
|
||||
case OMF_autorelease:
|
||||
case OMF_retain:
|
||||
case OMF_self: {
|
||||
// These methods return their receivers.
|
||||
return State->BindExpr(E, LCtx, Msg->getReceiverSVal());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conjure a symbol if the return value is unknown.
|
||||
QualType ResultTy = Call.getResultType();
|
||||
SValBuilder &SVB = getSValBuilder();
|
||||
unsigned Count = currentBuilderContext->getCurrentBlockCount();
|
||||
SVal R = SVB.getConjuredSymbolVal(0, E, LCtx, ResultTy, Count);
|
||||
return State->BindExpr(E, LCtx, R);
|
||||
}
|
||||
|
||||
void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
|
||||
const CallEvent &Call) {
|
||||
ProgramStateRef State = 0;
|
||||
const Expr *E = Call.getOriginExpr();
|
||||
|
||||
// Try to inline the call.
|
||||
// The origin expression here is just used as a kind of checksum;
|
||||
// for CallEvents that do not have origin expressions, this should still be
|
||||
// safe.
|
||||
const Expr *E = Call.getOriginExpr();
|
||||
ProgramStateRef state = getInlineFailedState(Pred, E);
|
||||
if (state == 0 && inlineCall(Dst, Call, Pred))
|
||||
return;
|
||||
if (!isa<ObjCMethodCall>(Call)) {
|
||||
State = getInlineFailedState(Pred->getState(), E);
|
||||
if (State == 0 && inlineCall(Call, Pred)) {
|
||||
// If we inlined the call, the successor has been manually added onto
|
||||
// the work list and we should not consider it for subsequent call
|
||||
// handling steps.
|
||||
Bldr.takeNodes(Pred);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we can't inline it, handle the return value and invalidate the regions.
|
||||
NodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
|
||||
if (State == 0)
|
||||
State = Pred->getState();
|
||||
|
||||
// Invalidate any regions touched by the call.
|
||||
unsigned Count = currentBuilderContext->getCurrentBlockCount();
|
||||
if (state == 0)
|
||||
state = Pred->getState();
|
||||
state = Call.invalidateRegions(Count, state);
|
||||
State = Call.invalidateRegions(Count, State);
|
||||
|
||||
// Conjure a symbol value to use as the result.
|
||||
if (E) {
|
||||
QualType ResultTy = Call.getResultType();
|
||||
SValBuilder &SVB = getSValBuilder();
|
||||
const LocationContext *LCtx = Pred->getLocationContext();
|
||||
SVal RetVal = SVB.getConjuredSymbolVal(0, E, LCtx, ResultTy, Count);
|
||||
|
||||
state = state->BindExpr(E, LCtx, RetVal);
|
||||
}
|
||||
// Construct and bind the return value.
|
||||
State = bindReturnValue(Call, Pred->getLocationContext(), State);
|
||||
|
||||
// And make the result node.
|
||||
Bldr.generateNode(Call.getProgramPoint(), state, Pred);
|
||||
Bldr.generateNode(Call.getProgramPoint(), State, Pred);
|
||||
}
|
||||
|
||||
void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
|
||||
|
|
|
@ -158,9 +158,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
|
|||
|
||||
for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
|
||||
DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
|
||||
|
||||
ExplodedNode *Pred = *DI;
|
||||
bool RaisesException = false;
|
||||
|
||||
if (msg.isInstanceMessage()) {
|
||||
SVal recVal = msg.getReceiverSVal();
|
||||
|
@ -182,13 +180,19 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
|
|||
|
||||
// Check if the "raise" message was sent.
|
||||
assert(notNilState);
|
||||
if (msg.getSelector() == RaiseSel)
|
||||
RaisesException = true;
|
||||
if (msg.getSelector() == RaiseSel) {
|
||||
// If we raise an exception, for now treat it as a sink.
|
||||
// Eventually we will want to handle exceptions properly.
|
||||
Bldr.generateNode(currentStmt, Pred, Pred->getState(), true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we raise an exception, for now treat it as a sink.
|
||||
// Eventually we will want to handle exceptions properly.
|
||||
// Dispatch to plug-in transfer function.
|
||||
evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException);
|
||||
// Generate a transition to non-Nil state.
|
||||
if (notNilState != state)
|
||||
Pred = Bldr.generateNode(currentStmt, Pred, notNilState);
|
||||
|
||||
// Evaluate the call.
|
||||
defaultEvalCall(Bldr, Pred, msg);
|
||||
}
|
||||
} else {
|
||||
// Check for special class methods.
|
||||
|
@ -222,19 +226,25 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
|
|||
}
|
||||
|
||||
Selector S = msg.getSelector();
|
||||
bool RaisesException = false;
|
||||
for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) {
|
||||
if (S == NSExceptionInstanceRaiseSelectors[i]) {
|
||||
RaisesException = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (RaisesException) {
|
||||
// If we raise an exception, for now treat it as a sink.
|
||||
// Eventually we will want to handle exceptions properly.
|
||||
Bldr.generateNode(currentStmt, Pred, Pred->getState(), true);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If we raise an exception, for now treat it as a sink.
|
||||
// Eventually we will want to handle exceptions properly.
|
||||
// Dispatch to plug-in transfer function.
|
||||
evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException);
|
||||
// Evaluate the call.
|
||||
defaultEvalCall(Bldr, Pred, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,48 +256,3 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
|
|||
getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
|
||||
msg, *this);
|
||||
}
|
||||
|
||||
void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr,
|
||||
const ObjCMethodCall &msg,
|
||||
ExplodedNode *Pred,
|
||||
ProgramStateRef state,
|
||||
bool GenSink) {
|
||||
// First handle the return value.
|
||||
SVal ReturnValue = UnknownVal();
|
||||
|
||||
// Some method families have known return values.
|
||||
switch (msg.getMethodFamily()) {
|
||||
default:
|
||||
break;
|
||||
case OMF_autorelease:
|
||||
case OMF_retain:
|
||||
case OMF_self: {
|
||||
// These methods return their receivers.
|
||||
ReturnValue = msg.getReceiverSVal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const LocationContext *LCtx = Pred->getLocationContext();
|
||||
unsigned BlockCount = currentBuilderContext->getCurrentBlockCount();
|
||||
|
||||
// If we failed to figure out the return value, use a conjured value instead.
|
||||
if (ReturnValue.isUnknown()) {
|
||||
SValBuilder &SVB = getSValBuilder();
|
||||
QualType ResultTy = msg.getResultType();
|
||||
const Expr *CurrentE = cast<Expr>(currentStmt);
|
||||
ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy,
|
||||
BlockCount);
|
||||
}
|
||||
|
||||
// Bind the return value.
|
||||
state = state->BindExpr(currentStmt, LCtx, ReturnValue);
|
||||
|
||||
// Invalidate the arguments (and the receiver)
|
||||
state = msg.invalidateRegions(BlockCount, state);
|
||||
|
||||
// And create the new node.
|
||||
Bldr.generateNode(currentStmt, Pred, state, GenSink);
|
||||
assert(Bldr.hasGeneratedNodes());
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче