1// 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
2// 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
3// RUN: %normalize_plist <%t | diff -ub %S/Inputs/expected-plists/retain-release-path-notes.m.plist -
4
5/***
6This file is for testing the path-sensitive notes for retain/release errors.
7Its goal is to have simple branch coverage of any path-based diagnostics,
8not to actually check all possible retain/release errors.
9
10This file includes notes that only appear in a ref-counted analysis.
11GC-specific notes should go in retain-release-path-notes-gc.m.
12***/
13
14@interface NSObject
15+ (id)alloc;
16- (id)init;
17- (void)dealloc;
18
19- (Class)class;
20
21- (id)retain;
22- (void)release;
23- (void)autorelease;
24@end
25
26@interface Foo : NSObject
27- (id)methodWithValue;
28@property(retain) id propertyValue;
29
30- (id)objectAtIndexedSubscript:(unsigned)index;
31- (id)objectForKeyedSubscript:(id)key;
32@end
33
34typedef struct CFType *CFTypeRef;
35CFTypeRef CFRetain(CFTypeRef);
36void CFRelease(CFTypeRef);
37CFTypeRef CFAutorelease(CFTypeRef __attribute__((cf_consumed)));
38
39id NSMakeCollectable(CFTypeRef);
40CFTypeRef CFMakeCollectable(CFTypeRef);
41
42CFTypeRef CFCreateSomething();
43CFTypeRef CFGetSomething();
44
45
46void creationViaAlloc () {
47  id leaked = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +1 retain count}}
48  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}}
49}
50
51void creationViaCFCreate () {
52  CFTypeRef leaked = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type 'CFTypeRef' with a +1 retain count}}
53  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}}
54}
55
56void acquisitionViaMethod (Foo *foo) {
57  id leaked = [foo methodWithValue]; // expected-note{{Method returns an Objective-C object with a +0 retain count}}
58  [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}}
59  [leaked retain]; // expected-note{{Reference count incremented. The object now has a +2 retain count}}
60  [leaked release]; // expected-note{{Reference count decremented. The object now has a +1 retain count}}
61  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}}
62}
63
64void acquisitionViaProperty (Foo *foo) {
65  id leaked = foo.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}}
66  [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}}
67  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}}
68}
69
70void acquisitionViaCFFunction () {
71  CFTypeRef leaked = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type 'CFTypeRef' with a +0 retain count}}
72  CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +1 retain count}}
73  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}}
74}
75
76void explicitDealloc () {
77  id object = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +1 retain count}}
78  [object dealloc]; // expected-note{{Object released by directly sending the '-dealloc' message}}
79  [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}}
80}
81
82void implicitDealloc () {
83  id object = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +1 retain count}}
84  [object release]; // expected-note{{Object released}}
85  [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}}
86}
87
88void overAutorelease () {
89  id object = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +1 retain count}}
90  [object autorelease]; // expected-note{{Object autoreleased}}
91  [object autorelease]; // expected-note{{Object autoreleased}}
92  return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased 2 times but the object has a +1 retain count}}
93}
94
95void autoreleaseUnowned (Foo *foo) {
96  id object = foo.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}}
97  [object autorelease]; // expected-note{{Object autoreleased}}
98  return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased but has a +0 retain count}}
99}
100
101void makeCollectableIgnored() {
102  CFTypeRef leaked = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type 'CFTypeRef' with a +1 retain count}}
103  CFMakeCollectable(leaked);
104  NSMakeCollectable(leaked);
105  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}}
106}
107
108CFTypeRef CFCopyRuleViolation () {
109  CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type 'CFTypeRef' with a +0 retain count}}
110  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}}
111}
112
113CFTypeRef CFGetRuleViolation () {
114  CFTypeRef object = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type 'CFTypeRef' with a +1 retain count}}
115  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}}
116}
117
118@implementation Foo (FundamentalMemoryManagementRules)
119- (id)copyViolation {
120  id result = self.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}}
121  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}}
122}
123
124- (id)copyViolationIndexedSubscript {
125  id result = self[0]; // expected-note{{Subscript returns an Objective-C object with a +0 retain count}}
126  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}}
127}
128
129- (id)copyViolationKeyedSubscript {
130  id result = self[self]; // expected-note{{Subscript returns an Objective-C object with a +0 retain count}}
131  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}}
132}
133
134- (id)getViolation {
135  id result = [[Foo alloc] init]; // expected-note{{Method returns an instance of Foo with a +1 retain count}}
136  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}}
137}
138
139- (id)copyAutorelease {
140  id result = [[Foo alloc] init]; // expected-note{{Method returns an instance of Foo with a +1 retain count}}
141  [result autorelease]; // expected-note{{Object autoreleased}}
142  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}}
143}
144@end
145
146
147typedef unsigned long NSUInteger;
148
149@interface NSValue : NSObject
150@end
151
152@interface NSNumber : NSValue
153+ (NSNumber *)numberWithInt:(int)i;
154@end
155
156@interface NSString : NSObject
157+ (NSString *)stringWithUTF8String:(const char *)str;
158@end
159
160@interface NSArray : NSObject
161+ (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count;
162@end
163
164@interface NSDictionary : NSObject
165+ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id /* <NSCopying> */ [])keys count:(NSUInteger)count;
166@end
167
168
169void testNumericLiteral() {
170  id result = @1; // expected-note{{NSNumber literal is an object with a +0 retain count}}
171  [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}}
172}
173
174void testBoxedInt(int x) {
175  id result = @(x); // expected-note{{NSNumber boxed expression produces an object with a +0 retain count}}
176  [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}}
177}
178
179void testBoxedString(const char *str) {
180  id result = @(str); // expected-note{{NSString boxed expression produces an object with a +0 retain count}}
181  [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}}
182}
183
184void testArray(id obj) {
185  id result = @[obj]; // expected-note{{NSArray literal is an object with a +0 retain count}}
186  [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}}
187}
188
189void testDictionary(id key, id value) {
190  id result = @{key: value}; // expected-note{{NSDictionary literal is an object with a +0 retain count}}
191  [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}}
192}
193
194// Test that we step into the init method when the allocated object is leaked due to early escape within init.
195
196static int Cond;
197@interface MyObj : NSObject
198-(id)initX;
199-(id)initY;
200-(id)initZ;
201+(void)test;
202@end
203
204@implementation MyObj
205
206-(id)initX {
207  if (Cond)  // expected-note {{Assuming 'Cond' is not equal to 0}}
208             // expected-note@-1{{Taking true branch}}
209    return 0;
210  self = [super init];
211  return self;
212}
213
214-(id)initY {
215  self = [super init]; // expected-note 6 {{Method returns an instance of MyObj with a +1 retain count}}
216  return self;
217}
218
219-(id)initZ {
220  self = [super init];
221  return self;
222}
223
224+(void)test {
225  // initX is inlined since we explicitly mark it as interesting
226  id x = [[MyObj alloc] initX]; // expected-warning {{Potential leak of an object}}
227                                // expected-note@-1 {{Method returns an instance of MyObj with a +1 retain count}}
228                                // expected-note@-2 {{Calling 'initX'}}
229                                // expected-note@-3 {{Returning from 'initX'}}
230                                // 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}}
231  // initI is inlined because the allocation happens within initY
232  id y = [[MyObj alloc] initY];
233                                // expected-note@-1 {{Calling 'initY'}}
234                                // expected-note@-2 {{Returning from 'initY'}}
235
236  // initZ is not inlined
237  id z = [[MyObj alloc] initZ]; // expected-warning {{Potential leak of an object}}
238                                // 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}}
239
240  [x release];
241  [z release];
242}
243@end
244
245
246void CFOverAutorelease() {
247  CFTypeRef object = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type 'CFTypeRef' with a +1 retain count}}
248  CFAutorelease(object); // expected-note{{Object autoreleased}}
249  CFAutorelease(object); // expected-note{{Object autoreleased}}
250  return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased 2 times but the object has a +1 retain count}}
251}
252
253void CFAutoreleaseUnowned() {
254  CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type 'CFTypeRef' with a +0 retain count}}
255  CFAutorelease(object); // expected-note{{Object autoreleased}}
256  return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased but has a +0 retain count}}
257}
258
259void CFAutoreleaseUnownedMixed() {
260  CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type 'CFTypeRef' with a +0 retain count}}
261  CFAutorelease(object); // expected-note{{Object autoreleased}}
262  [(id)object autorelease]; // expected-note{{Object autoreleased}}
263  return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased 2 times but the object has a +0 retain count}}
264}
265
266@interface PropertiesAndIvars : NSObject
267@property (strong) id ownedProp;
268@property (unsafe_unretained) id unownedProp;
269@property (nonatomic, strong) id manualProp;
270@end
271
272@interface NSObject (PropertiesAndIvarsHelper)
273- (void)myMethod;
274@end
275
276@implementation PropertiesAndIvars {
277  id _ivarOnly;
278}
279
280- (id)manualProp {
281  return _manualProp;
282}
283
284- (void)testOverreleaseUnownedIvar {
285  [_unownedProp retain]; // FIXME-note {{Object loaded from instance variable}}
286  // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}}
287  [_unownedProp release]; // FIXME-note {{Reference count decremented}}
288  [_unownedProp release]; // FIXME-note {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
289  // FIXME-warning@-1 {{not owned at this point by the caller}}
290}
291
292- (void)testOverreleaseOwnedIvarUse {
293  [_ownedProp retain]; // FIXME-note {{Object loaded from instance variable}}
294  // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}}
295  [_ownedProp release]; // FIXME-note {{Reference count decremented}}
296  [_ownedProp release]; // FIXME-note {{Strong instance variable relinquished. Object released}}
297  [_ownedProp myMethod]; // FIXME-note {{Reference-counted object is used after it is released}}
298  // FIXME-warning@-1 {{used after it is released}}
299}
300
301- (void)testOverreleaseIvarOnlyUse {
302  [_ivarOnly retain]; // FIXME-note {{Object loaded from instance variable}}
303  // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}}
304  [_ivarOnly release]; // FIXME-note {{Reference count decremented}}
305  [_ivarOnly release]; // FIXME-note {{Strong instance variable relinquished. Object released}}
306  [_ivarOnly myMethod]; // FIXME-note {{Reference-counted object is used after it is released}}
307  // FIXME-warning@-1 {{used after it is released}}
308}
309
310- (void)testOverreleaseOwnedIvarAutorelease {
311  [_ownedProp retain]; // FIXME-note {{Object loaded from instance variable}}
312  // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}}
313  [_ownedProp release]; // FIXME-note {{Reference count decremented}}
314  [_ownedProp autorelease]; // FIXME-note {{Object autoreleased}}
315  [_ownedProp autorelease]; // FIXME-note {{Object autoreleased}}
316  // FIXME-note@+1 {{Object was autoreleased 2 times but the object has a +0 retain count}}
317} // FIXME-warning{{Object autoreleased too many times}}
318
319- (void)testOverreleaseIvarOnlyAutorelease {
320  [_ivarOnly retain]; // FIXME-note {{Object loaded from instance variable}}
321  // FIXME-note@-1 {{Reference count incremented. The object now has a +1 retain count}}
322  [_ivarOnly release]; // FIXME-note {{Reference count decremented}}
323  [_ivarOnly autorelease]; // FIXME-note {{Object autoreleased}}
324  [_ivarOnly autorelease]; // FIXME-note {{Object autoreleased}}
325  // FIXME-note@+1 {{Object was autoreleased 2 times but the object has a +0 retain count}}
326} // FIXME-warning{{Object autoreleased too many times}}
327
328@end
329
330int seed();
331
332@interface LeakReassignmentTests : MyObj
333@end
334
335@implementation LeakReassignmentTests
336+(void)testLeakAliasSimple {
337  id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
338                                       // expected-note@215 {{Value assigned to 'self'}}
339                                       // expected-note@216 {{Returning pointer (loaded from 'self')}}
340                                       // expected-note@-3 {{Returning from 'initY'}}
341                                       // expected-note@-4 {{'Original' initialized here}}
342  id New = Original;                   // expected-note {{'New' initialized to the value of 'Original'}}
343  Original = [[MyObj alloc] initZ];
344  (void)New;
345  [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}}
346                      // 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}}
347}
348
349+(void)testLeakAliasChain {
350  id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
351                                       // expected-note@215 {{Value assigned to 'self'}}
352                                       // expected-note@216 {{Returning pointer (loaded from 'self')}}
353                                       // expected-note@-3 {{Returning from 'initY'}}
354                                       // expected-note@-4 {{'Original' initialized here}}
355  id Intermediate = Original;          // expected-note {{'Intermediate' initialized to the value of 'Original'}}
356  id New = Intermediate;               // expected-note {{'New' initialized to the value of 'Intermediate'}}
357  Original = [[MyObj alloc] initZ];
358  (void)New;
359  [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}}
360                      // 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}}
361}
362
363+(void)log:(id)Obj with:(int)Num {
364  Num *= 42;
365  if (Obj )
366    Num /= 2;
367}
368
369+(int)calculate {
370  int x = 10;
371  int y = 25;
372  x += y * x + seed();
373  return y - x * y;
374}
375
376+(void)testLeakAliasDeathInExpr {
377  id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
378                                       // expected-note@215 {{Value assigned to 'self'}}
379                                       // expected-note@216 {{Returning pointer (loaded from 'self')}}
380                                       // expected-note@-3 {{Returning from 'initY'}}
381                                       // expected-note@-4 {{'Original' initialized here}}
382  id New = 0;
383  New = Original; // expected-note {{The value of 'Original' is assigned to 'New'}}
384  Original = [[MyObj alloc] initZ];
385  [self log:New with:[self calculate]];
386  [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}}
387                      // 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}}
388}
389
390+(void)testLeakReassign {
391  id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
392                                       // expected-note@-1 {{Returning from 'initY'}}
393  // TODO: move warning here
394  Original = [[MyObj alloc] initZ];
395  [Original release]; // expected-warning {{Potential leak of an object stored into 'Original'}}
396                      // 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}}
397}
398
399+(void)testLeakReassign:(int)cond {
400  id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}}
401                                       // expected-note@-1 {{Returning from 'initY'}}
402  if (cond)                            // expected-note {{Assuming 'cond' is not equal to 0}}
403                                       // expected-note@-1 {{Taking true branch}}
404    // TODO: move warning here
405    Original = [[MyObj alloc] initZ];
406  [Original release]; // expected-warning {{Potential leak of an object stored into 'Original'}}
407                      // 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}}
408}
409
410@end
411