Skip to content

Commit

Permalink
[analyzer] RetainCountChecker: CF properties are always manually reta…
Browse files Browse the repository at this point in the history
…in-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
  • Loading branch information
jrose-apple committed Mar 7, 2015
1 parent 322df65 commit 4803293
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 8 deletions.
21 changes: 13 additions & 8 deletions lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand All @@ -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);
}
Expand Down
46 changes: 46 additions & 0 deletions test/Analysis/properties.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -352,6 +356,7 @@ @interface RetainCountTesting
@property (strong) id ownedProp;
@property (unsafe_unretained) id unownedProp;
@property (nonatomic, strong) id manualProp;
@property CFTypeRef cfProp;
@end

@implementation RetainCountTesting {
Expand Down Expand Up @@ -382,6 +387,13 @@ - (void)testOverreleaseIvarOnly {
[_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];
Expand All @@ -396,6 +408,15 @@ - (void)testOverreleaseIvarOnlyUse {
[_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];
Expand Down Expand Up @@ -465,6 +486,21 @@ - (void)testPropertyAccessThenReleaseManual2 {
[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];
Expand Down Expand Up @@ -498,6 +534,11 @@ - (void)testAssignIvarOnly:(id)newValue {
[_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
Expand All @@ -513,6 +554,11 @@ - (void)testAssignIvarOnlyOkay:(id)newValue {
[_ivarOnly release]; // no-warning
}

- (void)testAssignCFOkay:(CFTypeRef)newValue {
_cfProp = CFRetain(newValue);
CFRelease(_cfProp); // no-warning
}

// rdar://problem/19862648
- (void)establishIvarIsNilDuringLoops {
extern id getRandomObject();
Expand Down

0 comments on commit 4803293

Please sign in to comment.