]> granicus.if.org Git - clang/commitdiff
[analyzer] RetainCountChecker: CF properties are always manually retain-counted.
authorJordan Rose <jordan_rose@apple.com>
Sat, 7 Mar 2015 05:47:24 +0000 (05:47 +0000)
committerJordan Rose <jordan_rose@apple.com>
Sat, 7 Mar 2015 05:47:24 +0000 (05:47 +0000)
In theory we could assume a CF property is stored at +0 if there's not a custom
setter, but that's not really worth the complexity. What we do know is that a
CF property can't have ownership attributes, and so we shouldn't assume anything
about the ownership of the ivar.

rdar://problem/20076963

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

lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
test/Analysis/properties.m

index 55c7a65e6a600479f1a96786ee8ddf0848ce28a8..1991d2bad1b410c3a9b0224f82a07807febadfb5 100644 (file)
@@ -2882,9 +2882,10 @@ void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
 
     // Also don't do anything if the ivar is unretained. If so, we know that
     // there's no outstanding retain count for the value.
-    if (const ObjCPropertyDecl *Prop = findPropForIvar(IRE->getDecl()))
-      if (!Prop->isRetaining())
-        return;
+    if (Kind == RetEffect::ObjC)
+      if (const ObjCPropertyDecl *Prop = findPropForIvar(IRE->getDecl()))
+        if (!Prop->isRetaining())
+          return;
 
     // Note that this value has been loaded from an ivar.
     C.addTransition(setRefBinding(State, Sym, RV->withIvarAccess()));
@@ -2900,12 +2901,16 @@ void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
   }
 
   // Try to find the property associated with this ivar.
-  const ObjCPropertyDecl *Prop = findPropForIvar(IRE->getDecl());
-
-  if (Prop && !Prop->isRetaining())
-    State = setRefBinding(State, Sym, PlusZero);
-  else
+  if (Kind != RetEffect::ObjC) {
     State = setRefBinding(State, Sym, PlusZero.withIvarAccess());
+  } else {
+    const ObjCPropertyDecl *Prop = findPropForIvar(IRE->getDecl());
+
+    if (Prop && !Prop->isRetaining())
+      State = setRefBinding(State, Sym, PlusZero);
+    else
+      State = setRefBinding(State, Sym, PlusZero.withIvarAccess());
+  }
 
   C.addTransition(State);
 }
index ea8d19587a93fd60e285c4b4d2df5ff62898035f..6f92c6ec32ff6804bd6e318384532c4404779c40 100644 (file)
@@ -3,6 +3,10 @@
 
 void clang_analyzer_eval(int);
 
+typedef const void * CFTypeRef;
+extern CFTypeRef CFRetain(CFTypeRef cf);
+void CFRelease(CFTypeRef cf);
+
 typedef signed char BOOL;
 typedef unsigned int NSUInteger;
 typedef struct _NSZone NSZone;
@@ -352,6 +356,7 @@ void testOpaqueConsistency(OpaqueIntWrapper *w) {
 @property (strong) id ownedProp;
 @property (unsafe_unretained) id unownedProp;
 @property (nonatomic, strong) id manualProp;
+@property CFTypeRef cfProp;
 @end
 
 @implementation RetainCountTesting {
@@ -382,6 +387,13 @@ void testOpaqueConsistency(OpaqueIntWrapper *w) {
   [_ivarOnly release]; // expected-warning{{used after it is released}}
 }
 
+- (void)testOverreleaseCF {
+  CFRetain(_cfProp);
+  CFRelease(_cfProp);
+  CFRelease(_cfProp);
+  CFRelease(_cfProp); // expected-warning{{used after it is released}}
+}
+
 - (void)testOverreleaseOwnedIvarUse {
   [_ownedProp retain];
   [_ownedProp release];
@@ -396,6 +408,15 @@ void testOpaqueConsistency(OpaqueIntWrapper *w) {
   [_ivarOnly myMethod]; // expected-warning{{used after it is released}}
 }
 
+- (void)testOverreleaseCFUse {
+  CFRetain(_cfProp);
+  CFRelease(_cfProp);
+  CFRelease(_cfProp);
+
+  extern void CFUse(CFTypeRef);
+  CFUse(_cfProp); // expected-warning{{used after it is released}}
+}
+
 - (void)testOverreleaseOwnedIvarAutoreleaseOkay {
   [_ownedProp retain];
   [_ownedProp release];
@@ -465,6 +486,21 @@ void testOpaqueConsistency(OpaqueIntWrapper *w) {
   [fromIvar release]; // no-warning
 }
 
+- (void)testPropertyAccessThenReleaseCF {
+  CFTypeRef owned = CFRetain(self.cfProp);
+  CFRelease(owned);
+  CFRelease(_cfProp); // no-warning
+  clang_analyzer_eval(owned == _cfProp); // expected-warning{{TRUE}}
+}
+
+- (void)testPropertyAccessThenReleaseCF2 {
+  CFTypeRef fromIvar = _cfProp;
+  CFTypeRef owned = CFRetain(self.cfProp);
+  CFRelease(owned);
+  CFRelease(fromIvar);
+  clang_analyzer_eval(owned == fromIvar); // expected-warning{{TRUE}}
+}
+
 - (id)getUnownedFromProperty {
   [_ownedProp retain];
   [_ownedProp autorelease];
@@ -498,6 +534,11 @@ void testOpaqueConsistency(OpaqueIntWrapper *w) {
   [_ivarOnly release]; // FIXME: no-warning{{not owned}}
 }
 
+- (void)testAssignCF:(CFTypeRef)newValue {
+  _cfProp = newValue;
+  CFRelease(_cfProp); // FIXME: no-warning{{not owned}}
+}
+
 - (void)testAssignOwnedOkay:(id)newValue {
   _ownedProp = [newValue retain];
   [_ownedProp release]; // no-warning
@@ -513,6 +554,11 @@ void testOpaqueConsistency(OpaqueIntWrapper *w) {
   [_ivarOnly release]; // no-warning
 }
 
+- (void)testAssignCFOkay:(CFTypeRef)newValue {
+  _cfProp = CFRetain(newValue);
+  CFRelease(_cfProp); // no-warning
+}
+
 // rdar://problem/19862648
 - (void)establishIvarIsNilDuringLoops {
   extern id getRandomObject();