1 | // RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -analyzer-checker=core,osx.cocoa.NilArg,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -verify -Wno-objc-root-class %s |
2 | // RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -analyzer-checker=core,osx.cocoa.NilArg,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -analyzer-config mode=shallow -verify -Wno-objc-root-class %s |
3 | // RUN: %clang_analyze_cc1 -DTEST_64 -triple x86_64-apple-darwin10 -analyzer-checker=core,osx.cocoa.NilArg,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -verify -Wno-objc-root-class %s |
4 | // RUN: %clang_analyze_cc1 -DOSATOMIC_USE_INLINED -triple i386-apple-darwin10 -analyzer-checker=core,osx.cocoa.NilArg,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -verify -Wno-objc-root-class %s |
5 | |
6 | //===----------------------------------------------------------------------===// |
7 | // The following code is reduced using delta-debugging from |
8 | // Foundation.h (Mac OS X). |
9 | // |
10 | // It includes the basic definitions for the test cases below. |
11 | // Not directly including Foundation.h directly makes this test case |
12 | // both svelte and portable to non-Mac platforms. |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #ifdef TEST_64 |
16 | typedef long long int64_t; |
17 | _Bool OSAtomicCompareAndSwap64Barrier( int64_t __oldValue, int64_t __newValue, volatile int64_t *__theValue ); |
18 | #define COMPARE_SWAP_BARRIER OSAtomicCompareAndSwap64Barrier |
19 | typedef int64_t intptr_t; |
20 | #else |
21 | typedef int int32_t; |
22 | _Bool OSAtomicCompareAndSwap32Barrier( int32_t __oldValue, int32_t __newValue, volatile int32_t *__theValue ); |
23 | #define COMPARE_SWAP_BARRIER OSAtomicCompareAndSwap32Barrier |
24 | typedef int32_t intptr_t; |
25 | #endif |
26 | |
27 | typedef const void * CFTypeRef; |
28 | typedef const struct __CFString * CFStringRef; |
29 | typedef const struct __CFAllocator * CFAllocatorRef; |
30 | extern const CFAllocatorRef kCFAllocatorDefault; |
31 | extern CFTypeRef CFRetain(CFTypeRef cf); |
32 | void CFRelease(CFTypeRef cf); |
33 | typedef const struct __CFDictionary * CFDictionaryRef; |
34 | const void *CFDictionaryGetValue(CFDictionaryRef theDict, const void *key); |
35 | extern CFStringRef CFStringCreateWithFormat(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, ...); |
36 | typedef signed char BOOL; |
37 | typedef int NSInteger; |
38 | typedef unsigned int NSUInteger; |
39 | @class NSString, Protocol; |
40 | extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2))); |
41 | typedef NSInteger NSComparisonResult; |
42 | typedef struct _NSZone NSZone; |
43 | @class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; |
44 | @protocol NSObject |
45 | - (BOOL)isEqual:(id)object; |
46 | - (oneway void)release; |
47 | - (id)retain; |
48 | - (id)autorelease; |
49 | @end |
50 | @protocol NSCopying |
51 | - (id)copyWithZone:(NSZone *)zone; |
52 | @end |
53 | @protocol NSMutableCopying |
54 | - (id)mutableCopyWithZone:(NSZone *)zone; |
55 | @end |
56 | @protocol NSCoding |
57 | - (void)encodeWithCoder:(NSCoder *)aCoder; |
58 | @end |
59 | @interface NSObject <NSObject> {} |
60 | - (id)init; |
61 | + (id)alloc; |
62 | @end |
63 | extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); |
64 | typedef struct {} NSFastEnumerationState; |
65 | @protocol NSFastEnumeration |
66 | - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len; |
67 | @end |
68 | @class NSString; |
69 | typedef struct _NSRange {} NSRange; |
70 | @interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration> |
71 | - (NSUInteger)count; |
72 | @end |
73 | @interface NSMutableArray : NSArray |
74 | - (void)addObject:(id)anObject; |
75 | - (id)initWithCapacity:(NSUInteger)numItems; |
76 | @end |
77 | typedef unsigned short unichar; |
78 | @class NSData, NSArray, NSDictionary, NSCharacterSet, NSData, NSURL, NSError, NSLocale; |
79 | typedef NSUInteger NSStringCompareOptions; |
80 | @interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding> - (NSUInteger)length; |
81 | - (NSComparisonResult)compare:(NSString *)string; |
82 | - (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask; |
83 | - (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)compareRange; |
84 | - (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)compareRange locale:(id)locale; |
85 | - (NSComparisonResult)caseInsensitiveCompare:(NSString *)string; |
86 | - (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator; |
87 | + (id)stringWithFormat:(NSString *)format, ... __attribute__((format(__NSString__, 1, 2))); |
88 | @end |
89 | @interface NSSimpleCString : NSString {} @end |
90 | @interface NSConstantString : NSSimpleCString @end |
91 | extern void *_NSConstantStringClassReference; |
92 | |
93 | //===----------------------------------------------------------------------===// |
94 | // Test cases. |
95 | //===----------------------------------------------------------------------===// |
96 | |
97 | NSComparisonResult f1(NSString* s) { |
98 | NSString *aString = 0; |
99 | return [s compare:aString]; // expected-warning {{Argument to 'NSString' method 'compare:' cannot be nil}} |
100 | } |
101 | |
102 | NSComparisonResult f2(NSString* s) { |
103 | NSString *aString = 0; |
104 | return [s caseInsensitiveCompare:aString]; // expected-warning {{Argument to 'NSString' method 'caseInsensitiveCompare:' cannot be nil}} |
105 | } |
106 | |
107 | NSComparisonResult f3(NSString* s, NSStringCompareOptions op) { |
108 | NSString *aString = 0; |
109 | return [s compare:aString options:op]; // expected-warning {{Argument to 'NSString' method 'compare:options:' cannot be nil}} |
110 | } |
111 | |
112 | NSComparisonResult f4(NSString* s, NSStringCompareOptions op, NSRange R) { |
113 | NSString *aString = 0; |
114 | return [s compare:aString options:op range:R]; // expected-warning {{Argument to 'NSString' method 'compare:options:range:' cannot be nil}} |
115 | } |
116 | |
117 | NSComparisonResult f5(NSString* s, NSStringCompareOptions op, NSRange R) { |
118 | NSString *aString = 0; |
119 | return [s compare:aString options:op range:R locale:0]; // expected-warning {{Argument to 'NSString' method 'compare:options:range:locale:' cannot be nil}} |
120 | } |
121 | |
122 | NSArray *f6(NSString* s) { |
123 | return [s componentsSeparatedByCharactersInSet:0]; // expected-warning {{Argument to 'NSString' method 'componentsSeparatedByCharactersInSet:' cannot be nil}} |
124 | } |
125 | |
126 | NSString* f7(NSString* s1, NSString* s2, NSString* s3) { |
127 | |
128 | NSString* s4 = (NSString*) |
129 | CFStringCreateWithFormat(kCFAllocatorDefault, 0, // expected-warning{{leak}} |
130 | (CFStringRef) __builtin___CFStringMakeConstantString("%@ %@ (%@)"), |
131 | s1, s2, s3); |
132 | |
133 | CFRetain(s4); |
134 | return s4; |
135 | } |
136 | |
137 | NSMutableArray* f8() { |
138 | |
139 | NSString* s = [[NSString alloc] init]; |
140 | NSMutableArray* a = [[NSMutableArray alloc] initWithCapacity:2]; |
141 | [a addObject:s]; |
142 | [s release]; // no-warning |
143 | return a; |
144 | } |
145 | |
146 | void f9() { |
147 | |
148 | NSString* s = [[NSString alloc] init]; |
149 | NSString* q = s; |
150 | [s release]; |
151 | [q release]; // expected-warning {{used after it is released}} |
152 | } |
153 | |
154 | NSString* f10() { |
155 | static NSString* s = 0; |
156 | if (!s) s = [[NSString alloc] init]; |
157 | return s; // no-warning |
158 | } |
159 | |
160 | // Test case for regression reported in <rdar://problem/6452745>. |
161 | // Essentially 's' should not be considered allocated on the false branch. |
162 | // This exercises the 'EvalAssume' logic in GRTransferFuncs (CFRefCount.cpp). |
163 | NSString* f11(CFDictionaryRef dict, const char* key) { |
164 | NSString* s = (NSString*) CFDictionaryGetValue(dict, key); |
165 | [s retain]; |
166 | if (s) { |
167 | [s release]; |
168 | } |
169 | return 0; |
170 | } |
171 | |
172 | // Test case for passing a tracked object by-reference to a function we |
173 | // don't understand. |
174 | void unknown_function_f12(NSString** s); |
175 | void f12() { |
176 | NSString *string = [[NSString alloc] init]; |
177 | unknown_function_f12(&string); // no-warning |
178 | } |
179 | |
180 | // Test double release of CFString (PR 4014). |
181 | void f13(void) { |
182 | CFStringRef ref = CFStringCreateWithFormat(kCFAllocatorDefault, ((void*)0), ((CFStringRef) __builtin___CFStringMakeConstantString ("" "%d" "")), 100); |
183 | CFRelease(ref); |
184 | CFRelease(ref); // expected-warning{{Reference-counted object is used after it is released}} |
185 | } |
186 | |
187 | @interface MyString : NSString |
188 | @end |
189 | |
190 | void f14(MyString *s) { |
191 | [s compare:0]; // expected-warning {{Argument to 'MyString' method 'compare:' cannot be nil}} |
192 | } |
193 | |
194 | // Test regular use of -autorelease |
195 | @interface TestAutorelease |
196 | -(NSString*) getString; |
197 | @end |
198 | @implementation TestAutorelease |
199 | -(NSString*) getString { |
200 | NSString *str = [[NSString alloc] init]; |
201 | return [str autorelease]; // no-warning |
202 | } |
203 | - (void)m1 |
204 | { |
205 | NSString *s = [[NSString alloc] init]; // expected-warning{{leak}} |
206 | [s retain]; |
207 | [s autorelease]; |
208 | } |
209 | - (void)m2 |
210 | { |
211 | NSString *s = [[[NSString alloc] init] autorelease]; // expected-warning{{leak}} |
212 | [s retain]; |
213 | } |
214 | - (void)m3 |
215 | { |
216 | NSString *s = [[[NSString alloc] init] autorelease]; |
217 | [s retain]; |
218 | [s autorelease]; |
219 | } |
220 | - (void)m4 |
221 | { |
222 | NSString *s = [[NSString alloc] init]; // expected-warning{{leak}} |
223 | [s retain]; |
224 | } |
225 | - (void)m5 |
226 | { |
227 | NSString *s = [[NSString alloc] init]; |
228 | [s autorelease]; |
229 | } |
230 | @end |
231 | |
232 | @interface C1 : NSObject {} |
233 | - (NSString*) getShared; |
234 | + (C1*) sharedInstance; |
235 | @end |
236 | @implementation C1 : NSObject {} |
237 | - (NSString*) getShared { |
238 | static NSString* s = 0; |
239 | if (!s) s = [[NSString alloc] init]; |
240 | return s; // no-warning |
241 | } |
242 | + (C1 *)sharedInstance { |
243 | static C1 *sharedInstance = 0; |
244 | if (!sharedInstance) { |
245 | sharedInstance = [[C1 alloc] init]; |
246 | } |
247 | return sharedInstance; // no-warning |
248 | } |
249 | @end |
250 | |
251 | @interface SharedClass : NSObject |
252 | + (id)sharedInstance; |
253 | - (id)notShared; |
254 | @end |
255 | |
256 | @implementation SharedClass |
257 | |
258 | - (id)_init { |
259 | if ((self = [super init])) { |
260 | NSLog(@"Bar"); |
261 | } |
262 | return self; |
263 | } |
264 | |
265 | - (id)notShared { |
266 | return [[SharedClass alloc] _init]; // expected-warning{{leak}} |
267 | } |
268 | |
269 | + (id)sharedInstance { |
270 | static SharedClass *_sharedInstance = 0; |
271 | if (!_sharedInstance) { |
272 | _sharedInstance = [[SharedClass alloc] _init]; |
273 | } |
274 | return _sharedInstance; // no-warning |
275 | } |
276 | @end |
277 | |
278 | id testSharedClassFromFunction() { |
279 | return [[SharedClass alloc] _init]; // no-warning |
280 | } |
281 | |
282 | #if !(defined(OSATOMIC_USE_INLINED) && OSATOMIC_USE_INLINED) |
283 | // Test OSCompareAndSwap |
284 | _Bool OSAtomicCompareAndSwapPtr( void *__oldValue, void *__newValue, void * volatile *__theValue ); |
285 | extern BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation); |
286 | #else |
287 | // Test that the body farm models are still used even when a body is available. |
288 | _Bool opaque_OSAtomicCompareAndSwapPtr( void *__oldValue, void *__newValue, void * volatile *__theValue ); |
289 | _Bool OSAtomicCompareAndSwapPtr( void *__oldValue, void *__newValue, void * volatile *__theValue ) { |
290 | return opaque_OSAtomicCompareAndSwapPtr(__oldValue, __newValue, __theValue); |
291 | } |
292 | // Test that the analyzer doesn't crash when the farm model is used. |
293 | // The analyzer ignores the autosynthesized code. |
294 | _Bool OSAtomicCompareAndSwapEmptyFunction( void *__oldValue, void *__newValue, void * volatile *__theValue ) { |
295 | return 0; |
296 | } |
297 | extern BOOL opaque_objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation); |
298 | extern BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation) { |
299 | return opaque_objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); |
300 | } |
301 | #endif |
302 | |
303 | void testOSCompareAndSwap() { |
304 | NSString *old = 0; |
305 | NSString *s = [[NSString alloc] init]; // no-warning |
306 | if (!OSAtomicCompareAndSwapPtr(0, s, (void**) &old)) |
307 | [s release]; |
308 | else |
309 | [old release]; |
310 | } |
311 | |
312 | void testOSCompareAndSwapXXBarrier_local() { |
313 | NSString *old = 0; |
314 | NSString *s = [[NSString alloc] init]; // no-warning |
315 | if (!COMPARE_SWAP_BARRIER((intptr_t) 0, (intptr_t) s, (intptr_t*) &old)) |
316 | [s release]; |
317 | else |
318 | [old release]; |
319 | } |
320 | |
321 | void testOSCompareAndSwapXXBarrier_local_no_direct_release() { |
322 | NSString *old = 0; |
323 | NSString *s = [[NSString alloc] init]; // no-warning |
324 | if (!COMPARE_SWAP_BARRIER((intptr_t) 0, (intptr_t) s, (intptr_t*) &old)) |
325 | return; |
326 | else |
327 | [old release]; |
328 | } |
329 | |
330 | int testOSCompareAndSwapXXBarrier_id(Class myclass, id xclass) { |
331 | if (COMPARE_SWAP_BARRIER(0, (intptr_t) myclass, (intptr_t*) &xclass)) |
332 | return 1; |
333 | return 0; |
334 | } |
335 | |
336 | void test_objc_atomicCompareAndSwap_local() { |
337 | NSString *old = 0; |
338 | NSString *s = [[NSString alloc] init]; // no-warning |
339 | if (!objc_atomicCompareAndSwapPtr(0, s, &old)) |
340 | [s release]; |
341 | else |
342 | [old release]; |
343 | } |
344 | |
345 | void test_objc_atomicCompareAndSwap_local_no_direct_release() { |
346 | NSString *old = 0; |
347 | NSString *s = [[NSString alloc] init]; // no-warning |
348 | if (!objc_atomicCompareAndSwapPtr(0, s, &old)) |
349 | return; |
350 | else |
351 | [old release]; |
352 | } |
353 | |
354 | void test_objc_atomicCompareAndSwap_parameter(NSString **old) { |
355 | NSString *s = [[NSString alloc] init]; // no-warning |
356 | if (!objc_atomicCompareAndSwapPtr(0, s, old)) |
357 | [s release]; |
358 | else |
359 | [*old release]; |
360 | } |
361 | |
362 | void test_objc_atomicCompareAndSwap_parameter_no_direct_release(NSString **old) { |
363 | NSString *s = [[NSString alloc] init]; // expected-warning{{leak}} |
364 | if (!objc_atomicCompareAndSwapPtr(0, s, old)) |
365 | return; |
366 | else |
367 | [*old release]; |
368 | } |
369 | |
370 | |
371 | // Test stringWithFormat (<rdar://problem/6815234>) |
372 | void test_stringWithFormat() { |
373 | NSString *string = [[NSString stringWithFormat:@"%ld", (long) 100] retain]; |
374 | [string release]; |
375 | [string release]; // expected-warning{{Incorrect decrement of the reference count}} |
376 | } |
377 | |
378 | // Test isTrackedObjectType(). |
379 | typedef NSString* WonkyTypedef; |
380 | @interface TestIsTracked |
381 | + (WonkyTypedef)newString; |
382 | @end |
383 | |
384 | void test_isTrackedObjectType(void) { |
385 | NSString *str = [TestIsTracked newString]; // expected-warning{{Potential leak}} |
386 | } |
387 | |
388 | // Test isTrackedCFObjectType(). |
389 | @interface TestIsCFTracked |
390 | + (CFStringRef) badNewCFString; |
391 | + (CFStringRef) newCFString; |
392 | @end |
393 | |
394 | @implementation TestIsCFTracked |
395 | + (CFStringRef) newCFString { |
396 | return CFStringCreateWithFormat(kCFAllocatorDefault, ((void*)0), ((CFStringRef) __builtin___CFStringMakeConstantString ("" "%d" "")), 100); // no-warning |
397 | } |
398 | + (CFStringRef) badNewCFString { |
399 | return CFStringCreateWithFormat(kCFAllocatorDefault, ((void*)0), ((CFStringRef) __builtin___CFStringMakeConstantString ("" "%d" "")), 100); // expected-warning{{leak}} |
400 | } |
401 | |
402 | // Test @synchronized |
403 | void test_synchronized(id x) { |
404 | @synchronized(x) { |
405 | NSString *string = [[NSString stringWithFormat:@"%ld", (long) 100] retain]; // expected-warning {{leak}} |
406 | } |
407 | } |
408 | @end |
409 | |
410 | void testOSCompareAndSwapXXBarrier_parameter(NSString **old) { |
411 | NSString *s = [[NSString alloc] init]; // no-warning |
412 | if (!COMPARE_SWAP_BARRIER((intptr_t) 0, (intptr_t) s, (intptr_t*) old)) |
413 | [s release]; |
414 | else |
415 | [*old release]; |
416 | } |
417 | |
418 | void testOSCompareAndSwapXXBarrier_parameter_no_direct_release(NSString **old) { |
419 | NSString *s = [[NSString alloc] init]; // no-warning |
420 | if (!COMPARE_SWAP_BARRIER((intptr_t) 0, (intptr_t) s, (intptr_t*) old)) |
421 | [s release]; |
422 | else |
423 | return; |
424 | } |
425 | |
426 | @interface AlwaysInlineBodyFarmBodies : NSObject { |
427 | NSString *_value; |
428 | } |
429 | - (NSString *)_value; |
430 | - (void)callValue; |
431 | @end |
432 | |
433 | @implementation AlwaysInlineBodyFarmBodies |
434 | |
435 | - (NSString *)_value { |
436 | if (!_value) { |
437 | NSString *s = [[NSString alloc] init]; |
438 | if (!OSAtomicCompareAndSwapPtr(0, s, (void**)&_value)) { |
439 | [s release]; |
440 | } |
441 | } |
442 | return _value; |
443 | } |
444 | |
445 | - (void)callValue { |
446 | [self _value]; |
447 | } |
448 | @end |
449 | |