1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
|
// 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 6 {{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
int seed();
@interface LeakReassignmentTests : MyObj
@end
@implementation LeakReassignmentTests
+(void)testLeakAliasSimple {
id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
// expected-note@215 {{Value assigned to 'self'}}
// expected-note@216 {{Returning pointer (loaded from 'self')}}
// expected-note@-3 {{Returning from 'initY'}}
// expected-note@-4 {{'Original' initialized here}}
id New = Original; // expected-note {{'New' initialized to the value of 'Original'}}
Original = [[MyObj alloc] initZ];
(void)New;
[Original release]; // expected-warning {{Potential leak of an object stored into 'New'}}
// expected-note@-1 {{Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1}}
}
+(void)testLeakAliasChain {
id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
// expected-note@215 {{Value assigned to 'self'}}
// expected-note@216 {{Returning pointer (loaded from 'self')}}
// expected-note@-3 {{Returning from 'initY'}}
// expected-note@-4 {{'Original' initialized here}}
id Intermediate = Original; // expected-note {{'Intermediate' initialized to the value of 'Original'}}
id New = Intermediate; // expected-note {{'New' initialized to the value of 'Intermediate'}}
Original = [[MyObj alloc] initZ];
(void)New;
[Original release]; // expected-warning {{Potential leak of an object stored into 'New'}}
// expected-note@-1 {{Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1}}
}
+(void)log:(id)Obj with:(int)Num {
Num *= 42;
if (Obj )
Num /= 2;
}
+(int)calculate {
int x = 10;
int y = 25;
x += y * x + seed();
return y - x * y;
}
+(void)testLeakAliasDeathInExpr {
id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
// expected-note@215 {{Value assigned to 'self'}}
// expected-note@216 {{Returning pointer (loaded from 'self')}}
// expected-note@-3 {{Returning from 'initY'}}
// expected-note@-4 {{'Original' initialized here}}
id New = 0;
New = Original; // expected-note {{The value of 'Original' is assigned to 'New'}}
Original = [[MyObj alloc] initZ];
[self log:New with:[self calculate]];
[Original release]; // expected-warning {{Potential leak of an object stored into 'New'}}
// expected-note@-1 {{Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1}}
}
+(void)testLeakReassign {
id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
// expected-note@-1 {{Returning from 'initY'}}
// TODO: move warning here
Original = [[MyObj alloc] initZ];
[Original release]; // expected-warning {{Potential leak of an object stored into 'Original'}}
// expected-note@-1 {{Object leaked: object allocated and stored into 'Original' is not referenced later in this execution path and has a retain count of +1}}
}
+(void)testLeakReassign:(int)cond {
id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
// expected-note@-1 {{Returning from 'initY'}}
if (cond) // expected-note {{Assuming 'cond' is not equal to 0}}
// expected-note@-1 {{Taking true branch}}
// TODO: move warning here
Original = [[MyObj alloc] initZ];
[Original release]; // expected-warning {{Potential leak of an object stored into 'Original'}}
// expected-note@-1 {{Object leaked: object allocated and stored into 'Original' is not referenced later in this execution path and has a retain count of +1}}
}
@end
|