From 4803293ca39f7023d6308df43a65d142be5cf8d3 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Sat, 7 Mar 2015 05:47:24 +0000 Subject: [PATCH] [analyzer] RetainCountChecker: CF properties are always manually retain-counted. 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 --- .../Checkers/RetainCountChecker.cpp | 21 +++++---- test/Analysis/properties.m | 46 +++++++++++++++++++ 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 55c7a65e6a..1991d2bad1 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -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); } diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index ea8d19587a..6f92c6ec32 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -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(); -- 2.40.0