1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -analyzer-config eagerly-assume=false %s |
2 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc -analyzer-config eagerly-assume=false %s |
3 | |
4 | void clang_analyzer_eval(int); |
5 | |
6 | typedef const void * CFTypeRef; |
7 | extern CFTypeRef CFRetain(CFTypeRef cf); |
8 | void CFRelease(CFTypeRef cf); |
9 | |
10 | typedef signed char BOOL; |
11 | typedef unsigned int NSUInteger; |
12 | typedef struct _NSZone NSZone; |
13 | @class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; |
14 | @protocol NSObject - (BOOL)isEqual:(id)object; @end |
15 | @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end |
16 | @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end |
17 | @protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end |
18 | @interface NSObject <NSObject> {} |
19 | +(id)alloc; |
20 | -(id)init; |
21 | -(id)autorelease; |
22 | -(id)copy; |
23 | -(id)retain; |
24 | -(oneway void)release; |
25 | -(void)dealloc; |
26 | @end |
27 | @interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding> |
28 | - (NSUInteger)length; |
29 | -(id)initWithFormat:(NSString *)f,...; |
30 | -(BOOL)isEqualToString:(NSString *)s; |
31 | + (id)string; |
32 | @end |
33 | @interface NSNumber : NSObject {} |
34 | +(id)alloc; |
35 | -(id)initWithInteger:(int)i; |
36 | @end |
37 | |
38 | // rdar://6946338 |
39 | |
40 | @interface Test1 : NSObject { |
41 | NSString *text; |
42 | } |
43 | -(id)myMethod; |
44 | @property (nonatomic, assign) NSString *text; |
45 | @end |
46 | |
47 | |
48 | #if !__has_feature(objc_arc) |
49 | |
50 | @implementation Test1 |
51 | |
52 | @synthesize text; |
53 | |
54 | -(id)myMethod { |
55 | Test1 *cell = [[[Test1 alloc] init] autorelease]; |
56 | |
57 | NSString *string1 = [[NSString alloc] initWithFormat:@"test %f", 0.0]; // expected-warning {{Potential leak}} |
58 | cell.text = string1; |
59 | |
60 | return cell; |
61 | } |
62 | |
63 | @end |
64 | |
65 | |
66 | // rdar://8824416 |
67 | |
68 | @interface MyNumber : NSObject |
69 | { |
70 | NSNumber* _myNumber; |
71 | } |
72 | |
73 | - (id)initWithNumber:(NSNumber *)number; |
74 | |
75 | @property (nonatomic, readonly) NSNumber* myNumber; |
76 | @property (nonatomic, readonly) NSNumber* newMyNumber; |
77 | |
78 | @end |
79 | |
80 | @implementation MyNumber |
81 | @synthesize myNumber=_myNumber; |
82 | |
83 | - (id)initWithNumber:(NSNumber *)number |
84 | { |
85 | self = [super init]; |
86 | |
87 | if ( self ) |
88 | { |
89 | _myNumber = [number copy]; |
90 | } |
91 | |
92 | return self; |
93 | } |
94 | |
95 | - (NSNumber*)newMyNumber |
96 | { |
97 | if ( _myNumber ) |
98 | return [_myNumber retain]; |
99 | |
100 | return [[NSNumber alloc] initWithInteger:1]; |
101 | } |
102 | |
103 | - (id)valueForUndefinedKey:(NSString*)key |
104 | { |
105 | id value = 0; |
106 | |
107 | if ([key isEqualToString:@"MyIvarNumberAsPropertyOverReleased"]) |
108 | value = self.myNumber; // _myNumber will be over released, since the value returned from self.myNumber is not retained. |
109 | else if ([key isEqualToString:@"MyIvarNumberAsPropertyOk"]) |
110 | value = [self.myNumber retain]; // this line fixes the over release |
111 | else if ([key isEqualToString:@"MyIvarNumberAsNewMyNumber"]) |
112 | value = self.newMyNumber; // this one is ok, since value is returned retained |
113 | else |
114 | value = [[NSNumber alloc] initWithInteger:0]; |
115 | |
116 | return [value autorelease]; // expected-warning {{Object autoreleased too many times}} |
117 | } |
118 | |
119 | @end |
120 | |
121 | NSNumber* numberFromMyNumberProperty(MyNumber* aMyNumber) |
122 | { |
123 | NSNumber* result = aMyNumber.myNumber; |
124 | |
125 | return [result autorelease]; // expected-warning {{Object autoreleased too many times}} |
126 | } |
127 | |
128 | #endif |
129 | |
130 | |
131 | // rdar://6611873 |
132 | |
133 | @interface Person : NSObject { |
134 | NSString *_name; |
135 | } |
136 | @property (retain) NSString * name; |
137 | @property (assign) id friend; |
138 | @end |
139 | |
140 | @implementation Person |
141 | @synthesize name = _name; |
142 | |
143 | -(void)dealloc { |
144 | #if !__has_feature(objc_arc) |
145 | self.name = [[NSString alloc] init]; // expected-warning {{leak}} |
146 | |
147 | [super dealloc]; // expected-warning {{The '_name' ivar in 'Person' was retained by a synthesized property but not released before '[super dealloc]}} |
148 | #endif |
149 | } |
150 | @end |
151 | |
152 | #if !__has_feature(objc_arc) |
153 | void rdar6611873() { |
154 | Person *p = [[[Person alloc] init] autorelease]; |
155 | |
156 | p.name = [[NSString string] retain]; // expected-warning {{leak}} |
157 | p.name = [[NSString alloc] init]; // expected-warning {{leak}} |
158 | |
159 | p.friend = [[Person alloc] init]; // expected-warning {{leak}} |
160 | } |
161 | #endif |
162 | |
163 | @interface SubPerson : Person |
164 | -(NSString *)foo; |
165 | @end |
166 | |
167 | @implementation SubPerson |
168 | -(NSString *)foo { |
169 | return super.name; |
170 | } |
171 | @end |
172 | |
173 | |
174 | #if !__has_feature(objc_arc) |
175 | // <rdar://problem/9241180> Static analyzer doesn't detect uninitialized variable issues for property accesses |
176 | @interface RDar9241180 |
177 | @property (readwrite,assign) id x; |
178 | -(id)testAnalyzer1:(int) y; |
179 | -(void)testAnalyzer2; |
180 | @end |
181 | |
182 | @implementation RDar9241180 |
183 | @synthesize x; |
184 | -(id)testAnalyzer1:(int)y { |
185 | RDar9241180 *o; |
186 | if (y && o.x) // expected-warning {{Property access on an uninitialized object pointer}} |
187 | return o; |
188 | return o; // expected-warning {{Undefined or garbage value returned to caller}} |
189 | } |
190 | -(void)testAnalyzer2 { |
191 | id y; |
192 | self.x = y; // expected-warning {{Argument for property setter is an uninitialized value}} |
193 | } |
194 | @end |
195 | #endif |
196 | |
197 | |
198 | //------ |
199 | // Property accessor synthesis |
200 | //------ |
201 | |
202 | extern void doSomethingWithPerson(Person *p); |
203 | extern void doSomethingWithName(NSString *name); |
204 | |
205 | void testConsistencyRetain(Person *p) { |
206 | clang_analyzer_eval(p.name == p.name); // expected-warning{{TRUE}} |
207 | |
208 | id origName = p.name; |
209 | clang_analyzer_eval(p.name == origName); // expected-warning{{TRUE}} |
210 | doSomethingWithPerson(p); |
211 | clang_analyzer_eval(p.name == origName); // expected-warning{{UNKNOWN}} |
212 | } |
213 | |
214 | void testConsistencyAssign(Person *p) { |
215 | clang_analyzer_eval(p.friend == p.friend); // expected-warning{{TRUE}} |
216 | |
217 | id origFriend = p.friend; |
218 | clang_analyzer_eval(p.friend == origFriend); // expected-warning{{TRUE}} |
219 | doSomethingWithPerson(p); |
220 | clang_analyzer_eval(p.friend == origFriend); // expected-warning{{UNKNOWN}} |
221 | } |
222 | |
223 | @interface ClassWithShadowedReadWriteProperty { |
224 | int _f; |
225 | } |
226 | @property (readonly) int someProp; |
227 | @end |
228 | |
229 | @interface ClassWithShadowedReadWriteProperty () |
230 | @property (readwrite) int someProp; |
231 | @end |
232 | |
233 | @implementation ClassWithShadowedReadWriteProperty |
234 | - (void)testSynthesisForShadowedReadWriteProperties; { |
235 | clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}} |
236 | |
237 | _f = 1; |
238 | |
239 | // Read of shadowed property should not invalidate receiver. |
240 | (void)self.someProp; |
241 | clang_analyzer_eval(_f == 1); // expected-warning{{TRUE}} |
242 | |
243 | _f = 2; |
244 | // Call to getter of shadowed property should not invalidate receiver. |
245 | (void)[self someProp]; |
246 | clang_analyzer_eval(_f == 2); // expected-warning{{TRUE}} |
247 | } |
248 | @end |
249 | |
250 | // Tests for the analyzer fix that works around a Sema bug |
251 | // where multiple methods are created for properties in class extensions that |
252 | // are redeclared in a category method. |
253 | // The Sema bug is tracked as <rdar://problem/25481164>. |
254 | @interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory |
255 | @end |
256 | |
257 | @interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory () |
258 | @end |
259 | |
260 | @interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory () |
261 | @property (readwrite) int someProp; |
262 | @property (readonly) int otherProp; |
263 | @end |
264 | |
265 | @interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory (MyCat) |
266 | @property (readonly) int someProp; |
267 | @property (readonly) int otherProp; |
268 | @end |
269 | |
270 | @implementation ClassWithRedeclaredPropertyInExtensionFollowedByCategory |
271 | - (void)testSynthesisForRedeclaredProperties; { |
272 | clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}} |
273 | clang_analyzer_eval([self someProp] == self.someProp); // expected-warning{{TRUE}} |
274 | |
275 | clang_analyzer_eval(self.otherProp == self.otherProp); // expected-warning{{TRUE}} |
276 | clang_analyzer_eval([self otherProp] == self.otherProp); // expected-warning{{TRUE}} |
277 | } |
278 | @end |
279 | |
280 | // The relative order of the extension and the category matter, so test both. |
281 | @interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension |
282 | @end |
283 | |
284 | @interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension () |
285 | @property (readwrite) int someProp; |
286 | @end |
287 | |
288 | @interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension (MyCat) |
289 | @property (readonly) int someProp; |
290 | @end |
291 | |
292 | @implementation ClassWithRedeclaredPropertyInCategoryFollowedByExtension |
293 | - (void)testSynthesisForRedeclaredProperties; { |
294 | clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}} |
295 | clang_analyzer_eval([self someProp] == self.someProp); // expected-warning{{TRUE}} |
296 | } |
297 | @end |
298 | |
299 | @interface ClassWithSynthesizedPropertyAndGetter |
300 | @property (readonly) int someProp; |
301 | @end |
302 | |
303 | @implementation ClassWithSynthesizedPropertyAndGetter |
304 | @synthesize someProp; |
305 | |
306 | // Make sure that the actual getter is inlined and not a getter created |
307 | // by BodyFarm |
308 | - (void)testBodyFarmGetterNotUsed { |
309 | int i = self.someProp; |
310 | clang_analyzer_eval(i == 22); // expected-warning {{TRUE}} |
311 | } |
312 | |
313 | -(int)someProp { |
314 | return 22; |
315 | } |
316 | @end |
317 | |
318 | __attribute__((objc_root_class)) |
319 | @interface ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory |
320 | @end |
321 | |
322 | @protocol HasStuff |
323 | @property (nonatomic, readonly) int stuffProperty; |
324 | @end |
325 | |
326 | @interface ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory (Private) |
327 | @property (nonatomic, readonly) int stuffProperty; |
328 | @end |
329 | |
330 | @interface ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory (Internal) <HasStuff> |
331 | @end |
332 | |
333 | @interface ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory() <HasStuff> |
334 | @end |
335 | |
336 | @implementation ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory |
337 | @synthesize stuffProperty = _stuffProperty; |
338 | |
339 | -(void)foo { |
340 | (void)self.stuffProperty; |
341 | } |
342 | @end |
343 | |
344 | //------ |
345 | // Setter ivar invalidation. |
346 | //------ |
347 | |
348 | @interface ClassWithSetters |
349 | // Note: These properties have implicit @synthesize implementations to be |
350 | // backed with ivars. |
351 | @property (assign) int propWithIvar1; |
352 | @property (assign) int propWithIvar2; |
353 | |
354 | @property (retain) NSNumber *retainedProperty; |
355 | |
356 | @end |
357 | |
358 | @interface ClassWithSetters (InOtherTranslationUnit) |
359 | // The implementation of this property is in another translation unit. |
360 | // We don't know whether it is backed by an ivar or not. |
361 | @property (assign) int propInOther; |
362 | @end |
363 | |
364 | @implementation ClassWithSetters |
365 | - (void) testSettingPropWithIvarInvalidatesExactlyThatIvar; { |
366 | _propWithIvar1 = 1; |
367 | _propWithIvar2 = 2; |
368 | self.propWithIvar1 = 66; |
369 | |
370 | // Calling the setter of a property backed by the instance variable |
371 | // should invalidate the storage for the instance variable but not |
372 | // the rest of the receiver. Ideally we would model the setter completely |
373 | // but doing so would cause the new value to escape when it is bound |
374 | // to the ivar. This would cause bad false negatives in the retain count |
375 | // checker. (There is a test for this scenario in |
376 | // testWriteRetainedValueToRetainedProperty below). |
377 | clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}} |
378 | clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{TRUE}} |
379 | |
380 | _propWithIvar1 = 1; |
381 | [self setPropWithIvar1:66]; |
382 | |
383 | clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}} |
384 | clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{TRUE}} |
385 | } |
386 | |
387 | - (void) testSettingPropWithoutIvarInvalidatesEntireInstance; { |
388 | _propWithIvar1 = 1; |
389 | _propWithIvar2 = 2; |
390 | self.propInOther = 66; |
391 | |
392 | clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}} |
393 | clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{UNKNOWN}} |
394 | |
395 | _propWithIvar1 = 1; |
396 | _propWithIvar2 = 2; |
397 | [self setPropInOther:66]; |
398 | |
399 | clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}} |
400 | clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{UNKNOWN}} |
401 | } |
402 | |
403 | #if !__has_feature(objc_arc) |
404 | - (void) testWriteRetainedValueToRetainedProperty; { |
405 | NSNumber *number = [[NSNumber alloc] initWithInteger:5]; // expected-warning {{Potential leak of an object stored into 'number'}} |
406 | |
407 | // Make sure we catch this leak. |
408 | self.retainedProperty = number; |
409 | } |
410 | #endif |
411 | @end |
412 | |
413 | //------ |
414 | // class properties |
415 | //------ |
416 | |
417 | int gBackingForReadWriteClassProp = 0; |
418 | |
419 | @interface ClassWithClassProperties |
420 | @property(class, readonly) int readOnlyClassProp; |
421 | |
422 | @property(class) int readWriteClassProp; |
423 | |
424 | // Make sure we handle when a class and instance property have the same |
425 | // name. Test both when instance comes first and when class comes first. |
426 | @property(readonly) int classAndInstancePropWithSameNameOrderInstanceFirst; |
427 | @property(class, readonly) int classAndInstancePropWithSameNameOrderInstanceFirst; |
428 | |
429 | @property(class, readonly) int classAndInstancePropWithSameNameOrderClassFirst; |
430 | @property(readonly) int classAndInstancePropWithSameNameOrderClassFirst; |
431 | |
432 | |
433 | @property(class, readonly) int dynamicClassProp; |
434 | |
435 | @end |
436 | |
437 | @interface ClassWithClassProperties (OtherTranslationUnit) |
438 | @property(class, assign) id propInOtherTranslationUnit; |
439 | @end |
440 | |
441 | @implementation ClassWithClassProperties |
442 | |
443 | @dynamic dynamicClassProp; |
444 | |
445 | + (int)readOnlyClassProp { |
446 | return 1; |
447 | } |
448 | |
449 | + (int)readWriteClassProp { |
450 | return gBackingForReadWriteClassProp; |
451 | } |
452 | |
453 | + (void)setReadWriteClassProp:(int)val { |
454 | gBackingForReadWriteClassProp = val; |
455 | } |
456 | |
457 | - (int)classAndInstancePropWithSameNameOrderInstanceFirst { |
458 | return 12; |
459 | } |
460 | |
461 | + (int)classAndInstancePropWithSameNameOrderInstanceFirst { |
462 | return 13; |
463 | } |
464 | |
465 | + (int)classAndInstancePropWithSameNameOrderClassFirst { |
466 | return 14; |
467 | } |
468 | |
469 | - (int)classAndInstancePropWithSameNameOrderClassFirst { |
470 | return 15; |
471 | } |
472 | |
473 | - (void)testInlineClassProp { |
474 | clang_analyzer_eval(ClassWithClassProperties.readOnlyClassProp == 1); // expected-warning{{TRUE}} |
475 | |
476 | ClassWithClassProperties.readWriteClassProp = 7; |
477 | clang_analyzer_eval(ClassWithClassProperties.readWriteClassProp == 7); // expected-warning{{TRUE}} |
478 | ClassWithClassProperties.readWriteClassProp = 8; |
479 | clang_analyzer_eval(ClassWithClassProperties.readWriteClassProp == 8); // expected-warning{{TRUE}} |
480 | } |
481 | |
482 | - (void)testUnknownClassProp { |
483 | clang_analyzer_eval(ClassWithClassProperties.propInOtherTranslationUnit == ClassWithClassProperties.propInOtherTranslationUnit); // expected-warning{{UNKNOWN}} |
484 | } |
485 | |
486 | - (void)testEscapeGlobalOnUnknownProp { |
487 | gBackingForReadWriteClassProp = 33; |
488 | ClassWithClassProperties.propInOtherTranslationUnit = 0; |
489 | clang_analyzer_eval(gBackingForReadWriteClassProp == 33); // expected-warning{{UNKNOWN}} |
490 | } |
491 | |
492 | - (void)testClassAndInstancePropertyWithSameName { |
493 | clang_analyzer_eval(self.classAndInstancePropWithSameNameOrderInstanceFirst == 12); // expected-warning{{TRUE}} |
494 | clang_analyzer_eval(ClassWithClassProperties.classAndInstancePropWithSameNameOrderInstanceFirst == 13); // expected-warning{{TRUE}} |
495 | |
496 | clang_analyzer_eval(ClassWithClassProperties.classAndInstancePropWithSameNameOrderClassFirst == 14); // expected-warning{{TRUE}} |
497 | clang_analyzer_eval(self.classAndInstancePropWithSameNameOrderClassFirst == 15); // expected-warning{{TRUE}} |
498 | } |
499 | |
500 | - (void)testDynamicClassProp { |
501 | clang_analyzer_eval(ClassWithClassProperties.dynamicClassProp == 16); // expected-warning{{UNKNOWN}} |
502 | } |
503 | |
504 | @end |
505 | |
506 | @interface SubclassOfClassWithClassProperties : ClassWithClassProperties |
507 | @end |
508 | |
509 | @implementation SubclassOfClassWithClassProperties |
510 | + (int)dynamicClassProp; { |
511 | return 16; |
512 | } |
513 | |
514 | - (void)testDynamicClassProp { |
515 | clang_analyzer_eval(SubclassOfClassWithClassProperties.dynamicClassProp == 16); // expected-warning{{TRUE}} |
516 | } |
517 | |
518 | @end |
519 | |
520 | |
521 | #if !__has_feature(objc_arc) |
522 | void testOverrelease(Person *p, int coin) { |
523 | switch (coin) { |
524 | case 0: |
525 | [p.name release]; // expected-warning{{not owned}} |
526 | break; |
527 | case 1: |
528 | [p.friend release]; // expected-warning{{not owned}} |
529 | break; |
530 | case 2: { |
531 | id friend = p.friend; |
532 | doSomethingWithPerson(p); |
533 | [friend release]; // expected-warning{{not owned}} |
534 | } |
535 | } |
536 | } |
537 | |
538 | // <rdar://problem/16333368> |
539 | @implementation Person (Rdar16333368) |
540 | |
541 | - (void)testDeliberateRelease:(Person *)other { |
542 | doSomethingWithName(self.name); |
543 | [_name release]; // no-warning |
544 | self->_name = 0; |
545 | |
546 | doSomethingWithName(other->_name); |
547 | [other.name release]; // no-warning |
548 | } |
549 | |
550 | - (void)deliberateReleaseFalseNegative { |
551 | // This is arguably a false negative because the result of p.friend shouldn't |
552 | // be released, even though we are manipulating the ivar in between the two |
553 | // actions. |
554 | id name = self.name; |
555 | _name = 0; |
556 | [name release]; |
557 | } |
558 | |
559 | - (void)testRetainAndRelease { |
560 | [self.name retain]; |
561 | [self.name release]; |
562 | [self.name release]; // expected-warning{{not owned}} |
563 | } |
564 | |
565 | - (void)testRetainAndReleaseIVar { |
566 | [self.name retain]; |
567 | [_name release]; |
568 | [_name release]; |
569 | } |
570 | |
571 | @end |
572 | #endif |
573 | |
574 | @interface IntWrapper |
575 | @property int value; |
576 | @end |
577 | |
578 | @implementation IntWrapper |
579 | @synthesize value; |
580 | @end |
581 | |
582 | void testConsistencyInt(IntWrapper *w) { |
583 | clang_analyzer_eval(w.value == w.value); // expected-warning{{TRUE}} |
584 | |
585 | int origValue = w.value; |
586 | if (origValue != 42) |
587 | return; |
588 | |
589 | clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}} |
590 | } |
591 | |
592 | void testConsistencyInt2(IntWrapper *w) { |
593 | if (w.value != 42) |
594 | return; |
595 | |
596 | clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}} |
597 | } |
598 | |
599 | |
600 | @interface IntWrapperAuto |
601 | @property int value; |
602 | @end |
603 | |
604 | @implementation IntWrapperAuto |
605 | @end |
606 | |
607 | void testConsistencyIntAuto(IntWrapperAuto *w) { |
608 | clang_analyzer_eval(w.value == w.value); // expected-warning{{TRUE}} |
609 | |
610 | int origValue = w.value; |
611 | if (origValue != 42) |
612 | return; |
613 | |
614 | clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}} |
615 | } |
616 | |
617 | void testConsistencyIntAuto2(IntWrapperAuto *w) { |
618 | if (w.value != 42) |
619 | return; |
620 | |
621 | clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}} |
622 | } |
623 | |
624 | |
625 | typedef struct { |
626 | int value; |
627 | } IntWrapperStruct; |
628 | |
629 | @interface StructWrapper |
630 | @property IntWrapperStruct inner; |
631 | @end |
632 | |
633 | @implementation StructWrapper |
634 | @synthesize inner; |
635 | @end |
636 | |
637 | void testConsistencyStruct(StructWrapper *w) { |
638 | clang_analyzer_eval(w.inner.value == w.inner.value); // expected-warning{{TRUE}} |
639 | |
640 | int origValue = w.inner.value; |
641 | if (origValue != 42) |
642 | return; |
643 | |
644 | clang_analyzer_eval(w.inner.value == 42); // expected-warning{{TRUE}} |
645 | } |
646 | |
647 | |
648 | @interface OpaqueIntWrapper |
649 | @property int value; |
650 | @end |
651 | |
652 | // For now, don't assume a property is implemented using an ivar unless we can |
653 | // actually see that it is. |
654 | void testOpaqueConsistency(OpaqueIntWrapper *w) { |
655 | clang_analyzer_eval(w.value == w.value); // expected-warning{{UNKNOWN}} |
656 | } |
657 | |
658 | |
659 | #if !__has_feature(objc_arc) |
660 | // Test quite a few cases of retain/release issues. |
661 | |
662 | @interface RetainCountTesting |
663 | @property (strong) id ownedProp; |
664 | @property (unsafe_unretained) id unownedProp; |
665 | @property (nonatomic, strong) id manualProp; |
666 | @property (readonly) id readonlyProp; |
667 | @property (nonatomic, readwrite/*, assign */) id implicitManualProp; // expected-warning {{'assign' is assumed}} expected-warning {{'assign' not appropriate}} |
668 | @property (nonatomic, readwrite/*, assign */) id implicitSynthProp; // expected-warning {{'assign' is assumed}} expected-warning {{'assign' not appropriate}} |
669 | @property CFTypeRef cfProp; |
670 | @end |
671 | |
672 | @implementation RetainCountTesting { |
673 | id _ivarOnly; |
674 | } |
675 | |
676 | - (id)manualProp { |
677 | return _manualProp; |
678 | } |
679 | |
680 | - (void)setImplicitManualProp:(id)newValue {} |
681 | |
682 | - (void)testOverreleaseOwnedIvar { |
683 | [_ownedProp retain]; |
684 | [_ownedProp release]; |
685 | [_ownedProp release]; |
686 | [_ownedProp release]; // FIXME-warning{{used after it is released}} |
687 | } |
688 | |
689 | - (void)testOverreleaseUnownedIvar { |
690 | [_unownedProp retain]; |
691 | [_unownedProp release]; |
692 | [_unownedProp release]; // FIXME-warning{{not owned at this point by the caller}} |
693 | } |
694 | |
695 | - (void)testOverreleaseIvarOnly { |
696 | [_ivarOnly retain]; |
697 | [_ivarOnly release]; |
698 | [_ivarOnly release]; |
699 | [_ivarOnly release]; // FIXME-warning{{used after it is released}} |
700 | } |
701 | |
702 | - (void)testOverreleaseReadonlyIvar { |
703 | [_readonlyProp retain]; |
704 | [_readonlyProp release]; |
705 | [_readonlyProp release]; |
706 | [_readonlyProp release]; // FIXME-warning{{used after it is released}} |
707 | } |
708 | |
709 | - (void)testOverreleaseImplicitManualIvar { |
710 | [_implicitManualProp retain]; |
711 | [_implicitManualProp release]; |
712 | [_implicitManualProp release]; |
713 | [_implicitManualProp release]; // FIXME-warning{{used after it is released}} |
714 | } |
715 | |
716 | - (void)testOverreleaseImplicitSynthIvar { |
717 | [_implicitSynthProp retain]; |
718 | [_implicitSynthProp release]; |
719 | [_implicitSynthProp release]; // FIXME-warning{{not owned at this point by the caller}} |
720 | } |
721 | |
722 | - (void)testOverreleaseCF { |
723 | CFRetain(_cfProp); |
724 | CFRelease(_cfProp); |
725 | CFRelease(_cfProp); |
726 | CFRelease(_cfProp); // FIXME-warning{{used after it is released}} |
727 | } |
728 | |
729 | - (void)testOverreleaseOwnedIvarUse { |
730 | [_ownedProp retain]; |
731 | [_ownedProp release]; |
732 | [_ownedProp release]; |
733 | [_ownedProp myMethod]; // FIXME-warning{{used after it is released}} |
734 | } |
735 | |
736 | - (void)testOverreleaseIvarOnlyUse { |
737 | [_ivarOnly retain]; |
738 | [_ivarOnly release]; |
739 | [_ivarOnly release]; |
740 | [_ivarOnly myMethod]; // FIXME-warning{{used after it is released}} |
741 | } |
742 | |
743 | - (void)testOverreleaseCFUse { |
744 | CFRetain(_cfProp); |
745 | CFRelease(_cfProp); |
746 | CFRelease(_cfProp); |
747 | |
748 | extern void CFUse(CFTypeRef); |
749 | CFUse(_cfProp); // FIXME-warning{{used after it is released}} |
750 | } |
751 | |
752 | - (void)testOverreleaseOwnedIvarAutoreleaseOkay { |
753 | [_ownedProp retain]; |
754 | [_ownedProp release]; |
755 | [_ownedProp autorelease]; |
756 | } // no-warning |
757 | |
758 | - (void)testOverreleaseIvarOnlyAutoreleaseOkay { |
759 | [_ivarOnly retain]; |
760 | [_ivarOnly release]; |
761 | [_ivarOnly autorelease]; |
762 | } // no-warning |
763 | |
764 | - (void)testOverreleaseOwnedIvarAutorelease { |
765 | [_ownedProp retain]; |
766 | [_ownedProp release]; |
767 | [_ownedProp autorelease]; |
768 | [_ownedProp autorelease]; |
769 | } // FIXME-warning{{Object autoreleased too many times}} |
770 | |
771 | - (void)testOverreleaseIvarOnlyAutorelease { |
772 | [_ivarOnly retain]; |
773 | [_ivarOnly release]; |
774 | [_ivarOnly autorelease]; |
775 | [_ivarOnly autorelease]; |
776 | } // FIXME-warning{{Object autoreleased too many times}} |
777 | |
778 | - (void)testPropertyAccessThenReleaseOwned { |
779 | id owned = [self.ownedProp retain]; |
780 | [owned release]; |
781 | [_ownedProp release]; |
782 | clang_analyzer_eval(owned == _ownedProp); // expected-warning{{TRUE}} |
783 | } |
784 | |
785 | - (void)testPropertyAccessThenReleaseOwned2 { |
786 | id fromIvar = _ownedProp; |
787 | id owned = [self.ownedProp retain]; |
788 | [owned release]; |
789 | [fromIvar release]; |
790 | clang_analyzer_eval(owned == fromIvar); // expected-warning{{TRUE}} |
791 | } |
792 | |
793 | - (void)testPropertyAccessThenReleaseUnowned { |
794 | id unowned = [self.unownedProp retain]; |
795 | [unowned release]; |
796 | [_unownedProp release]; // FIXME-warning{{not owned}} |
797 | } |
798 | |
799 | - (void)testPropertyAccessThenReleaseUnowned2 { |
800 | id fromIvar = _unownedProp; |
801 | id unowned = [self.unownedProp retain]; |
802 | [unowned release]; |
803 | clang_analyzer_eval(unowned == fromIvar); // expected-warning{{TRUE}} |
804 | [fromIvar release]; // FIXME-warning{{not owned}} |
805 | } |
806 | |
807 | - (void)testPropertyAccessThenReleaseManual { |
808 | id prop = [self.manualProp retain]; |
809 | [prop release]; |
810 | [_manualProp release]; // no-warning |
811 | } |
812 | |
813 | - (void)testPropertyAccessThenReleaseManual2 { |
814 | id fromIvar = _manualProp; |
815 | id prop = [self.manualProp retain]; |
816 | [prop release]; |
817 | clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}} |
818 | [fromIvar release]; // no-warning |
819 | } |
820 | |
821 | - (void)testPropertyAccessThenReleaseCF { |
822 | CFTypeRef owned = CFRetain(self.cfProp); |
823 | CFRelease(owned); |
824 | CFRelease(_cfProp); // no-warning |
825 | clang_analyzer_eval(owned == _cfProp); // expected-warning{{TRUE}} |
826 | } |
827 | |
828 | - (void)testPropertyAccessThenReleaseCF2 { |
829 | CFTypeRef fromIvar = _cfProp; |
830 | CFTypeRef owned = CFRetain(self.cfProp); |
831 | CFRelease(owned); |
832 | CFRelease(fromIvar); |
833 | clang_analyzer_eval(owned == fromIvar); // expected-warning{{TRUE}} |
834 | } |
835 | |
836 | - (void)testPropertyAccessThenReleaseReadonly { |
837 | id prop = [self.readonlyProp retain]; |
838 | [prop release]; |
839 | [_readonlyProp release]; // no-warning |
840 | } |
841 | |
842 | - (void)testPropertyAccessThenReleaseReadonly2 { |
843 | id fromIvar = _readonlyProp; |
844 | id prop = [self.readonlyProp retain]; |
845 | [prop release]; |
846 | clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}} |
847 | [fromIvar release]; // no-warning |
848 | } |
849 | |
850 | - (void)testPropertyAccessThenReleaseImplicitManual { |
851 | id prop = [self.implicitManualProp retain]; |
852 | [prop release]; |
853 | [_implicitManualProp release]; // no-warning |
854 | } |
855 | |
856 | - (void)testPropertyAccessThenReleaseImplicitManual2 { |
857 | id fromIvar = _implicitManualProp; |
858 | id prop = [self.implicitManualProp retain]; |
859 | [prop release]; |
860 | clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}} |
861 | [fromIvar release]; // no-warning |
862 | } |
863 | |
864 | - (void)testPropertyAccessThenReleaseImplicitSynth { |
865 | id prop = [self.implicitSynthProp retain]; |
866 | [prop release]; |
867 | [_implicitSynthProp release]; // FIXME-warning{{not owned}} |
868 | } |
869 | |
870 | - (void)testPropertyAccessThenReleaseImplicitSynth2 { |
871 | id fromIvar = _implicitSynthProp; |
872 | id prop = [self.implicitSynthProp retain]; |
873 | [prop release]; |
874 | clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}} |
875 | [fromIvar release]; // FIXME-warning{{not owned}} |
876 | } |
877 | |
878 | - (id)getUnownedFromProperty { |
879 | [_ownedProp retain]; |
880 | [_ownedProp autorelease]; |
881 | return _ownedProp; // no-warning |
882 | } |
883 | |
884 | - (id)transferUnownedFromProperty { |
885 | [_ownedProp retain]; |
886 | [_ownedProp autorelease]; |
887 | return [_ownedProp autorelease]; // no-warning |
888 | } |
889 | |
890 | - (id)transferOwnedFromProperty __attribute__((ns_returns_retained)) { |
891 | [_ownedProp retain]; |
892 | [_ownedProp autorelease]; |
893 | return _ownedProp; // no-warning |
894 | } |
895 | |
896 | - (void)testAssignOwned:(id)newValue { |
897 | _ownedProp = newValue; |
898 | [_ownedProp release]; // FIXME: no-warning{{not owned}} |
899 | } |
900 | |
901 | - (void)testAssignUnowned:(id)newValue { |
902 | _unownedProp = newValue; |
903 | [_unownedProp release]; // FIXME: no-warning{{not owned}} |
904 | } |
905 | |
906 | - (void)testAssignIvarOnly:(id)newValue { |
907 | _ivarOnly = newValue; |
908 | [_ivarOnly release]; // FIXME: no-warning{{not owned}} |
909 | } |
910 | |
911 | - (void)testAssignCF:(CFTypeRef)newValue { |
912 | _cfProp = newValue; |
913 | CFRelease(_cfProp); // FIXME: no-warning{{not owned}} |
914 | } |
915 | |
916 | - (void)testAssignReadonly:(id)newValue { |
917 | _readonlyProp = newValue; |
918 | [_readonlyProp release]; // FIXME: no-warning{{not owned}} |
919 | } |
920 | |
921 | - (void)testAssignImplicitManual:(id)newValue { |
922 | _implicitManualProp = newValue; |
923 | [_implicitManualProp release]; // FIXME: no-warning{{not owned}} |
924 | } |
925 | |
926 | - (void)testAssignImplicitSynth:(id)newValue { |
927 | _implicitSynthProp = newValue; |
928 | [_implicitSynthProp release]; // FIXME: no-warning{{not owned}} |
929 | } |
930 | |
931 | - (void)testAssignOwnedOkay:(id)newValue { |
932 | _ownedProp = [newValue retain]; |
933 | [_ownedProp release]; // no-warning |
934 | } |
935 | |
936 | - (void)testAssignUnownedOkay:(id)newValue { |
937 | _unownedProp = [newValue retain]; |
938 | [_unownedProp release]; // no-warning |
939 | } |
940 | |
941 | - (void)testAssignIvarOnlyOkay:(id)newValue { |
942 | _ivarOnly = [newValue retain]; |
943 | [_ivarOnly release]; // no-warning |
944 | } |
945 | |
946 | - (void)testAssignCFOkay:(CFTypeRef)newValue { |
947 | _cfProp = CFRetain(newValue); |
948 | CFRelease(_cfProp); // no-warning |
949 | } |
950 | |
951 | - (void)testAssignReadonlyOkay:(id)newValue { |
952 | _readonlyProp = [newValue retain]; |
953 | [_readonlyProp release]; // FIXME: no-warning{{not owned}} |
954 | } |
955 | |
956 | - (void)testAssignImplicitManualOkay:(id)newValue { |
957 | _implicitManualProp = [newValue retain]; |
958 | [_implicitManualProp release]; // FIXME: no-warning{{not owned}} |
959 | } |
960 | |
961 | - (void)testAssignImplicitSynthOkay:(id)newValue { |
962 | _implicitSynthProp = [newValue retain]; |
963 | [_implicitSynthProp release]; // FIXME: no-warning{{not owned}} |
964 | } |
965 | |
966 | // rdar://problem/19862648 |
967 | - (void)establishIvarIsNilDuringLoops { |
968 | extern id getRandomObject(); |
969 | |
970 | int i = 4; // Must be at least 4 to trigger the bug. |
971 | while (--i) { |
972 | id x = 0; |
973 | if (getRandomObject()) |
974 | x = _ivarOnly; |
975 | if (!x) |
976 | x = getRandomObject(); |
977 | [x myMethod]; |
978 | } |
979 | } |
980 | |
981 | // rdar://problem/20335433 |
982 | - (void)retainIvarAndInvalidateSelf { |
983 | extern void invalidate(id); |
984 | [_unownedProp retain]; |
985 | invalidate(self); |
986 | [_unownedProp release]; // no-warning |
987 | } |
988 | |
989 | @end |
990 | |
991 | @interface Wrapper |
992 | @property(nonatomic, readonly) int value; |
993 | @end |
994 | |
995 | @implementation Wrapper |
996 | @synthesize value; |
997 | @end |
998 | |
999 | void testNoCrashWhenAccessPropertyAndThereAreNoDirectBindingsAtAll() { |
1000 | union { |
1001 | Wrapper *wrapper; |
1002 | } u = { 0 }; |
1003 | [u.wrapper value]; |
1004 | } |
1005 | |
1006 | #endif // non-ARC |
1007 | |
1008 | @interface ExplicitAccessorInCategory : NSObject |
1009 | @property(readonly) int normal; |
1010 | - (int)normal; |
1011 | @property(readonly) int no_custom_accessor; |
1012 | @end |
1013 | |
1014 | @interface ExplicitAccessorInCategory () |
1015 | @property(readonly) int in_category; |
1016 | |
1017 | @property(readonly) int still_no_custom_accessor; |
1018 | // This is an ordinary method, not a getter. |
1019 | - (int)still_no_custom_accessor; |
1020 | @end |
1021 | |
1022 | @interface ExplicitAccessorInCategory () |
1023 | - (int)in_category; |
1024 | |
1025 | // This is an ordinary method, not a getter. |
1026 | - (int)no_custom_accessor; |
1027 | @end |
1028 | |
1029 | @implementation ExplicitAccessorInCategory |
1030 | - (void)foo { |
1031 | // Make sure we don't farm bodies for explicit accessors: in particular, |
1032 | // we're not sure that the accessor always returns the same value. |
1033 | clang_analyzer_eval(self.normal == self.normal); // expected-warning{{UNKNOWN}} |
1034 | // Also this used to crash. |
1035 | clang_analyzer_eval(self.in_category == self.in_category); // expected-warning{{UNKNOWN}} |
1036 | |
1037 | // When there is no explicit accessor defined (even if it looks like there is), |
1038 | // farm the getter body and see if it does actually always yield the same value. |
1039 | clang_analyzer_eval(self.no_custom_accessor == self.no_custom_accessor); // expected-warning{{TRUE}} |
1040 | clang_analyzer_eval(self.still_no_custom_accessor == self.still_no_custom_accessor); // expected-warning{{TRUE}} |
1041 | } |
1042 | @end |
1043 | |