[llvm-branch-commits] [cfe-branch] r71417 - in /cfe/branches/Apple/Dib: lib/Analysis/CFRefCount.cpp test/Analysis/retain-release-gc-only.m test/Analysis/retain-release.m

Mike Stump mrs at apple.com
Sun May 10 20:42:11 PDT 2009


Author: mrs
Date: Sun May 10 22:42:11 2009
New Revision: 71417

URL: http://llvm.org/viewvc/llvm-project?rev=71417&view=rev
Log:
Merge in 71388:

retain/release checker: Flag a warning for non-owned objects returned
where an owned one is expected.  Also add preliminary checking for
returning a positive retain count object in GC mode where an owned GC
object is expected.

Modified:
    cfe/branches/Apple/Dib/lib/Analysis/CFRefCount.cpp
    cfe/branches/Apple/Dib/test/Analysis/retain-release-gc-only.m
    cfe/branches/Apple/Dib/test/Analysis/retain-release.m

Modified: cfe/branches/Apple/Dib/lib/Analysis/CFRefCount.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/Apple/Dib/lib/Analysis/CFRefCount.cpp?rev=71417&r1=71416&r2=71417&view=diff

==============================================================================
--- cfe/branches/Apple/Dib/lib/Analysis/CFRefCount.cpp (original)
+++ cfe/branches/Apple/Dib/lib/Analysis/CFRefCount.cpp Sun May 10 22:42:11 2009
@@ -296,7 +296,7 @@
   bool isOwned() const {
     return K == OwnedSymbol || K == OwnedAllocatedSymbol;
   }
-  
+    
   static RetEffect MakeAlias(unsigned Idx) {
     return RetEffect(Alias, Idx);
   }
@@ -1429,7 +1429,9 @@
     ErrorLeak,  // A memory leak due to excessive reference counts.
     ErrorLeakReturned, // A memory leak due to the returning method not having
                       // the correct naming conventions.
-    ErrorOverAutorelease
+    ErrorGCLeakReturned,
+    ErrorOverAutorelease,
+    ErrorReturnedNotOwned
   };
 
 private:  
@@ -1586,6 +1588,10 @@
       Out << "Leaked (Bad naming)";
       break;
       
+    case ErrorGCLeakReturned:
+      Out << "Leaked (GC-ed at return)";
+      break;
+
     case ErrorUseAfterRelease:
       Out << "Use-After-Release [ERROR]";
       break;
@@ -1597,6 +1603,10 @@
     case RefVal::ErrorOverAutorelease:
       Out << "Over autoreleased";
       break;
+      
+    case RefVal::ErrorReturnedNotOwned:
+      Out << "Non-owned object returned instead of owned";
+      break;
   }
   
   if (ACnt) {
@@ -1695,6 +1705,7 @@
   BugType *deallocGC, *deallocNotOwned;
   BugType *leakWithinFunction, *leakAtReturn;
   BugType *overAutorelease;
+  BugType *returnNotOwnedForOwned;
   BugReporter *BR;
   
   GRStateRef Update(GRStateRef state, SymbolRef sym, RefVal V, ArgEffect E,
@@ -1721,7 +1732,8 @@
     : Summaries(Ctx, gcenabled),
       LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),
       deallocGC(0), deallocNotOwned(0),
-      leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), BR(0) {}
+      leakWithinFunction(0), leakAtReturn(0), overAutorelease(0),
+      returnNotOwnedForOwned(0), BR(0) {}
   
   virtual ~CFRefCount() {}
   
@@ -1925,6 +1937,17 @@
     }
   };
   
+  class VISIBILITY_HIDDEN ReturnedNotOwnedForOwned : public CFRefBug {
+  public:
+    ReturnedNotOwnedForOwned(CFRefCount *tf) :
+      CFRefBug(tf, "Method should return an owned object") {}
+    
+    const char *getDescription() const {
+      return "Object with +0 retain counts returned to caller where a +1 "
+             "(owning) retain count is expected";
+    }
+  };
+  
   class VISIBILITY_HIDDEN Leak : public CFRefBug {
     const bool isReturn;
   protected:
@@ -2027,6 +2050,9 @@
   overAutorelease = new OverAutorelease(this);
   BR.Register(overAutorelease);
   
+  returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this);
+  BR.Register(returnNotOwnedForOwned);
+  
   // First register "return" leaks.
   const char* name = 0;
   
@@ -2488,6 +2514,13 @@
     " 'new' or 'alloc'.  This violates the naming convention rules given"
     " in the Memory Management Guide for Cocoa (object leaked)";
   }
+  else if (RV->getKind() == RefVal::ErrorGCLeakReturned) {
+    ObjCMethodDecl& MD = cast<ObjCMethodDecl>(BRC.getCodeDecl());
+    os << " and returned from method '" << MD.getSelector().getAsString()
+       << "' is potentially leaked when using garbage collection.  Callers"
+          " of this method do not expect a +1 retain count since the return"
+          " type is an Objective-C object reference";
+  }
   else
     os << " is no longer referenced after this point and has a retain count of"
           " +" << RV->getCount() << " (object leaked)";
