| // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -analyzer-output=text -verify %s |
| // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -analyzer-output=plist-multi-file %s -o %t |
| // RUN: %normalize_plist <%t | diff -ub %S/Inputs/expected-plists/retain-release-path-notes.m.plist - |
| |
| /*** |
| This file is for testing the path-sensitive notes for retain/release errors. |
| Its goal is to have simple branch coverage of any path-based diagnostics, |
| not to actually check all possible retain/release errors. |
| |
| This file includes notes that only appear in a ref-counted analysis. |
| GC-specific notes should go in retain-release-path-notes-gc.m. |
| ***/ |
| |
| @interface NSObject |
| + (id)alloc; |
| - (id)init; |
| - (void)dealloc; |
| |
| - (Class)class; |
| |
| - (id)retain; |
| - (void)release; |
| - (void)autorelease; |
| @end |
| |
| @interface Foo : NSObject |
| - (id)methodWithValue; |
| @property(retain) id propertyValue; |
| |
| - (id)objectAtIndexedSubscript:(unsigned)index; |
| - (id)objectForKeyedSubscript:(id)key; |
| @end |
| |
| typedef struct CFType *CFTypeRef; |
| CFTypeRef CFRetain(CFTypeRef); |
| void CFRelease(CFTypeRef); |
| CFTypeRef CFAutorelease(CFTypeRef __attribute__((cf_consumed))); |
| |
| id NSMakeCollectable(CFTypeRef); |
| CFTypeRef CFMakeCollectable(CFTypeRef); |
| |
| CFTypeRef CFCreateSomething(); |
| CFTypeRef CFGetSomething(); |
| |
| |
| void creationViaAlloc () { |
| id leaked = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +1 retain count}} |
| return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} |
| } |
| |
| void creationViaCFCreate () { |
| CFTypeRef leaked = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type 'CFTypeRef' with a +1 retain count}} |
| return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} |
| } |
| |
| void acquisitionViaMethod (Foo *foo) { |
| id leaked = [foo methodWithValue]; // expected-note{{Method returns an Objective-C object with a +0 retain count}} |
| [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}} |
| [leaked retain]; // expected-note{{Reference count incremented. The object now has a +2 retain count}} |
| [leaked release]; // expected-note{{Reference count decremented. The object now has a +1 retain count}} |
| return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} |
| } |
| |
| void acquisitionViaProperty (Foo *foo) { |
| id leaked = foo.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}} |
| [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}} |
| return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} |
| } |
| |
| void acquisitionViaCFFunction () { |
| CFTypeRef leaked = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type 'CFTypeRef' with a +0 retain count}} |
| CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +1 retain count}} |
| return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} |
| } |
| |
| void explicitDealloc () { |
| id object = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +1 retain count}} |
| [object dealloc]; // expected-note{{Object released by directly sending the '-dealloc' message}} |
| [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}} |
| } |
| |
| void implicitDealloc () { |
| id object = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +1 retain count}} |
| [object release]; // expected-note{{Object released}} |
| [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}} |
| } |
| |
| void overAutorelease () { |
| id object = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +1 retain count}} |
| [object autorelease]; // expected-note{{Object autoreleased}} |
| [object autorelease]; // expected-note{{Object autoreleased}} |
| return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased 2 times but the object has a +1 retain count}} |
| } |
| |
| void autoreleaseUnowned (Foo *foo) { |
| id object = foo.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}} |
| [object autorelease]; // expected-note{{Object autoreleased}} |
| return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased but has a +0 retain count}} |
| } |
| |
| void makeCollectableIgnored() { |
| CFTypeRef leaked = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type 'CFTypeRef' with a +1 retain count}} |
| CFMakeCollectable(leaked); |
| NSMakeCollectable(leaked); |
| return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} |
| } |
| |
| CFTypeRef CFCopyRuleViolation () { |
| CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type 'CFTypeRef' with a +0 retain count}} |
| return object; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} |
| } |
| |
| CFTypeRef CFGetRuleViolation () { |
| CFTypeRef object = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type 'CFTypeRef' with a +1 retain count}} |
| return object; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'object' is returned from a function whose name ('CFGetRuleViolation') does not contain 'Copy' or 'Create'. This violates the naming convention rules given in the Memory Management Guide for Core Foundation}} |
| } |
| |
| @implementation Foo (FundamentalMemoryManagementRules) |
| - (id)copyViolation { |
| id result = self.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}} |
| return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} |
| } |
| |
| - (id)copyViolationIndexedSubscript { |
| id result = self[0]; // expected-note{{Subscript returns an Objective-C object with a +0 retain count}} |
| return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} |
| } |
| |
| - (id)copyViolationKeyedSubscript { |
| id result = self[self]; // expected-note{{Subscript returns an Objective-C object with a +0 retain count}} |
| return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} |
| } |
| |
| - (id)getViolation { |
| id result = [[Foo alloc] init]; // expected-note{{Method returns an instance of Foo with a +1 retain count}} |
| return result; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'result' is returned from a method whose name ('getViolation') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa}} |
| } |
| |
| - (id)copyAutorelease { |
| id result = [[Foo alloc] init]; // expected-note{{Method returns an instance of Foo with a +1 retain count}} |
| [result autorelease]; // expected-note{{Object autoreleased}} |
| return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} |
| } |
| @end |
| |
| |
| typedef unsigned long NSUInteger; |
| |
| @interface NSValue : NSObject |
| @end |
| |
| @interface NSNumber : NSValue |
| + (NSNumber *)numberWithInt:(int)i; |
| @end |
| |
| @interface NSString : NSObject |
| + (NSString *)stringWithUTF8String:(const char *)str; |
| @end |
| |
| @interface NSArray : NSObject |
| + (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count; |
| @end |
| |
| @interface NSDictionary : NSObject |
| + (id)dictionaryWithObjects:(const id [])objects forKeys:(const id /* <NSCopying> */ [])keys count:(NSUInteger)count; |
| @end |
| |
| |
| void testNumericLiteral() { |
| id result = @1; // expected-note{{NSNumber literal is an object with a +0 retain count}} |
| [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} |
| } |
| |
| void testBoxedInt(int x) { |
| id result = @(x); // expected-note{{NSNumber boxed expression produces an object with a +0 retain count}} |
| [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} |
| } |
| |
| void testBoxedString(const char *str) { |
| id result = @(str); // expected-note{{NSString boxed expression produces an object with a +0 retain count}} |
| [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} |
| } |
| |
| void testArray(id obj) { |
| id result = @[obj]; // expected-note{{NSArray literal is an object with a +0 retain count}} |
| [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} |
| } |
| |
| void testDictionary(id key, id value) { |
| id result = @{key: value}; // expected-note{{NSDictionary literal is an object with a +0 retain count}} |
| [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} |
| } |
| |
| // Test that we step into the init method when the allocated object is leaked due to early escape within init. |
| |
| static int Cond; |
| @interface MyObj : NSObject |
| -(id)initX; |
| -(id)initY; |
| -(id)initZ; |
| +(void)test; |
| @end |
| |
| @implementation MyObj |
| |
| -(id)initX { |
| if (Cond) // expected-note {{Assuming 'Cond' is not equal to 0}} |
| // expected-note@-1{{Taking true branch}} |
| return 0; |
| self = [super init]; |
| return self; |
| } |
| |
| -(id)initY { |
| self = [super init]; //expected-note {{Method returns an instance of MyObj with a +1 retain count}} |
| return self; |
| } |
| |
| -(id)initZ { |
| self = [super init]; |
| return self; |
| } |
| |
| +(void)test { |
| // initX is inlined since we explicitly mark it as interesting |
| id x = [[MyObj alloc] initX]; // expected-warning {{Potential leak of an object}} |
| // expected-note@-1 {{Method returns an instance of MyObj with a +1 retain count}} |
| // expected-note@-2 {{Calling 'initX'}} |
| // expected-note@-3 {{Returning from 'initX'}} |
| // expected-note@-4 {{Object leaked: allocated object of type 'MyObj *' is not referenced later in this execution path and has a retain count of +1}} |
| // initI is inlined because the allocation happens within initY |
| id y = [[MyObj alloc] initY]; |
| // expected-note@-1 {{Calling 'initY'}} |
| // expected-note@-2 {{Returning from 'initY'}} |
| |
| // initZ is not inlined |
| id z = [[MyObj alloc] initZ]; // expected-warning {{Potential leak of an object}} |
| // expected-note@-1 {{Object leaked: object allocated and stored into 'y' is not referenced later in this execution path and has a retain count of +1}} |
| |
| [x release]; |
| [z release]; |
| } |
| @end |
| |
| |
| void CFOverAutorelease() { |
| CFTypeRef object = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type 'CFTypeRef' with a +1 retain count}} |
| CFAutorelease(object); // expected-note{{Object autoreleased}} |
| CFAutorelease(object); // expected-note{{Object autoreleased}} |
| return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased 2 times but the object has a +1 retain count}} |
| } |
| |
| void CFAutoreleaseUnowned() { |
| CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type 'CFTypeRef' with a +0 retain count}} |
| CFAutorelease(object); // expected-note{{Object autoreleased}} |
| return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased but has a +0 retain count}} |
| } |
| |
| void CFAutoreleaseUnownedMixed() { |
| CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type 'CFTypeRef' with a +0 retain count}} |
| CFAutorelease(object); // expected-note{{Object autoreleased}} |
| [(id)object autorelease]; // expected-note{{Object autoreleased}} |
| return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased 2 times but the object has a +0 retain count}} |
| } |
| |
| @interface PropertiesAndIvars : NSObject |
| @property (strong) id ownedProp; |
| @property (unsafe_unretained) id unownedProp; |
| @property (nonatomic, strong) id manualProp; |
| @end |
| |
| @interface NSObject (PropertiesAndIvarsHelper) |
| - (void)myMethod; |
| @end |
| |
| @implementation PropertiesAndIvars { |
| id _ivarOnly; |
| } |
| |
| - (id)manualProp { |
| return _manualProp; |
| } |
| |
| - (void)testOverreleaseUnownedIvar { |
| [_unownedProp retain]; // FIXME-note {{Object loaded from instance variable}} |
| // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}} |
| [_unownedProp release]; // FIXME-note {{Reference count decremented}} |
| [_unownedProp release]; // FIXME-note {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} |
| // FIXME-warning@-1 {{not owned at this point by the caller}} |
| } |
| |
| - (void)testOverreleaseOwnedIvarUse { |
| [_ownedProp retain]; // FIXME-note {{Object loaded from instance variable}} |
| // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}} |
| [_ownedProp release]; // FIXME-note {{Reference count decremented}} |
| [_ownedProp release]; // FIXME-note {{Strong instance variable relinquished. Object released}} |
| [_ownedProp myMethod]; // FIXME-note {{Reference-counted object is used after it is released}} |
| // FIXME-warning@-1 {{used after it is released}} |
| } |
| |
| - (void)testOverreleaseIvarOnlyUse { |
| [_ivarOnly retain]; // FIXME-note {{Object loaded from instance variable}} |
| // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}} |
| [_ivarOnly release]; // FIXME-note {{Reference count decremented}} |
| [_ivarOnly release]; // FIXME-note {{Strong instance variable relinquished. Object released}} |
| [_ivarOnly myMethod]; // FIXME-note {{Reference-counted object is used after it is released}} |
| // FIXME-warning@-1 {{used after it is released}} |
| } |
| |
| - (void)testOverreleaseOwnedIvarAutorelease { |
| [_ownedProp retain]; // FIXME-note {{Object loaded from instance variable}} |
| // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}} |
| [_ownedProp release]; // FIXME-note {{Reference count decremented}} |
| [_ownedProp autorelease]; // FIXME-note {{Object autoreleased}} |
| [_ownedProp autorelease]; // FIXME-note {{Object autoreleased}} |
| // FIXME-note@+1 {{Object was autoreleased 2 times but the object has a +0 retain count}} |
| } // FIXME-warning{{Object autoreleased too many times}} |
| |
| - (void)testOverreleaseIvarOnlyAutorelease { |
| [_ivarOnly retain]; // FIXME-note {{Object loaded from instance variable}} |
| // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}} |
| [_ivarOnly release]; // FIXME-note {{Reference count decremented}} |
| [_ivarOnly autorelease]; // FIXME-note {{Object autoreleased}} |
| [_ivarOnly autorelease]; // FIXME-note {{Object autoreleased}} |
| // FIXME-note@+1 {{Object was autoreleased 2 times but the object has a +0 retain count}} |
| } // FIXME-warning{{Object autoreleased too many times}} |
| |
| @end |
| |
| |
| |