]> granicus.if.org Git - clang/commitdiff
Use EvalSummary to process message expressions, thereby unifying the checker
authorTed Kremenek <kremenek@apple.com>
Tue, 6 May 2008 04:20:12 +0000 (04:20 +0000)
committerTed Kremenek <kremenek@apple.com>
Tue, 6 May 2008 04:20:12 +0000 (04:20 +0000)
logic for function calls and message expressions.

Use the following heuristic to infer "allocating" instance methods:

  [ClassName classWithXXX]  allocates an object

Update testcase to reflect this heuristic.

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

lib/Analysis/CFRefCount.cpp
test/Analysis-Apple/CFDate.m

index f3e68886b110b74a5dddabce2bcd5e029eba63c9..25d1ed0299f5b9e0cd7e3f9baeba6cec54b6a2d9 100644 (file)
@@ -234,9 +234,7 @@ class RetainSummaryManager {
     return getPersistentSummary(getArgEffects(), RE, ReceiverEff);
   }
   
-  RetainSummary* getInstanceMethodSummary(Selector S);
-  
-  RetainSummary* getMethodSummary(Selector S);    
+
   RetainSummary* getInitMethodSummary(Selector S);
 
   void InitializeInstMethSummaries();
@@ -255,6 +253,9 @@ public:
   
   RetainSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx);
   
+  RetainSummary* getMethodSummary(Selector S);    
+  RetainSummary* getInstanceMethodSummary(IdentifierInfo* ClsName, Selector S);
+  
   bool isGCEnabled() const { return GCEnabled; }
 };
   
@@ -590,15 +591,65 @@ void RetainSummaryManager::InitializeMethSummaries() {
 }
 
 
