diff --git a/include/clang/Analysis/PathSensitive/GRCoreEngine.h b/include/clang/Analysis/PathSensitive/GRCoreEngine.h index 1dd19efb4c..e3a723fd63 100644 --- a/include/clang/Analysis/PathSensitive/GRCoreEngine.h +++ b/include/clang/Analysis/PathSensitive/GRCoreEngine.h @@ -76,7 +76,7 @@ protected: void HandlePostStmt(const PostStmt& S, CFGBlock* B, unsigned StmtIdx, ExplodedNodeImpl *Pred); - void HandleBranch(Expr* Cond, Stmt* Term, CFGBlock* B, + void HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock* B, ExplodedNodeImpl* Pred); virtual void ProcessEndPath(GREndPathNodeBuilderImpl& Builder) = 0; @@ -86,7 +86,7 @@ protected: virtual void ProcessStmt(Stmt* S, GRStmtNodeBuilderImpl& Builder) = 0; - virtual void ProcessBranch(Expr* Condition, Stmt* Terminator, + virtual void ProcessBranch(Stmt* Condition, Stmt* Terminator, GRBranchNodeBuilderImpl& Builder) = 0; virtual void ProcessIndirectGoto(GRIndirectGotoNodeBuilderImpl& Builder) = 0; @@ -593,7 +593,7 @@ protected: BC); } - virtual void ProcessBranch(Expr* Condition, Stmt* Terminator, + virtual void ProcessBranch(Stmt* Condition, Stmt* Terminator, GRBranchNodeBuilderImpl& BuilderImpl) { GRBranchNodeBuilder Builder(BuilderImpl); SubEngine.ProcessBranch(Condition, Terminator, Builder); diff --git a/include/clang/Analysis/PathSensitive/GRExprEngine.h b/include/clang/Analysis/PathSensitive/GRExprEngine.h index 45874bb681..4dd5d394b8 100644 --- a/include/clang/Analysis/PathSensitive/GRExprEngine.h +++ b/include/clang/Analysis/PathSensitive/GRExprEngine.h @@ -368,7 +368,7 @@ public: /// ProcessBranch - Called by GRCoreEngine. Used to generate successor /// nodes by processing the 'effects' of a branch condition. - void ProcessBranch(Expr* Condition, Stmt* Term, BranchNodeBuilder& builder); + void ProcessBranch(Stmt* Condition, Stmt* Term, BranchNodeBuilder& builder); /// ProcessIndirectGoto - Called by GRCoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. @@ -423,17 +423,17 @@ protected: return StateMgr.BindLoc(St, LV, V); } - SVal GetSVal(const GRState* St, Expr* Ex) { + SVal GetSVal(const GRState* St, Stmt* Ex) { return StateMgr.GetSVal(St, Ex); } - SVal GetSVal(const GRState* St, const Expr* Ex) { - return GetSVal(St, const_cast(Ex)); + SVal GetSVal(const GRState* St, const Stmt* Ex) { + return GetSVal(St, const_cast(Ex)); } - SVal GetBlkExprSVal(const GRState* St, Expr* Ex) { + SVal GetBlkExprSVal(const GRState* St, Stmt* Ex) { return StateMgr.GetBlkExprSVal(St, Ex); - } + } SVal GetSVal(const GRState* St, Loc LV, QualType T = QualType()) { return StateMgr.GetSVal(St, LV, T); @@ -537,6 +537,11 @@ protected: /// VisitObjCIvarRefExpr - Transfer function logic for ObjCIvarRefExprs. void VisitObjCIvarRefExpr(ObjCIvarRefExpr* DR, NodeTy* Pred, NodeSet& Dst, bool asLValue); + + /// VisitObjCForCollectionStmt - Transfer function logic for + /// ObjCForCollectionStmt. + void VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, NodeTy* Pred, + NodeSet& Dst); /// VisitObjCMessageExpr - Transfer function for ObjC message expressions. void VisitObjCMessageExpr(ObjCMessageExpr* ME, NodeTy* Pred, NodeSet& Dst); diff --git a/lib/Analysis/GRCoreEngine.cpp b/lib/Analysis/GRCoreEngine.cpp index 99e2a7f06e..44c9b4871f 100644 --- a/lib/Analysis/GRCoreEngine.cpp +++ b/lib/Analysis/GRCoreEngine.cpp @@ -1,4 +1,4 @@ -//==- GRCoreEngine.cpp - Path-Sensitive Dataflow Engine ----------------*- C++ -*-// +//==- GRCoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- C++ -*-// // // The LLVM Compiler Infrastructure // @@ -212,6 +212,21 @@ void GRCoreEngineImpl::HandleBlockExit(CFGBlock * B, ExplodedNodeImpl* Pred) { return; } + case Stmt::ObjCForCollectionStmtClass: { + // In the case of ObjCForCollectionStmt, it appears twice in a CFG: + // + // (1) inside a basic block, which represents the binding of the + // 'element' variable to a value. + // (2) in a terminator, which represents the branch. + // + // For (1), subengines will bind a value (i.e., 0 or 1) indicating + // whether or not collection contains any more elements. We cannot + // just test to see if the element is nil because a container can + // contain nil elements. + HandleBranch(Term, Term, B, Pred); + return; + } + case Stmt::SwitchStmtClass: { GRSwitchNodeBuilderImpl builder(Pred, B, cast(Term)->getCond(), @@ -233,8 +248,8 @@ void GRCoreEngineImpl::HandleBlockExit(CFGBlock * B, ExplodedNodeImpl* Pred) { GenerateNode(BlockEdge(B, *(B->succ_begin())), Pred->State, Pred); } -void GRCoreEngineImpl::HandleBranch(Expr* Cond, Stmt* Term, CFGBlock * B, - ExplodedNodeImpl* Pred) { +void GRCoreEngineImpl::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, + ExplodedNodeImpl* Pred) { assert (B->succ_size() == 2); GRBranchNodeBuilderImpl Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index 8e6d5ab5cc..2fa163ad23 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -366,6 +366,10 @@ void GRExprEngine::Visit(Stmt* S, NodeTy* Pred, NodeSet& Dst) { case Stmt::ObjCIvarRefExprClass: VisitObjCIvarRefExpr(cast(S), Pred, Dst, false); break; + + case Stmt::ObjCForCollectionStmtClass: + VisitObjCForCollectionStmt(cast(S), Pred, Dst); + break; case Stmt::ObjCMessageExprClass: { VisitObjCMessageExpr(cast(S), Pred, Dst); @@ -547,7 +551,7 @@ const GRState* GRExprEngine::MarkBranch(const GRState* St, } } -void GRExprEngine::ProcessBranch(Expr* Condition, Stmt* Term, +void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, BranchNodeBuilder& builder) { // Remove old bindings for subexpressions. @@ -1343,6 +1347,79 @@ void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, else EvalLoad(Dst, Ex, *I, St, location); } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C fast enumeration 'for' statements. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, + NodeTy* Pred, NodeSet& Dst) { + + // ObjCForCollectionStmts are processed in two places. This method + // handles the case where an ObjCForCollectionStmt* occurs as one of the + // statements within a basic block. This transfer function does two things: + // + // (1) binds the next container value to 'element'. This creates a new + // node in the ExplodedGraph. + // + // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating + // whether or not the container has any more elements. This value + // will be tested in ProcessBranch. We need to explicitly bind + // this value because a container can contain nil elements. + // + // FIXME: Eventually this logic should actually do dispatches to + // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). + // This will require simulating a temporary NSFastEnumerationState, either + // through an SVal or through the use of MemRegions. This value can + // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop + // terminates we reclaim the temporary (it goes out of scope) and we + // we can test if the SVal is 0 or if the MemRegion is null (depending + // on what approach we take). + // + // For now: simulate (1) by assigning either a symbol or nil if the + // container is empty. Thus this transfer function will by default + // result in state splitting. + + Stmt* elem = S->getElement(); + VarDecl* ElemD; + bool bindDecl = false; + + if (DeclStmt* DS = dyn_cast(elem)) { + ElemD = cast(DS->getSolitaryDecl()); + assert (ElemD->getInit() == 0); + bindDecl = true; + } + else + ElemD = cast(cast(elem)->getDecl()); + + + // Get the current state, as well as the QualType for 'int' and the + // type of the element. + GRStateRef state = GRStateRef(GetState(Pred), getStateManager()); + QualType IntTy = getContext().IntTy; + QualType ElemTy = ElemD->getType(); + + // Handle the case where the container still has elements. + SVal TrueV = NonLoc::MakeVal(getBasicVals(), 1, IntTy); + GRStateRef hasElems = state.BindExpr(S, TrueV); + + assert (Loc::IsLocType(ElemTy)); + unsigned Count = Builder->getCurrentBlockCount(); + loc::SymbolVal SymV(SymMgr.getConjuredSymbol(elem, ElemTy, Count)); + + if (bindDecl) + hasElems = hasElems.BindDecl(ElemD, &SymV, Count); + else + hasElems = hasElems.BindLoc(hasElems.GetLValue(ElemD), SymV); + + + // Handle the case where the container has no elements. + + + + + } //===----------------------------------------------------------------------===// @@ -1630,21 +1707,44 @@ void GRExprEngine::VisitDeclStmt(DeclStmt* DS, NodeTy* Pred, NodeSet& Dst) { const VarDecl* VD = dyn_cast(D); - Expr* Ex = const_cast(VD->getInit()); + Expr* InitEx = const_cast(VD->getInit()); // FIXME: static variables may have an initializer, but the second // time a function is called those values may not be current. NodeSet Tmp; - if (Ex) - Visit(Ex, Pred, Tmp); + if (InitEx) + Visit(InitEx, Pred, Tmp); if (Tmp.empty()) Tmp.Add(Pred); for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { const GRState* St = GetState(*I); - St = StateMgr.BindDecl(St, VD, Ex, Builder->getCurrentBlockCount()); + unsigned Count = Builder->getCurrentBlockCount(); + + if (InitEx) { + SVal InitVal = GetSVal(St, InitEx); + QualType T = VD->getType(); + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown()) { + if (Loc::IsLocType(T)) { + SymbolID Sym = SymMgr.getConjuredSymbol(InitEx, Count); + InitVal = loc::SymbolVal(Sym); + } + else if (T->isIntegerType()) { + SymbolID Sym = SymMgr.getConjuredSymbol(InitEx, Count); + InitVal = nonloc::SymbolVal(Sym); + } + } + + St = StateMgr.BindDecl(St, VD, &InitVal, Count); + } + else + St = StateMgr.BindDecl(St, VD, 0, Count); + MakeNode(Dst, DS, *I, St); } }