properties.m revision 0e2c34f92f00628d48968dfea096d36381f494cb
1// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s
2// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s
3
4void clang_analyzer_eval(int);
5
6typedef signed char BOOL;
7typedef unsigned int NSUInteger;
8typedef struct _NSZone NSZone;
9@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
10@protocol NSObject  - (BOOL)isEqual:(id)object; @end
11@protocol NSCopying  - (id)copyWithZone:(NSZone *)zone; @end
12@protocol NSCoding  - (void)encodeWithCoder:(NSCoder *)aCoder; @end
13@protocol NSMutableCopying  - (id)mutableCopyWithZone:(NSZone *)zone; @end
14@interface NSObject <NSObject> {}
15+(id)alloc;
16-(id)init;
17-(id)autorelease;
18-(id)copy;
19-(id)retain;
20-(oneway void)release;
21@end
22@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
23- (NSUInteger)length;
24-(id)initWithFormat:(NSString *)f,...;
25-(BOOL)isEqualToString:(NSString *)s;
26+ (id)string;
27@end
28@interface NSNumber : NSObject {}
29+(id)alloc;
30-(id)initWithInteger:(int)i;
31@end
32
33// rdar://6946338
34
35@interface Test1 : NSObject {
36  NSString *text;
37}
38-(id)myMethod;
39@property (nonatomic, assign) NSString *text;
40@end
41
42
43#if !__has_feature(objc_arc)
44
45@implementation Test1
46
47@synthesize text;
48
49-(id)myMethod {
50  Test1 *cell = [[[Test1 alloc] init] autorelease];
51
52  NSString *string1 = [[NSString alloc] initWithFormat:@"test %f", 0.0]; // expected-warning {{Potential leak}}
53  cell.text = string1;
54
55  return cell;
56}
57
58@end
59
60
61// rdar://8824416
62
63@interface MyNumber : NSObject
64{
65  NSNumber* _myNumber;
66}
67
68- (id)initWithNumber:(NSNumber *)number;
69
70@property (nonatomic, readonly) NSNumber* myNumber;
71@property (nonatomic, readonly) NSNumber* newMyNumber;
72
73@end
74
75@implementation MyNumber
76@synthesize myNumber=_myNumber;
77 
78- (id)initWithNumber:(NSNumber *)number
79{
80  self = [super init];
81  
82  if ( self )
83  {
84    _myNumber = [number copy];
85  }
86  
87  return self;
88}
89
90- (NSNumber*)newMyNumber
91{
92  if ( _myNumber )
93    return [_myNumber retain];
94  
95  return [[NSNumber alloc] initWithInteger:1];
96}
97
98- (id)valueForUndefinedKey:(NSString*)key
99{
100  id value = 0;
101  
102  if ([key isEqualToString:@"MyIvarNumberAsPropertyOverReleased"])
103    value = self.myNumber; // _myNumber will be over released, since the value returned from self.myNumber is not retained.
104  else if ([key isEqualToString:@"MyIvarNumberAsPropertyOk"])
105    value = [self.myNumber retain]; // this line fixes the over release
106  else if ([key isEqualToString:@"MyIvarNumberAsNewMyNumber"])
107    value = self.newMyNumber; // this one is ok, since value is returned retained
108  else 
109    value = [[NSNumber alloc] initWithInteger:0];
110  
111  return [value autorelease]; // expected-warning {{Object autoreleased too many times}}
112}
113
114@end
115
116NSNumber* numberFromMyNumberProperty(MyNumber* aMyNumber)
117{
118  NSNumber* result = aMyNumber.myNumber;
119    
120  return [result autorelease]; // expected-warning {{Object autoreleased too many times}}
121}
122
123#endif
124
125
126// rdar://6611873
127
128@interface Person : NSObject {
129  NSString *_name;
130}
131@property (retain) NSString * name;
132@property (assign) id friend;
133@end
134
135@implementation Person
136@synthesize name = _name;
137@end
138
139#if !__has_feature(objc_arc)
140void rdar6611873() {
141  Person *p = [[[Person alloc] init] autorelease];
142  
143  p.name = [[NSString string] retain]; // expected-warning {{leak}}
144  p.name = [[NSString alloc] init]; // expected-warning {{leak}}
145
146  p.friend = [[Person alloc] init]; // expected-warning {{leak}}
147}
148#endif
149
150@interface SubPerson : Person
151-(NSString *)foo;
152@end
153
154@implementation SubPerson
155-(NSString *)foo {
156  return super.name;
157}
158@end
159
160
161#if !__has_feature(objc_arc)
162// <rdar://problem/9241180> Static analyzer doesn't detect uninitialized variable issues for property accesses
163@interface RDar9241180
164@property (readwrite,assign) id x;
165-(id)testAnalyzer1:(int) y;
166-(void)testAnalyzer2;
167@end
168
169@implementation RDar9241180
170@synthesize x;
171-(id)testAnalyzer1:(int)y {
172    RDar9241180 *o;
173    if (y && o.x) // expected-warning {{Property access on an uninitialized object pointer}}
174      return o;
175    return o; // expected-warning {{Undefined or garbage value returned to caller}}
176}
177-(void)testAnalyzer2 {
178  id y;
179  self.x = y;  // expected-warning {{Argument for property setter is an uninitialized value}}
180}
181@end
182#endif
183
184
185//------
186// Property accessor synthesis
187//------
188
189extern void doSomethingWithPerson(Person *p);
190extern void doSomethingWithName(NSString *name);
191
192void testConsistencyRetain(Person *p) {
193  clang_analyzer_eval(p.name == p.name); // expected-warning{{TRUE}}
194
195  id origName = p.name;
196  clang_analyzer_eval(p.name == origName); // expected-warning{{TRUE}}
197  doSomethingWithPerson(p);
198  clang_analyzer_eval(p.name == origName); // expected-warning{{UNKNOWN}}
199}
200
201void testConsistencyAssign(Person *p) {
202  clang_analyzer_eval(p.friend == p.friend); // expected-warning{{TRUE}}
203
204  id origFriend = p.friend;
205  clang_analyzer_eval(p.friend == origFriend); // expected-warning{{TRUE}}
206  doSomethingWithPerson(p);
207  clang_analyzer_eval(p.friend == origFriend); // expected-warning{{UNKNOWN}}
208}
209
210#if !__has_feature(objc_arc)
211void testOverrelease(Person *p, int coin) {
212  switch (coin) {
213  case 0:
214    [p.name release]; // expected-warning{{not owned}}
215    break;
216  case 1:
217    [p.friend release]; // expected-warning{{not owned}}
218    break;
219  case 2: {
220    id friend = p.friend;
221    doSomethingWithPerson(p);
222    [friend release]; // expected-warning{{not owned}}
223  }
224  }
225}
226
227// <rdar://problem/16333368>
228@implementation Person (Rdar16333368)
229
230- (void)testDeliberateRelease:(Person *)other {
231  doSomethingWithName(self.name);
232  [_name release]; // no-warning
233  self->_name = 0;
234
235  doSomethingWithName(other->_name);
236  [other.name release]; // no-warning
237}
238
239- (void)deliberateReleaseFalseNegative {
240  // This is arguably a false negative because the result of p.friend shouldn't
241  // be released, even though we are manipulating the ivar in between the two
242  // actions.
243  id name = self.name;
244  _name = 0;
245  [name release];
246}
247
248- (void)testRetainAndRelease {
249  [self.name retain];
250  [self.name release];
251  [self.name release]; // expected-warning{{not owned}}
252}
253
254- (void)testRetainAndReleaseIVar {
255  [self.name retain];
256  [_name release];
257  [_name release];
258}
259
260@end
261#endif
262
263@interface IntWrapper
264@property int value;
265@end
266
267@implementation IntWrapper
268@synthesize value;
269@end
270
271void testConsistencyInt(IntWrapper *w) {
272  clang_analyzer_eval(w.value == w.value); // expected-warning{{TRUE}}
273
274  int origValue = w.value;
275  if (origValue != 42)
276    return;
277
278  clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}}
279}
280
281void testConsistencyInt2(IntWrapper *w) {
282  if (w.value != 42)
283    return;
284
285  clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}}
286}
287
288
289@interface IntWrapperAuto
290@property int value;
291@end
292
293@implementation IntWrapperAuto
294@end
295
296void testConsistencyIntAuto(IntWrapperAuto *w) {
297  clang_analyzer_eval(w.value == w.value); // expected-warning{{TRUE}}
298
299  int origValue = w.value;
300  if (origValue != 42)
301    return;
302
303  clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}}
304}
305
306void testConsistencyIntAuto2(IntWrapperAuto *w) {
307  if (w.value != 42)
308    return;
309
310  clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}}
311}
312
313
314typedef struct {
315  int value;
316} IntWrapperStruct;
317
318@interface StructWrapper
319@property IntWrapperStruct inner;
320@end
321
322@implementation StructWrapper
323@synthesize inner;
324@end
325
326void testConsistencyStruct(StructWrapper *w) {
327  clang_analyzer_eval(w.inner.value == w.inner.value); // expected-warning{{TRUE}}
328
329  int origValue = w.inner.value;
330  if (origValue != 42)
331    return;
332
333  clang_analyzer_eval(w.inner.value == 42); // expected-warning{{TRUE}}
334}
335
336
337@interface OpaqueIntWrapper
338@property int value;
339@end
340
341// For now, don't assume a property is implemented using an ivar unless we can
342// actually see that it is.
343void testOpaqueConsistency(OpaqueIntWrapper *w) {
344  clang_analyzer_eval(w.value == w.value); // expected-warning{{UNKNOWN}}
345}
346
347
348#if !__has_feature(objc_arc)
349// Test quite a few cases of retain/release issues.
350
351@interface RetainCountTesting
352@property (strong) id ownedProp;
353@property (unsafe_unretained) id unownedProp;
354@property (nonatomic, strong) id manualProp;
355@end
356
357@implementation RetainCountTesting {
358  id _ivarOnly;
359}
360
361- (id)manualProp {
362  return _manualProp;
363}
364
365- (void)testOverreleaseOwnedIvar {
366  [_ownedProp retain];
367  [_ownedProp release];
368  [_ownedProp release];
369  [_ownedProp release]; // expected-warning{{used after it is released}}
370}
371
372- (void)testOverreleaseUnownedIvar {
373  [_unownedProp retain];
374  [_unownedProp release];
375  [_unownedProp release]; // expected-warning{{not owned at this point by the caller}}
376}
377
378- (void)testOverreleaseIvarOnly {
379  [_ivarOnly retain];
380  [_ivarOnly release];
381  [_ivarOnly release];
382  [_ivarOnly release]; // expected-warning{{used after it is released}}
383}
384
385- (void)testOverreleaseOwnedIvarUse {
386  [_ownedProp retain];
387  [_ownedProp release];
388  [_ownedProp release];
389  [_ownedProp myMethod]; // expected-warning{{used after it is released}}
390}
391
392- (void)testOverreleaseIvarOnlyUse {
393  [_ivarOnly retain];
394  [_ivarOnly release];
395  [_ivarOnly release];
396  [_ivarOnly myMethod]; // expected-warning{{used after it is released}}
397}
398
399- (void)testOverreleaseOwnedIvarAutoreleaseOkay {
400  [_ownedProp retain];
401  [_ownedProp release];
402  [_ownedProp autorelease];
403} // no-warning
404
405- (void)testOverreleaseIvarOnlyAutoreleaseOkay {
406  [_ivarOnly retain];
407  [_ivarOnly release];
408  [_ivarOnly autorelease];
409} // no-warning
410
411- (void)testOverreleaseOwnedIvarAutorelease {
412  [_ownedProp retain];
413  [_ownedProp release];
414  [_ownedProp autorelease];
415  [_ownedProp autorelease];
416} // expected-warning{{Object autoreleased too many times}}
417
418- (void)testOverreleaseIvarOnlyAutorelease {
419  [_ivarOnly retain];
420  [_ivarOnly release];
421  [_ivarOnly autorelease];
422  [_ivarOnly autorelease];
423} // expected-warning{{Object autoreleased too many times}}
424
425- (void)testPropertyAccessThenReleaseOwned {
426  id owned = [self.ownedProp retain];
427  [owned release];
428  [_ownedProp release];
429  clang_analyzer_eval(owned == _ownedProp); // expected-warning{{TRUE}}
430}
431
432- (void)testPropertyAccessThenReleaseOwned2 {
433  id fromIvar = _ownedProp;
434  id owned = [self.ownedProp retain];
435  [owned release];
436  [fromIvar release];
437  clang_analyzer_eval(owned == fromIvar); // expected-warning{{TRUE}}
438}
439
440- (void)testPropertyAccessThenReleaseUnowned {
441  id unowned = [self.unownedProp retain];
442  [unowned release];
443  [_unownedProp release]; // expected-warning{{not owned}}
444}
445
446- (void)testPropertyAccessThenReleaseUnowned2 {
447  id fromIvar = _unownedProp;
448  id unowned = [self.unownedProp retain];
449  [unowned release];
450  clang_analyzer_eval(unowned == fromIvar); // expected-warning{{TRUE}}
451  [fromIvar release]; // expected-warning{{not owned}}
452}
453
454- (void)testPropertyAccessThenReleaseManual {
455  id prop = [self.manualProp retain];
456  [prop release];
457  [_manualProp release]; // no-warning
458}
459
460- (void)testPropertyAccessThenReleaseManual2 {
461  id fromIvar = _manualProp;
462  id prop = [self.manualProp retain];
463  [prop release];
464  clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}}
465  [fromIvar release]; // no-warning
466}
467
468- (id)getUnownedFromProperty {
469  [_ownedProp retain];
470  [_ownedProp autorelease];
471  return _ownedProp; // no-warning
472}
473
474- (id)transferUnownedFromProperty {
475  [_ownedProp retain];
476  [_ownedProp autorelease];
477  return [_ownedProp autorelease]; // no-warning
478}
479
480- (id)transferOwnedFromProperty __attribute__((ns_returns_retained)) {
481  [_ownedProp retain];
482  [_ownedProp autorelease];
483  return _ownedProp; // no-warning
484}
485
486- (void)testAssignOwned:(id)newValue {
487  _ownedProp = newValue;
488  [_ownedProp release]; // FIXME: no-warning{{not owned}}
489}
490
491- (void)testAssignUnowned:(id)newValue {
492  _unownedProp = newValue;
493  [_unownedProp release]; // FIXME: no-warning{{not owned}}
494}
495
496- (void)testAssignIvarOnly:(id)newValue {
497  _ivarOnly = newValue;
498  [_ivarOnly release]; // FIXME: no-warning{{not owned}}
499}
500
501- (void)testAssignOwnedOkay:(id)newValue {
502  _ownedProp = [newValue retain];
503  [_ownedProp release]; // no-warning
504}
505
506- (void)testAssignUnownedOkay:(id)newValue {
507  _unownedProp = [newValue retain];
508  [_unownedProp release]; // no-warning
509}
510
511- (void)testAssignIvarOnlyOkay:(id)newValue {
512  _ivarOnly = [newValue retain];
513  [_ivarOnly release]; // no-warning
514}
515
516// rdar://problem/19862648
517- (void)establishIvarIsNilDuringLoops {
518  extern id getRandomObject();
519
520  int i = 4; // Must be at least 4 to trigger the bug.
521  while (--i) {
522    id x = 0;
523    if (getRandomObject())
524      x = _ivarOnly;
525    if (!x)
526      x = getRandomObject();
527    [x myMethod];
528  }
529}
530
531@end
532#endif // non-ARC
533
534