1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-config ipa=dynamic-bifurcate -verify %s |
2 | |
3 | typedef signed char BOOL; |
4 | typedef struct objc_class *Class; |
5 | typedef struct objc_object { |
6 | Class isa; |
7 | } *id; |
8 | @protocol NSObject - (BOOL)isEqual:(id)object; @end |
9 | @interface NSObject <NSObject> {} |
10 | +(id)alloc; |
11 | +(id)new; |
12 | - (oneway void)release; |
13 | -(id)init; |
14 | -(id)autorelease; |
15 | -(id)copy; |
16 | - (Class)class; |
17 | -(id)retain; |
18 | - (oneway void)release; |
19 | @end |
20 | |
21 | @interface SelfStaysLive : NSObject |
22 | - (id)init; |
23 | @end |
24 | |
25 | @implementation SelfStaysLive |
26 | - (id)init { |
27 | return [super init]; |
28 | } |
29 | @end |
30 | |
31 | void selfStaysLive() { |
32 | SelfStaysLive *foo = [[SelfStaysLive alloc] init]; |
33 | [foo release]; |
34 | } |
35 | |
36 | // Test that retain release checker warns on leaks and use-after-frees when |
37 | // self init is not enabled. |
38 | // radar://12115830 |
39 | @interface ParentOfCell : NSObject |
40 | - (id)initWithInt: (int)inInt; |
41 | @end |
42 | @interface Cell : ParentOfCell{ |
43 | int x; |
44 | } |
45 | - (id)initWithInt: (int)inInt; |
46 | + (void)testOverRelease; |
47 | + (void)testLeak; |
48 | @property int x; |
49 | @end |
50 | @implementation Cell |
51 | @synthesize x; |
52 | - (id) initWithInt: (int)inInt { |
53 | [super initWithInt: inInt]; |
54 | self.x = inInt; // no-warning |
55 | return self; // Self Init checker would produce a warning here. |
56 | } |
57 | + (void) testOverRelease { |
58 | Cell *sharedCell3 = [[Cell alloc] initWithInt: 3]; |
59 | [sharedCell3 release]; |
60 | [sharedCell3 release]; // expected-warning {{Reference-counted object is used after it is released}} |
61 | } |
62 | + (void) testLeak { |
63 | Cell *sharedCell4 = [[Cell alloc] initWithInt: 3]; // expected-warning {{leak}} |
64 | } |
65 | @end |
66 | |
67 | // We should stop tracking some objects even when we inline the call. |
68 | // Specialically, the objects passed into calls with delegate and callback |
69 | // parameters. |
70 | @class DelegateTest; |
71 | typedef void (*ReleaseCallbackTy) (DelegateTest *c); |
72 | |
73 | @interface Delegate : NSObject |
74 | @end |
75 | |
76 | @interface DelegateTest : NSObject { |
77 | Delegate *myDel; |
78 | } |
79 | // Object initialized with a delagate which could potentially release it. |
80 | - (id)initWithDelegate: (id) d; |
81 | |
82 | - (void) setDelegate: (id) d; |
83 | |
84 | // Releases object through callback. |
85 | + (void)updateObject:(DelegateTest*)obj WithCallback:(ReleaseCallbackTy)rc; |
86 | |
87 | + (void)test: (Delegate *)d; |
88 | |
89 | @property (assign) Delegate* myDel; |
90 | @end |
91 | |
92 | void releaseObj(DelegateTest *c); |
93 | |
94 | // Releases object through callback. |
95 | void updateObject(DelegateTest *c, ReleaseCallbackTy rel) { |
96 | rel(c); |
97 | } |
98 | |
99 | @implementation DelegateTest |
100 | @synthesize myDel; |
101 | |
102 | - (id) initWithDelegate: (id) d { |
103 | if ((self = [super init])) |
104 | myDel = d; |
105 | return self; |
106 | } |
107 | |
108 | - (void) setDelegate: (id) d { |
109 | myDel = d; |
110 | } |
111 | |
112 | + (void)updateObject:(DelegateTest*)obj WithCallback:(ReleaseCallbackTy)rc { |
113 | rc(obj); |
114 | } |
115 | |
116 | + (void) test: (Delegate *)d { |
117 | DelegateTest *obj1 = [[DelegateTest alloc] initWithDelegate: d]; // no-warning |
118 | DelegateTest *obj2 = [[DelegateTest alloc] init]; // no-warning |
119 | DelegateTest *obj3 = [[DelegateTest alloc] init]; // no-warning |
120 | updateObject(obj2, releaseObj); |
121 | [DelegateTest updateObject: obj3 |
122 | WithCallback: releaseObj]; |
123 | DelegateTest *obj4 = [[DelegateTest alloc] init]; // no-warning |
124 | [obj4 setDelegate: d]; |
125 | } |
126 | @end |
127 | |
128 | |