aboutsummaryrefslogtreecommitdiff
path: root/lib/Analysis/GRExprEngine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis/GRExprEngine.cpp')
-rw-r--r--lib/Analysis/GRExprEngine.cpp541
1 files changed, 228 insertions, 313 deletions
diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp
index 20820d4f3833..51e6a547529f 100644
--- a/lib/Analysis/GRExprEngine.cpp
+++ b/lib/Analysis/GRExprEngine.cpp
@@ -116,53 +116,91 @@ 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;
+ return;
}
ExplodedNodeSet Tmp;
ExplodedNodeSet *PrevSet = &Src;
- bool stopProcessingAfterCurrentChecker = false;
-
- for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
- {
- ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst
- : (PrevSet == &Tmp) ? &Src : &Tmp;
- CurrSet->clear();
+ for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){
+ ExplodedNodeSet *CurrSet = 0;
+ if (I+1 == E)
+ CurrSet = &Dst;
+ 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) {
- // 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) {
+ 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, ME, 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 GRExprEngine::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);
+ else
+ Dst.insert(Pred);
+
+ return Evaluated;
}
// FIXME: This is largely copy-paste from CheckerVisit(). Need to
@@ -173,7 +211,7 @@ void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE,
SVal location, SVal val, bool isPrevisit) {
if (Checkers.empty()) {
- Dst = Src;
+ Dst.insert(Src);
return;
}
@@ -182,10 +220,14 @@ void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE,
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
{
- ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst
- : (PrevSet == &Tmp) ? &Src : &Tmp;
+ ExplodedNodeSet *CurrSet = 0;
+ if (I+1 == E)
+ CurrSet = &Dst;
+ else {
+ CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp;
+ CurrSet->clear();
+ }
- CurrSet->clear();
void *tag = I->first;
Checker *checker = I->second;
@@ -227,6 +269,11 @@ static void RegisterInternalChecks(GRExprEngine &Eng) {
RegisterUndefinedAssignmentChecker(Eng);
RegisterUndefBranchChecker(Eng);
RegisterUndefResultChecker(Eng);
+
+ // This is not a checker yet.
+ RegisterNoReturnFunctionChecker(Eng);
+ RegisterBuiltinFunctionChecker(Eng);
+ RegisterOSAtomicChecker(Eng);
}
GRExprEngine::GRExprEngine(AnalysisManager &mgr)
@@ -347,8 +394,9 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) {
Builder->setAuditor(BatchAuditor.get());
// Create the cleaned state.
- SymbolReaper SymReaper(Builder->getBasePredecessor()->getLiveVariables(),
- SymMgr);
+ const ExplodedNode *BasePred = Builder->getBasePredecessor();
+ SymbolReaper SymReaper(BasePred->getLiveVariables(), SymMgr,
+ BasePred->getLocationContext()->getCurrentStackFrame());
CleanedState = AMgr.shouldPurgeDead()
? StateMgr.RemoveDeadBindings(EntryNode->getState(), CurrentStmt, SymReaper)
: EntryNode->getState();
@@ -371,16 +419,20 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) {
CleanedState, SymReaper);
if (Checkers.empty())
- Tmp = Tmp2;
+ Tmp.insert(Tmp2);
else {
ExplodedNodeSet Tmp3;
ExplodedNodeSet *SrcSet = &Tmp2;
for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end();
I != E; ++I) {
- ExplodedNodeSet *DstSet = (I+1 == E) ? &Tmp
- : (SrcSet == &Tmp2) ? &Tmp3
- : &Tmp2;
- DstSet->clear();
+ ExplodedNodeSet *DstSet = 0;
+ if (I+1 == E)
+ DstSet = &Tmp;
+ 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();
@@ -441,6 +493,41 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
}
switch (S->getStmtClass()) {
+ // C++ stuff we don't support yet.
+ case Stmt::CXXMemberCallExprClass:
+ case Stmt::CXXNamedCastExprClass:
+ case Stmt::CXXStaticCastExprClass:
+ case Stmt::CXXDynamicCastExprClass:
+ case Stmt::CXXReinterpretCastExprClass:
+ case Stmt::CXXConstCastExprClass:
+ case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::CXXTypeidExprClass:
+ case Stmt::CXXBoolLiteralExprClass:
+ case Stmt::CXXNullPtrLiteralExprClass:
+ case Stmt::CXXThisExprClass:
+ case Stmt::CXXThrowExprClass:
+ case Stmt::CXXDefaultArgExprClass:
+ case Stmt::CXXZeroInitValueExprClass:
+ case Stmt::CXXNewExprClass:
+ case Stmt::CXXDeleteExprClass:
+ case Stmt::CXXPseudoDestructorExprClass:
+ case Stmt::UnresolvedLookupExprClass:
+ case Stmt::UnaryTypeTraitExprClass:
+ case Stmt::DependentScopeDeclRefExprClass:
+ case Stmt::CXXConstructExprClass:
+ case Stmt::CXXBindTemporaryExprClass:
+ case Stmt::CXXExprWithTemporariesClass:
+ case Stmt::CXXTemporaryObjectExprClass:
+ case Stmt::CXXUnresolvedConstructExprClass:
+ case Stmt::CXXDependentScopeMemberExprClass:
+ case Stmt::UnresolvedMemberExprClass:
+ case Stmt::CXXCatchStmtClass:
+ case Stmt::CXXTryStmtClass: {
+ SaveAndRestore<bool> OldSink(Builder->BuildSinks);
+ Builder->BuildSinks = true;
+ MakeNode(Dst, S, Pred, GetState(Pred));
+ break;
+ }
default:
// Cases we intentionally have "default" handle:
@@ -456,6 +543,10 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
case Stmt::AsmStmtClass:
VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst);
break;
+
+ case Stmt::BlockDeclRefExprClass:
+ VisitBlockDeclRefExpr(cast<BlockDeclRefExpr>(S), Pred, Dst, false);
+ break;
case Stmt::BlockExprClass:
VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst);
@@ -627,6 +718,10 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred,
VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(Ex), Pred, Dst, true);
return;
+ case Stmt::BlockDeclRefExprClass:
+ VisitBlockDeclRefExpr(cast<BlockDeclRefExpr>(Ex), Pred, Dst, true);
+ return;
+
case Stmt::DeclRefExprClass:
VisitDeclRefExpr(cast<DeclRefExpr>(Ex), Pred, Dst, true);
return;
@@ -1118,9 +1213,20 @@ void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred,
void GRExprEngine::VisitDeclRefExpr(DeclRefExpr *Ex, ExplodedNode *Pred,
ExplodedNodeSet &Dst, bool asLValue) {
+ VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue);
+}
+
+void GRExprEngine::VisitBlockDeclRefExpr(BlockDeclRefExpr *Ex,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst, bool asLValue) {
+ VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue);
+}
+
+void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst, bool asLValue) {
const GRState *state = GetState(Pred);
- const NamedDecl *D = Ex->getDecl();
if (const VarDecl* VD = dyn_cast<VarDecl>(D)) {
@@ -1354,10 +1460,14 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S,
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
{
- ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst
- : (PrevSet == &Tmp) ? &Src : &Tmp;
+ ExplodedNodeSet *CurrSet = 0;
+ if (I+1 == E)
+ CurrSet = &Dst;
+ else {
+ CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp;
+ CurrSet->clear();
+ }
- CurrSet->clear();
void *tag = I->first;
Checker *checker = I->second;
@@ -1376,232 +1486,8 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S,
}
//===----------------------------------------------------------------------===//
-// Transfer function: OSAtomics.
-//
-// FIXME: Eventually refactor into a more "plugin" infrastructure.
-//===----------------------------------------------------------------------===//
-
-// Mac OS X:
-// http://developer.apple.com/documentation/Darwin/Reference/Manpages/man3
-// atomic.3.html
-//
-static bool EvalOSAtomicCompareAndSwap(ExplodedNodeSet& Dst,
- GRExprEngine& Engine,
- GRStmtNodeBuilder& Builder,
- CallExpr* CE, ExplodedNode* Pred) {
-
- // Not enough arguments to match OSAtomicCompareAndSwap?
- if (CE->getNumArgs() != 3)
- return false;
-
- ASTContext &C = Engine.getContext();
- Expr *oldValueExpr = CE->getArg(0);
- QualType oldValueType = C.getCanonicalType(oldValueExpr->getType());
-
- Expr *newValueExpr = CE->getArg(1);
- QualType newValueType = C.getCanonicalType(newValueExpr->getType());
-
- // Do the types of 'oldValue' and 'newValue' match?
- if (oldValueType != newValueType)
- return false;
-
- Expr *theValueExpr = CE->getArg(2);
- const PointerType *theValueType =
- theValueExpr->getType()->getAs<PointerType>();
-
- // theValueType not a pointer?
- if (!theValueType)
- return false;
-
- QualType theValueTypePointee =
- C.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType();
-
- // The pointee must match newValueType and oldValueType.
- if (theValueTypePointee != newValueType)
- return false;
-
- static unsigned magic_load = 0;
- static unsigned magic_store = 0;
-
- const void *OSAtomicLoadTag = &magic_load;
- const void *OSAtomicStoreTag = &magic_store;
-
- // Load 'theValue'.
- const GRState *state = Pred->getState();
- ExplodedNodeSet Tmp;
- SVal location = state->getSVal(theValueExpr);
- // Here we should use the value type of the region as the load type.
- const MemRegion *R = location.getAsRegion();
- QualType LoadTy;
- if (R)
- LoadTy = cast<TypedRegion>(R)->getValueType(C);
- Engine.EvalLoad(Tmp, theValueExpr, Pred, state, location, OSAtomicLoadTag,
- LoadTy);
-
- for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end();
- I != E; ++I) {
-
- ExplodedNode *N = *I;
- const GRState *stateLoad = N->getState();
- SVal theValueVal_untested = stateLoad->getSVal(theValueExpr);
- SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr);
-
- // FIXME: Issue an error.
- if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) {
- return false;
- }
-
- DefinedOrUnknownSVal theValueVal =
- cast<DefinedOrUnknownSVal>(theValueVal_untested);
- DefinedOrUnknownSVal oldValueVal =
- cast<DefinedOrUnknownSVal>(oldValueVal_untested);
-
- SValuator &SVator = Engine.getSValuator();
-
- // Perform the comparison.
- DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad, theValueVal,
- oldValueVal);
-
- const GRState *stateEqual = stateLoad->Assume(Cmp, true);
-
- // Were they equal?
- if (stateEqual) {
- // Perform the store.
- ExplodedNodeSet TmpStore;
- SVal val = stateEqual->getSVal(newValueExpr);
-
- // Handle implicit value casts.
- if (const TypedRegion *R =
- dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
- llvm::tie(state, val) = SVator.EvalCast(val, state, R->getValueType(C),
- newValueExpr->getType());
- }
-
- Engine.EvalStore(TmpStore, NULL, theValueExpr, N, stateEqual, location,
- val, OSAtomicStoreTag);
-
- // Now bind the result of the comparison.
- for (ExplodedNodeSet::iterator I2 = TmpStore.begin(),
- E2 = TmpStore.end(); I2 != E2; ++I2) {
- ExplodedNode *predNew = *I2;
- const GRState *stateNew = predNew->getState();
- SVal Res = Engine.getValueManager().makeTruthVal(true, CE->getType());
- Engine.MakeNode(Dst, CE, predNew, stateNew->BindExpr(CE, Res));
- }
- }
-
- // Were they not equal?
- if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) {
- SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType());
- Engine.MakeNode(Dst, CE, N, stateNotEqual->BindExpr(CE, Res));
- }
- }
-
- return true;
-}
-
-static bool EvalOSAtomic(ExplodedNodeSet& Dst,
- GRExprEngine& Engine,
- GRStmtNodeBuilder& Builder,
- CallExpr* CE, SVal L,
- ExplodedNode* Pred) {
- const FunctionDecl* FD = L.getAsFunctionDecl();
- if (!FD)
- return false;
-
- const char *FName = FD->getNameAsCString();
-
- // Check for compare and swap.
- if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0 ||
- strncmp(FName, "objc_atomicCompareAndSwap", 25) == 0)
- return EvalOSAtomicCompareAndSwap(Dst, Engine, Builder, CE, Pred);
-
- // FIXME: Other atomics.
- return false;
-}
-
-//===----------------------------------------------------------------------===//
// Transfer function: Function calls.
//===----------------------------------------------------------------------===//
-static void MarkNoReturnFunction(const FunctionDecl *FD, CallExpr *CE,
- const GRState *state,
- GRStmtNodeBuilder *Builder) {
- if (!FD)
- return;
-
- if (FD->getAttr<NoReturnAttr>() ||
- FD->getAttr<AnalyzerNoReturnAttr>())
- Builder->BuildSinks = true;
- else {
- // HACK: Some functions are not marked noreturn, and don't return.
- // Here are a few hardwired ones. If this takes too long, we can
- // potentially cache these results.
- using llvm::StringRef;
- bool BuildSinks
- = llvm::StringSwitch<bool>(StringRef(FD->getIdentifier()->getName()))
- .Case("exit", true)
- .Case("panic", true)
- .Case("error", true)
- .Case("Assert", true)
- // FIXME: This is just a wrapper around throwing an exception.
- // Eventually inter-procedural analysis should handle this easily.
- .Case("ziperr", true)
- .Case("assfail", true)
- .Case("db_error", true)
- .Case("__assert", true)
- .Case("__assert_rtn", true)
- .Case("__assert_fail", true)
- .Case("dtrace_assfail", true)
- .Case("yy_fatal_error", true)
- .Case("_XCAssertionFailureHandler", true)
- .Case("_DTAssertionFailureHandler", true)
- .Case("_TSAssertionFailureHandler", true)
- .Default(false);
-
- if (BuildSinks)
- Builder->BuildSinks = true;
- }
-}
-
-bool GRExprEngine::EvalBuiltinFunction(const FunctionDecl *FD, CallExpr *CE,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- if (!FD)
- return false;
-
- unsigned id = FD->getBuiltinID();
- if (!id)
- return false;
-
- const GRState *state = Pred->getState();
-
- switch (id) {
- case Builtin::BI__builtin_expect: {
- // For __builtin_expect, just return the value of the subexpression.
- assert (CE->arg_begin() != CE->arg_end());
- SVal X = state->getSVal(*(CE->arg_begin()));
- MakeNode(Dst, CE, Pred, state->BindExpr(CE, X));
- return true;
- }
-
- case Builtin::BI__builtin_alloca: {
- // FIXME: Refactor into StoreManager itself?
- MemRegionManager& RM = getStateManager().getRegionManager();
- const MemRegion* R =
- RM.getAllocaRegion(CE, Builder->getCurrentBlockCount());
-
- // Set the extent of the region in bytes. This enables us to use the
- // SVal of the argument directly. If we save the extent in bits, we
- // cannot represent values like symbol*8.
- SVal Extent = state->getSVal(*(CE->arg_begin()));
- state = getStoreManager().setExtent(state, R, Extent);
- MakeNode(Dst, CE, Pred, state->BindExpr(CE, loc::MemRegionVal(R)));
- return true;
- }
- }
-
- return false;
-}
void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred,
CallExpr::arg_iterator AI,
@@ -1659,6 +1545,8 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred,
}
// Finally, evaluate the function call.
+ ExplodedNodeSet DstTmp3;
+
for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end();
DI != DE; ++DI) {
@@ -1667,39 +1555,39 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred,
// FIXME: Add support for symbolic function calls (calls involving
// function pointer values that are symbolic).
-
- // Check for the "noreturn" attribute.
-
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
- const FunctionDecl* FD = L.getAsFunctionDecl();
-
- MarkNoReturnFunction(FD, CE, state, Builder);
-
- // Evaluate the call.
- if (EvalBuiltinFunction(FD, CE, *DI, Dst))
- continue;
-
- // Dispatch to the plug-in transfer function.
- SaveOr OldHasGen(Builder->HasGeneratedNode);
- Pred = *DI;
+ ExplodedNodeSet DstChecker;
- // Dispatch to transfer function logic to handle the call itself.
- // FIXME: Allow us to chain together transfer functions.
- assert(Builder && "GRStmtNodeBuilder must be defined.");
- ExplodedNodeSet DstTmp;
+ // If the callee is processed by a checker, skip the rest logic.
+ if (CheckerEvalCall(CE, DstChecker, *DI))
+ DstTmp3.insert(DstChecker);
+ 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 && "GRStmtNodeBuilder must be defined.");
- if (!EvalOSAtomic(DstTmp, *this, *Builder, CE, L, Pred))
- getTF().EvalCall(DstTmp, *this, *Builder, CE, L, Pred);
+ 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 && DstTmp.empty() &&
- !Builder->HasGeneratedNode)
- MakeNode(DstTmp, CE, Pred, state);
-
- // Perform the post-condition check of the CallExpr.
- CheckerVisit(CE, Dst, DstTmp, false);
+ // 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);
+ }
+ }
}
+
+ // Perform the post-condition check of the CallExpr.
+ CheckerVisit(CE, Dst, DstTmp3, false);
}
//===----------------------------------------------------------------------===//
@@ -1922,10 +1810,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 +1819,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 +1897,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;
+ // 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);
+ // Dispatch to plug-in transfer function.
+ SaveOr OldHasGen(Builder->HasGeneratedNode);
+ EvalObjCMessageExpr(Dst, ME, Pred, Builder->GetState(Pred));
+ }
}
// Handle the case where no nodes where generated. Auto-generate that
@@ -2052,10 +1965,12 @@ void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL,
for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) {
const GRState* state = GetState(*I);
SVal ILV = state->getSVal(ILE);
- state = state->bindCompoundLiteral(CL, ILV);
+ const LocationContext *LC = (*I)->getLocationContext();
+ state = state->bindCompoundLiteral(CL, LC, ILV);
- if (asLValue)
- MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL)));
+ if (asLValue) {
+ MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC)));
+ }
else
MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV));
}