summaryrefslogtreecommitdiffstats
path: root/test/Analysis/retain-release-path-notes.m
blob: 2ade99d883a2c390f006fa8a20a5affc226a8ab0 (plain)
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
// 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: cat %t | %diff_plist %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