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: cat %t | %diff_plist %S/Inputs/expected-plists/retain-release-path-notes.m.plist - |
4 | |
5 | /*** |
6 | This file is for testing the path-sensitive notes for retain/release errors. |
7 | Its goal is to have simple branch coverage of any path-based diagnostics, |
8 | not to actually check all possible retain/release errors. |
9 | |
10 | This file includes notes that only appear in a ref-counted analysis. |
11 | GC-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 | |
34 | typedef struct CFType *CFTypeRef; |
35 | CFTypeRef CFRetain(CFTypeRef); |
36 | void CFRelease(CFTypeRef); |
37 | CFTypeRef CFAutorelease(CFTypeRef __attribute__((cf_consumed))); |
38 | |
39 | id NSMakeCollectable(CFTypeRef); |
40 | CFTypeRef CFMakeCollectable(CFTypeRef); |
41 | |
42 | CFTypeRef CFCreateSomething(); |
43 | CFTypeRef CFGetSomething(); |
44 | |
45 | |
46 | void 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 | |
51 | void 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 | |
56 | void 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 | |
64 | void 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 | |
70 | void 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 | |
76 | void 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 | |
82 | void 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 | |
88 | void 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 | |
95 | void 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 | |
101 | void 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 | |
108 | CFTypeRef 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 | |
113 | CFTypeRef 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 | |
147 | typedef 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 | |
169 | void 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 | |
174 | void 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 | |
179 | void 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 | |
184 | void 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 | |
189 | void 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 | |
196 | static 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 {{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 | |
246 | void 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 | |
253 | void 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 | |
259 | void 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 | |
330 | |
331 | |
332 | |