]> granicus.if.org Git - clang/commitdiff
Add (preliminary) transfer function support for ObjCForCollectionStmt. Still need...
authorTed Kremenek <kremenek@apple.com>
Wed, 12 Nov 2008 19:24:17 +0000 (19:24 +0000)
committerTed Kremenek <kremenek@apple.com>
Wed, 12 Nov 2008 19:24:17 +0000 (19:24 +0000)
When processing DeclStmt, use the new interface to StateManager::BindDecl.  Conjuring of symbols is now done in VisitDeclStmt.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59155 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/PathSensitive/GRCoreEngine.h
include/clang/Analysis/PathSensitive/GRExprEngine.h
lib/Analysis/GRCoreEngine.cpp
lib/Analysis/GRExprEngine.cpp

index 1dd19efb4c320779ef07392cb713ae21b30a3f9f..e3a723fd637e5554aeb2e96de4f44cbc5c0231e5 100644 (file)
@@ -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<StateTy> Builder(BuilderImpl);
     SubEngine.ProcessBranch(Condition, Terminator, Builder);    
index 45874bb6819e95e7088f7c6d2a240d5500506302..4dd5d394b8d52d8e10b126a243ac15fed827ca67 100644 (file)
@@ -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<Expr*>(Ex));
+  SVal GetSVal(const GRState* St, const Stmt* Ex) {
+    return GetSVal(St, const_cast<Stmt*>(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);
index 99e2a7f06e010f0fcdaba1e7857ffa727fe1a915..44c9b4871f3446575481f151f14a9868d07850f4 100644 (file)
@@ -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<SwitchStmt>(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),
index 8e6d5ab5cc35057202b38b3308d1e766830444b8..2fa163ad230ea8f399fadeeaf08bba91d8a804c5 100644 (file)
@@ -366,6 +366,10 @@ void GRExprEngine::Visit(Stmt* S, NodeTy* Pred, NodeSet& Dst) {
     case Stmt::ObjCIvarRefExprClass:
       VisitObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst, false);
       break;
+
+    case Stmt::ObjCForCollectionStmtClass:
+      VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst);
+      break;
       
     case Stmt::ObjCMessageExprClass: {
       VisitObjCMessageExpr(cast<ObjCMessageExpr>(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<DeclStmt>(elem)) {
+    ElemD = cast<VarDecl>(DS->getSolitaryDecl());
+    assert (ElemD->getInit() == 0);
+    bindDecl = true;
+  }
+  else
+    ElemD = cast<VarDecl>(cast<DeclRefExpr>(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<VarDecl>(D);
   
-  Expr* Ex = const_cast<Expr*>(VD->getInit());
+  Expr* InitEx = const_cast<Expr*>(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);
   }
 }