-RetainSummary* RetainSummaryManager::getInstanceMethodSummary(Selector S) {
+RetainSummary*
+RetainSummaryManager::getInstanceMethodSummary(IdentifierInfo* ClsName,
+                                               Selector S) {
     
   // Look up a summary in our cache of Selectors -> Summaries.
   ObjCMethSummariesTy::iterator I = ObjCInstMethSummaries.find(S);
   
   if (I != ObjCInstMethSummaries.end())
     return I->second;
+  
+  // Don't track anything if using GC.
+  if (isGCEnabled())
+    return 0;
+  
+  // Heuristic: XXXXwithYYYY, where XXX is the class name with the "NS"
+  //  stripped off is usually an allocation.
+  
+  const char* cls = ClsName->getName();
+  const char* s = S.getIdentifierInfoForSlot(0)->getName();
+  
+  if (cls[0] == 'N' && cls[1] == 'S')
+    cls += 2;
+  
+  if (cls[0] == '\0' || s[0] == '\0' || tolower(cls[0]) != s[0])
+    return 0;  
+  
+  ++cls;
+  ++s;
     
-  return 0;
+  // Now look at the rest of the characters.
+  unsigned len = strlen(cls);
+  assert (len);
+
+  // Prefix matches?
+  if (strncmp(cls, s, len))
+    return 0;
+  
+  s += len;  
+  
+  // If 's' is the same as clsName, or 's' has "With" after the class name,
+  // treat it as an allocator.
+  do {
+  
+    if (s[0] == '\0')
+      break;
+  
+    if (s[0]=='\0' || s[0]!='W' || s[1]!='i' || s[2]!='t' || s[3]!='h')
+      break;
+    
+    return 0;
+    
+  } while(0);
+  
+  // Generate the summary.
+  
+  assert (ScratchArgs.empty());
+  RetainSummary* Summ = getPersistentSummary(RetEffect::MakeOwned());
+  ObjCInstMethSummaries[S] = Summ;
+  return Summ;
 }
 
 //===----------------------------------------------------------------------===//
@@ -1183,140 +1234,18 @@ void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<ValueState>& Dst,
                                      ObjCMessageExpr* ME,
                                      ExplodedNode<ValueState>* Pred) {
   
-  if (!EvalObjCMessageExprAux(Dst, Eng, Builder, ME, Pred))
-    return;
-  
-  // The basic transfer function logic for message expressions does nothing.
-  // We just invalidate all arguments passed in by references.
+  RetainSummary* Summ;
   
-  ValueStateManager& StateMgr = Eng.getStateManager();
-  ValueState* St = Builder.GetState(Pred);
-  RefBindings B = GetRefBindings(*St);
-  
-  for (ObjCMessageExpr::arg_iterator I = ME->arg_begin(), E = ME->arg_end();
-       I != E; ++I) {
-    
-    RVal V = StateMgr.GetRVal(St, *I);
-    
-    if (isa<LVal>(V)) {
-
-      LVal lv = cast<LVal>(V);
-      
-      // Did the lval bind to a symbol?
-      RVal X = StateMgr.GetRVal(St, lv);
-      
-      if (isa<lval::SymbolVal>(X)) {
-        SymbolID Sym = cast<lval::SymbolVal>(X).getSymbol();
-        B = Remove(B, Sym);
-        
-        // Create a new state with the updated bindings.  
-        ValueState StVals = *St;
-        SetRefBindings(StVals, B);
-        St = StateMgr.getPersistentState(StVals);
-      }
-        
-      St = StateMgr.SetRVal(St, cast<LVal>(V), UnknownVal());
-    }
-  }
-  
-  Builder.MakeNode(Dst, ME, Pred, St);
-}
-
-bool CFRefCount::EvalObjCMessageExprAux(ExplodedNodeSet<ValueState>& Dst,
-                                        GRExprEngine& Eng,
-                                        GRStmtNodeBuilder<ValueState>& Builder,
-                                        ObjCMessageExpr* ME,
-                                        ExplodedNode<ValueState>* Pred) {
-  
-  if (isGCEnabled())
-    return true;
-  
-  // Handle "toll-free bridging" of calls to "Release" and "Retain".
-  
-  // FIXME: track the underlying object type associated so that we can
-  //  flag illegal uses of toll-free bridging (or at least handle it
-  //  at casts).
-  
-  Selector S = ME->getSelector();
-  
-  if (!S.isUnarySelector())
-    return true;
-
-  Expr* Receiver = ME->getReceiver();
-  
-  if (!Receiver)
-    return true;
-
-  // Check if we are calling "autorelease".
-
-  enum { IsRelease, IsRetain, IsAutorelease, IsNone } mode = IsNone;
-  
-  if (S == AutoreleaseSelector)
-    mode = IsAutorelease;
-  else if (S == RetainSelector)
-    mode = IsRetain;
-  else if (S == ReleaseSelector)
-    mode = IsRelease;
-  
-  if (mode == IsNone)
-    return true;
-  
-  // We have "retain", "release", or "autorelease".  
-  ValueStateManager& StateMgr = Eng.getStateManager();
-  ValueState* St = Builder.GetState(Pred);
-  RVal V = StateMgr.GetRVal(St, Receiver);
-  
-  // Was the argument something we are not tracking?  
-  if (!isa<lval::SymbolVal>(V))
-    return true;
-
-  // Get the bindings.
-  SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
-  RefBindings B = GetRefBindings(*St);
-  
-  // Find the tracked value.
-  RefBindings::TreeTy* T = B.SlimFind(Sym);
-
-  if (!T)
-    return true;
-
-  RefVal::Kind hasErr = (RefVal::Kind) 0;
-  
-  // Update the bindings.
-  switch (mode) {
-    case IsNone:
-      assert(false);
-      
-    case IsRelease:
-      B = Update(B, Sym, T->getValue().second, DecRef, hasErr);
-      break;
-      
-    case IsRetain:
-      B = Update(B, Sym, T->getValue().second, IncRef, hasErr);
-      break;
-      
-    case IsAutorelease:
-      // For now we just stop tracking a value if we see
-      // it sent "autorelease."  In the future we can potentially
-      // track the associated pool.
-      B = Remove(B, Sym);
-      break;
-  }
-
-  // Create a new state with the updated bindings.  
-  ValueState StVals = *St;
-  SetRefBindings(StVals, B);
-  St = Eng.SetRVal(StateMgr.getPersistentState(StVals), ME, V);
-  
-  // Create an error node if it exists.  
-  if (hasErr)
-    ProcessNonLeakError(Dst, Builder, ME, Receiver, Pred, St, hasErr, Sym);
+  if (ME->getReceiver())
+    Summ = Summaries.getMethodSummary(ME->getSelector());
   else
-    Builder.MakeNode(Dst, ME, Pred, St);
+    Summ = Summaries.getInstanceMethodSummary(ME->getClassName(),
+                                              ME->getSelector());
 
-  return false;
+  EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), Summ,
+              ME->arg_begin(), ME->arg_end(), Pred);
 }
-
+  
 // Stores.
 
 void CFRefCount::EvalStore(ExplodedNodeSet<ValueState>& Dst,
index 09db82a161bdd8e4fd12207de5d5a194be01bb5b..4f39fc68eb049fd795b48be43d0eb2c68ae619a3 100644 (file)
@@ -92,3 +92,13 @@ CFDateRef f7() {
   date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); //expected-warning{{leak}}
   return date;
 }
+
+NSDate* f8(int x) {
+  NSDate* date = [NSDate date];
+  
+  if (x) [date retain];
+  
+  return date; // expected-warning{{leak}}
+}
+