From: Anna Zaks Date: Thu, 19 Jul 2012 23:38:13 +0000 (+0000) Subject: [analyzer] Refactor VisitObjCMessage and VisitCallExpr to rely on the X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e81ce256b62717dd846bd19aecc4115a0dcd4995;p=clang [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 --- diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 810331521c..eeb4c57f34 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -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); }; diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index d0d212130a..591609d285 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -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); + } } } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index f786a4f8c5..7c3000ee0d 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -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, diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 7cedb7ac5e..cc257eb38b 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -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(); + void *ReplayState = State->get(); if (!ReplayState) return 0; assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call."); (void)CallE; - return N->getState()->remove(); + return State->remove(); } 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(&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(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); - - // 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); - } + State = Call.invalidateRegions(Count, State); + + // 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, diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index f99fe5ff37..a8e0b3b809 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -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(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()); -} -