aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp21
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp13
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp8
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp38
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp97
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp39
7 files changed, 109 insertions, 114 deletions
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index 488549c343..53656cf6a0 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -1975,8 +1975,11 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
}
else {
assert(isa<ObjCMessageExpr>(S));
- ObjCMethodCall Call(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
- switch (Call.getMessageKind()) {
+ CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
+ CallEventRef<ObjCMethodCall> Call
+ = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
+
+ switch (Call->getMessageKind()) {
case OCM_Message:
os << "Method";
break;
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 2375784681..ab4b3832ee 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -620,3 +620,24 @@ ObjCMethodDecl *ObjCMethodCall::LookupClassMethodDefinition(Selector Sel,
return Method;
}
+
+CallEventRef<SimpleCall>
+CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
+ const LocationContext *LCtx) {
+ if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE))
+ return create<CXXMemberCall>(MCE, State, LCtx);
+
+ if (const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
+ if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
+ if (MD->isInstance())
+ return create<CXXMemberOperatorCall>(OpCE, State, LCtx);
+
+ } else if (CE->getCallee()->getType()->isBlockPointerType()) {
+ return create<BlockCall>(CE, State, LCtx);
+ }
+
+ // Otherwise, it's a normal function call, static member function call, or
+ // something we can't reason about.
+ return create<FunctionCall>(CE, State, LCtx);
+}
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 5e3ce7a72d..e1d6a26193 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -192,15 +192,10 @@ namespace {
void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
- ProgramPoint::PostStmtKind;
- const ProgramPoint &L =
- ProgramPoint::getProgramPoint(Msg.getOriginExpr(),
- K, Pred->getLocationContext(),
- checkFn.Checker);
+ const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
- checkFn(Msg, C);
+ checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C);
}
};
}
@@ -237,10 +232,10 @@ namespace {
void runChecker(CheckerManager::CheckCallFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- const ProgramPoint &L = Call.getProgramPoint(IsPreVisit, checkFn.Checker);
+ const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
- checkFn(Call, C);
+ checkFn(*Call.cloneWithState(Pred->getState()), C);
}
};
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 7db519c578..a1cd23dc6b 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -875,15 +875,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
- case Stmt::ObjCMessageExprClass: {
+ case Stmt::ObjCMessageExprClass:
Bldr.takeNodes(Pred);
- ObjCMethodCall Call(cast<ObjCMessageExpr>(S),
- Pred->getState(),
- Pred->getLocationContext());
- VisitObjCMessage(Call, Pred, Dst);
+ VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
- }
case Stmt::ObjCAtThrowStmtClass: {
// FIXME: This is not complete. We basically treat @throw as
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 3072234e0c..d0a0e32f74 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -125,23 +125,25 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
}
}
- CXXConstructorCall Call(CE, Target, State, LCtx);
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXConstructorCall> Call =
+ CEMgr.getCXXConstructorCall(CE, Target, State, LCtx);
ExplodedNodeSet DstPreVisit;
getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
ExplodedNodeSet DstPreCall;
getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit,
- Call, *this);
+ *Call, *this);
ExplodedNodeSet DstInvalidated;
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
- defaultEvalCall(Bldr, *I, Call);
+ defaultEvalCall(Bldr, *I, *Call);
ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated,
- Call, *this);
+ *Call, *this);
getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
}
@@ -150,36 +152,39 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
const Stmt *S,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
// FIXME: We need to run the same destructor on every element of the array.
// This workaround will just run the first destructor (which will still
// invalidate the entire array).
if (const ArrayType *AT = getContext().getAsArrayType(ObjectType)) {
ObjectType = AT->getElementType();
- Dest = Pred->getState()->getLValue(ObjectType,
- getSValBuilder().makeZeroArrayIndex(),
- loc::MemRegionVal(Dest)).getAsRegion();
+ Dest = State->getLValue(ObjectType, getSValBuilder().makeZeroArrayIndex(),
+ loc::MemRegionVal(Dest)).getAsRegion();
}
const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
assert(RecordDecl && "Only CXXRecordDecls should have destructors");
const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
- CXXDestructorCall Call(DtorDecl, S, Dest, Pred->getState(),
- Pred->getLocationContext());
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXDestructorCall> Call =
+ CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, State, LCtx);
ExplodedNodeSet DstPreCall;
getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
- Call, *this);
+ *Call, *this);
ExplodedNodeSet DstInvalidated;
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
- defaultEvalCall(Bldr, *I, Call);
+ defaultEvalCall(Bldr, *I, *Call);
ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
- Call, *this);
+ *Call, *this);
}
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
@@ -196,9 +201,14 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
svalBuilder.getConjuredSymbolVal(0, CNE, LCtx, CNE->getType(), blockCount);
ProgramStateRef State = Pred->getState();
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXAllocatorCall> Call =
+ CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+
// Invalidate placement args.
- CXXAllocatorCall Call(CNE, State, LCtx);
- State = Call.invalidateRegions(blockCount);
+ // FIXME: Once we figure out how we want allocators to work,
+ // we should be using the usual pre-/(default-)eval-/post-call checks here.
+ State = Call->invalidateRegions(blockCount);
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 0392a2490f..cbd5ea7553 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -22,23 +22,6 @@
using namespace clang;
using namespace ento;
-static CallEventKind classifyCallExpr(const CallExpr *CE) {
- if (isa<CXXMemberCallExpr>(CE))
- return CE_CXXMember;
-
- const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE);
- if (OpCE) {
- const FunctionDecl *DirectCallee = CE->getDirectCallee();
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
- if (MD->isInstance())
- return CE_CXXMemberOperator;
- } else if (CE->getCallee()->getType()->isBlockPointerType()) {
- return CE_Block;
- }
-
- return CE_Function;
-}
-
void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
// Get the entry block in the CFG of the callee.
const StackFrameContext *calleeCtx = CE.getCalleeContext();
@@ -387,42 +370,18 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this);
- // Get the callee kind.
- CallEventKind K = classifyCallExpr(CE);
+ // Get the call in its initial state. We use this as a template to perform
+ // all the checks.
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<SimpleCall> CallTemplate
+ = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext());
// Evaluate the function call. We try each of the checkers
// to see if the can evaluate the function call.
ExplodedNodeSet dstCallEvaluated;
for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
I != E; ++I) {
- ProgramStateRef State = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
-
- // Evaluate the call.
- switch (K) {
- case CE_Function: {
- FunctionCall Call(CE, State, LCtx);
- evalCall(dstCallEvaluated, *I, Call);
- break;
- }
- case CE_CXXMember: {
- CXXMemberCall Call(cast<CXXMemberCallExpr>(CE), State, LCtx);
- evalCall(dstCallEvaluated, *I, Call);
- break;
- }
- case CE_CXXMemberOperator: {
- CXXMemberOperatorCall Call(cast<CXXOperatorCallExpr>(CE), State, LCtx);
- evalCall(dstCallEvaluated, *I, Call);
- break;
- }
- case CE_Block: {
- BlockCall Call(CE, State, LCtx);
- evalCall(dstCallEvaluated, *I, Call);
- break;
- }
- default:
- llvm_unreachable("Non-CallExpr CallEventKind");
- }
+ evalCall(dstCallEvaluated, *I, *CallTemplate);
}
// Finally, perform the post-condition check of the CallExpr and store
@@ -435,6 +394,13 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
const SimpleCall &Call) {
+ // WARNING: At this time, the state attached to 'Call' may be older than the
+ // state in 'Pred'. This is a minor optimization since CheckerManager will
+ // use an updated CallEvent instance when calling checkers, but if 'Call' is
+ // ever used directly in this function all callers should be updated to pass
+ // the most recent state. (It is probably not worth doing the work here since
+ // for some callers this will not be necessary.)
+
// Run any pre-call checks using the generic call interface.
ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this);
@@ -483,36 +449,35 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
}
void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
- const CallEvent &Call) {
- ProgramStateRef State = 0;
- const Expr *E = Call.getOriginExpr();
+ const CallEvent &CallTemplate) {
+ // Make sure we have the most recent state attached to the call.
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<> Call = CallTemplate.cloneWithState(State);
// 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.
- 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.
+ // this should still be safe even for CallEvents that don't come from exprs.
+ const Expr *E = Call->getOriginExpr();
+ ProgramStateRef InlinedFailedState = getInlineFailedState(State, E);
+
+ if (InlinedFailedState) {
+ // If we already tried once and failed, make sure we don't retry later.
+ State = InlinedFailedState;
+ } else if (inlineCall(*Call, Pred)) {
+ // If we decided to inline the call, the successor has been manually
+ // added onto the work list and we should not perform our generic
+ // call-handling steps.
Bldr.takeNodes(Pred);
return;
}
// If we can't inline it, handle the return value and invalidate the regions.
- if (State == 0)
- State = Pred->getState();
-
- // Invalidate any regions touched by the call.
unsigned Count = currentBuilderContext->getCurrentBlockCount();
- State = Call.invalidateRegions(Count, State);
-
- // Construct and bind the return value.
- State = bindReturnValue(Call, Pred->getLocationContext(), State);
+ State = Call->invalidateRegions(Count, State);
+ 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 e61ea79594..e3bc498b51 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -140,17 +140,20 @@ static bool isSubclass(const ObjCInterfaceDecl *Class, IdentifierInfo *II) {
return isSubclass(Class->getSuperClass(), II);
}
-void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
+void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
-
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<ObjCMethodCall> Msg =
+ CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext());
+
// Handle the previsits checks.
ExplodedNodeSet dstPrevisit;
getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
- msg, *this);
+ *Msg, *this);
ExplodedNodeSet dstGenericPrevisit;
getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit,
- msg, *this);
+ *Msg, *this);
// Proceed with evaluate the message expression.
ExplodedNodeSet dstEval;
@@ -159,16 +162,17 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
ExplodedNode *Pred = *DI;
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
- if (msg.isInstanceMessage()) {
- SVal recVal = msg.getReceiverSVal();
+ if (UpdatedMsg->isInstanceMessage()) {
+ SVal recVal = UpdatedMsg->getReceiverSVal();
if (!recVal.isUndef()) {
// Bifurcate the state into nil and non-nil ones.
DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
- ProgramStateRef state = Pred->getState();
ProgramStateRef notNilState, nilState;
- llvm::tie(notNilState, nilState) = state->assume(receiverVal);
+ 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 ignore must be nil, and merge the rest two into non-nil.
@@ -180,20 +184,20 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
// Check if the "raise" message was sent.
assert(notNilState);
- if (msg.getSelector() == RaiseSel) {
+ 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);
+ Bldr.generateNode(currentStmt, Pred, State, true);
continue;
}
// Generate a transition to non-Nil state.
- if (notNilState != state)
+ if (notNilState != State)
Pred = Bldr.generateNode(currentStmt, Pred, notNilState);
}
} else {
// Check for special class methods.
- if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
+ if (const ObjCInterfaceDecl *Iface = Msg->getReceiverInterface()) {
if (!NSExceptionII) {
ASTContext &Ctx = getContext();
NSExceptionII = &Ctx.Idents.get("NSException");
@@ -222,7 +226,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
Ctx.Selectors.getSelector(II.size(), &II[0]);
}
- Selector S = msg.getSelector();
+ Selector S = Msg->getSelector();
bool RaisesException = false;
for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) {
if (S == NSExceptionInstanceRaiseSelectors[i]) {
@@ -240,16 +244,17 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
}
}
}
- // Evaluate the call.
- defaultEvalCall(Bldr, Pred, msg);
+ // Evaluate the call.
+ defaultEvalCall(Bldr, Pred, *UpdatedMsg);
}
ExplodedNodeSet dstPostvisit;
- getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, msg, *this);
+ getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval,
+ *Msg, *this);
// Finally, perform the post-condition check of the ObjCMessageExpr and store
// the created nodes in 'Dst'.
getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
- msg, *this);
+ *Msg, *this);
}