@@ -3031,26 +3064,65 @@
     
   // Any leaks or other errors?
   if (X.isReturnedOwned() && X.getCount() == 0) {
-    const Decl *CD = &Eng.getStateManager().getCodeDecl();
-    
+    const Decl *CD = &Eng.getStateManager().getCodeDecl();    
     if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {      
       const RetainSummary &Summ = *Summaries.getMethodSummary(MD);
-      if (!Summ.getRetEffect().isOwned()) {
-        static int ReturnOwnLeakTag = 0;
-        state = state.set<RefBindings>(Sym, X ^ RefVal::ErrorLeakReturned);
+      RetEffect RE = Summ.getRetEffect();
+      bool hasError = false;
+
+      if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) {
+        // Things are more complicated with garbage collection.  If the
+        // returned object is suppose to be an Objective-C object, we have
+        // a leak (as the caller expects a GC'ed object).        
+        X = X ^ RefVal::ErrorGCLeakReturned;
+        
+        // Keep this false until this is properly tested.
+        hasError = false;
+      }
+      else if (!RE.isOwned()) {
+        // Either we are using GC and the returned object is a CF type
+        // or we aren't using GC.  In either case, we expect that the
+        // enclosing method is expected to return ownership.        
+        hasError = true;
+        X = X ^ RefVal::ErrorLeakReturned;
+      }
+      
+      if (hasError) {        
         // Generate an error node.
-        if (ExplodedNode<GRState> *N =
-            Builder.generateNode(PostStmt(S, &ReturnOwnLeakTag), state, Pred)) {
-          CFRefLeakReport *report =
+        static int ReturnOwnLeakTag = 0;
+        state = state.set<RefBindings>(Sym, X);
+        ExplodedNode<GRState> *N =
+          Builder.generateNode(PostStmt(S, &ReturnOwnLeakTag), state, Pred);
+        if (N) {
+          CFRefReport *report =
             new CFRefLeakReport(*static_cast<CFRefBug*>(leakAtReturn), *this,
                                 N, Sym, Eng);
           BR->EmitReport(report);
         }
       }
+    } 
+  }
+  else if (X.isReturnedNotOwned()) {
+    const Decl *CD = &Eng.getStateManager().getCodeDecl();    
+    if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
+      const RetainSummary &Summ = *Summaries.getMethodSummary(MD);
+      if (Summ.getRetEffect().isOwned()) {
+        // Trying to return a not owned object to a caller expecting an
+        // owned object.
+        
+        static int ReturnNotOwnedForOwnedTag = 0;
+        state = state.set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned);
+        if (ExplodedNode<GRState> *N =
+              Builder.generateNode(PostStmt(S, &ReturnNotOwnedForOwnedTag),
+                                   state, Pred)) {
+            CFRefReport *report =
+                new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned),
+                                *this, N, Sym);
+            BR->EmitReport(report);
+        }
+      }
     }
   }
-  
-
 }
 
 // Assumptions.

Modified: cfe/branches/Apple/Dib/test/Analysis/retain-release-gc-only.m
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/Apple/Dib/test/Analysis/retain-release-gc-only.m?rev=71417&r1=71416&r2=71417&view=diff

==============================================================================
--- cfe/branches/Apple/Dib/test/Analysis/retain-release-gc-only.m (original)
+++ cfe/branches/Apple/Dib/test/Analysis/retain-release-gc-only.m Sun May 10 22:42:11 2009
@@ -124,6 +124,20 @@
   CFRetain(A);
 }
 
+// Test return of non-owned objects in contexts where an owned object
+// is expected.
+ at interface TestReturnNotOwnedWhenExpectedOwned
+- (NSString*)newString;
+ at end
+
+ at implementation TestReturnNotOwnedWhenExpectedOwned
+- (NSString*)newString {
+  NSString *s = [NSString stringWithUTF8String:"hello"];  
+  // FIXME: Should this be an error anyway?
+  return s; // no-warning
+}
+ at end
+
 //===----------------------------------------------------------------------===//
 // Tests of ownership attributes.
 //===----------------------------------------------------------------------===//

Modified: cfe/branches/Apple/Dib/test/Analysis/retain-release.m
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/Apple/Dib/test/Analysis/retain-release.m?rev=71417&r1=71416&r2=71417&view=diff

==============================================================================
--- cfe/branches/Apple/Dib/test/Analysis/retain-release.m (original)
+++ cfe/branches/Apple/Dib/test/Analysis/retain-release.m Sun May 10 22:42:11 2009
@@ -341,6 +341,19 @@
 }
 @end
 
+// Test return of non-owned objects in contexts where an owned object
+// is expected.
+ at interface TestReturnNotOwnedWhenExpectedOwned
+- (NSString*)newString;
+ at end
+
+ at implementation TestReturnNotOwnedWhenExpectedOwned
+- (NSString*)newString {
+  NSString *s = [NSString stringWithUTF8String:"hello"];
+  return s; // expected-warning{{Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected}}
+}
+ at end
+
 // <rdar://problem/6659160>
 int isFoo(char c);
 





More information about the llvm-branch-commits mailing list