1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify -analyzer-config eagerly-assume=false %s |
2 | |
3 | void clang_analyzer_checkInlined(int); |
4 | void clang_analyzer_eval(int); |
5 | |
6 | // Test inlining of ObjC class methods. |
7 | |
8 | typedef signed char BOOL; |
9 | typedef struct objc_class *Class; |
10 | typedef struct objc_object { |
11 | Class isa; |
12 | } *id; |
13 | @protocol NSObject - (BOOL)isEqual:(id)object; @end |
14 | @interface NSObject <NSObject> {} |
15 | +(id)alloc; |
16 | -(id)init; |
17 | -(id)autorelease; |
18 | -(id)copy; |
19 | - (Class)class; |
20 | -(id)retain; |
21 | @end |
22 | |
23 | // Vanila: ObjC class method is called by name. |
24 | @interface MyParent : NSObject |
25 | + (int)getInt; |
26 | @end |
27 | @interface MyClass : MyParent |
28 | + (int)getInt; |
29 | @end |
30 | @implementation MyClass |
31 | + (int)testClassMethodByName { |
32 | int y = [MyClass getInt]; |
33 | return 5/y; // expected-warning {{Division by zero}} |
34 | } |
35 | + (int)getInt { |
36 | return 0; |
37 | } |
38 | @end |
39 | |
40 | // The definition is defined by the parent. Make sure we find it and inline. |
41 | @interface MyParentDIP : NSObject |
42 | + (int)getInt; |
43 | @end |
44 | @interface MyClassDIP : MyParentDIP |
45 | @end |
46 | @implementation MyClassDIP |
47 | + (int)testClassMethodByName { |
48 | int y = [MyClassDIP getInt]; |
49 | return 5/y; // expected-warning {{Division by zero}} |
50 | } |
51 | @end |
52 | @implementation MyParentDIP |
53 | + (int)getInt { |
54 | return 0; |
55 | } |
56 | @end |
57 | |
58 | // ObjC class method is called by name. Definition is in the category. |
59 | @interface AAA : NSObject |
60 | @end |
61 | @interface AAA (MyCat) |
62 | + (int)getInt; |
63 | @end |
64 | int foo() { |
65 | int y = [AAA getInt]; |
66 | return 5/y; // expected-warning {{Division by zero}} |
67 | } |
68 | @implementation AAA |
69 | @end |
70 | @implementation AAA (MyCat) |
71 | + (int)getInt { |
72 | return 0; |
73 | } |
74 | @end |
75 | |
76 | // ObjC class method is called by name. Definition is in the parent category. |
77 | @interface PPP : NSObject |
78 | @end |
79 | @interface PPP (MyCat) |
80 | + (int)getInt; |
81 | @end |
82 | @interface CCC : PPP |
83 | @end |
84 | int foo4() { |
85 | int y = [CCC getInt]; |
86 | return 5/y; // expected-warning {{Division by zero}} |
87 | } |
88 | @implementation PPP |
89 | @end |
90 | @implementation PPP (MyCat) |
91 | + (int)getInt { |
92 | return 0; |
93 | } |
94 | @end |
95 | |
96 | // There is no declaration in the class but there is one in the parent. Make |
97 | // sure we pick the definition from the class and not the parent. |
98 | @interface MyParentTricky : NSObject |
99 | + (int)getInt; |
100 | @end |
101 | @interface MyClassTricky : MyParentTricky |
102 | @end |
103 | @implementation MyParentTricky |
104 | + (int)getInt { |
105 | return 0; |
106 | } |
107 | @end |
108 | @implementation MyClassTricky |
109 | + (int)getInt { |
110 | return 1; |
111 | } |
112 | + (int)testClassMethodByName { |
113 | int y = [MyClassTricky getInt]; |
114 | return 5/y; // no-warning |
115 | } |
116 | @end |
117 | |
118 | // ObjC class method is called by unknown class declaration (passed in as a |
119 | // parameter). We should not inline in such case. |
120 | @interface MyParentUnknown : NSObject |
121 | + (int)getInt; |
122 | @end |
123 | @interface MyClassUnknown : MyParentUnknown |
124 | + (int)getInt; |
125 | @end |
126 | @implementation MyClassUnknown |
127 | + (int)testClassVariableByUnknownVarDecl: (Class)cl { |
128 | int y = [cl getInt]; |
129 | return 3/y; // no-warning |
130 | } |
131 | + (int)getInt { |
132 | return 0; |
133 | } |
134 | @end |
135 | |
136 | |
137 | // False negative. |
138 | // ObjC class method call through a decl with a known type. |
139 | // We should be able to track the type of currentClass and inline this call. |
140 | // Note, [self class] could be a subclass. Do we still want to inline here? |
141 | @interface MyClassKT : NSObject |
142 | @end |
143 | @interface MyClassKT (MyCatKT) |
144 | + (int)getInt; |
145 | @end |
146 | @implementation MyClassKT (MyCatKT) |
147 | + (int)getInt { |
148 | return 0; |
149 | } |
150 | @end |
151 | @implementation MyClassKT |
152 | - (int)testClassMethodByKnownVarDecl { |
153 | Class currentClass = [self class]; |
154 | int y = [currentClass getInt]; |
155 | return 5/y; // Would be great to get a warning here. |
156 | } |
157 | @end |
158 | |
159 | // Another false negative due to us not reasoning about self, which in this |
160 | // case points to the object of the class in the call site and should be equal |
161 | // to [MyParent class]. |
162 | @interface MyParentSelf : NSObject |
163 | + (int)testSelf; |
164 | @end |
165 | @implementation MyParentSelf |
166 | + (int)testSelf { |
167 | if (self == [MyParentSelf class]) |
168 | return 0; |
169 | else |
170 | return 1; |
171 | } |
172 | @end |
173 | @interface MyClassSelf : MyParentSelf |
174 | @end |
175 | @implementation MyClassSelf |
176 | + (int)testClassMethodByKnownVarDecl { |
177 | int y = [MyParentSelf testSelf]; |
178 | return 5/y; // expected-warning{{Division by zero}} |
179 | } |
180 | @end |
181 | int foo2() { |
182 | int y = [MyParentSelf testSelf]; |
183 | return 5/y; // expected-warning{{Division by zero}} |
184 | } |
185 | |
186 | // TODO: We do not inline 'getNum' in the following case, where the value of |
187 | // 'self' in call '[self getNum]' is available and evaualtes to |
188 | // 'SelfUsedInParentChild' if it's called from fooA. |
189 | // Self region should get created before we call foo and yje call to super |
190 | // should keep it live. |
191 | @interface SelfUsedInParent : NSObject |
192 | + (int)getNum; |
193 | + (int)foo; |
194 | @end |
195 | @implementation SelfUsedInParent |
196 | + (int)getNum {return 5;} |
197 | + (int)foo { |
198 | int r = [self getNum]; |
199 | clang_analyzer_eval(r == 5); // expected-warning{{TRUE}} |
200 | return r; |
201 | } |
202 | @end |
203 | @interface SelfUsedInParentChild : SelfUsedInParent |
204 | + (int)getNum; |
205 | + (int)fooA; |
206 | @end |
207 | @implementation SelfUsedInParentChild |
208 | + (int)getNum {return 0;} |
209 | + (int)fooA { |
210 | return [super foo]; |
211 | } |
212 | @end |
213 | int checkSelfUsedInparentClassMethod() { |
214 | return 5/[SelfUsedInParentChild fooA]; |
215 | } |
216 | |
217 | |
218 | @interface Rdar15037033 : NSObject |
219 | @end |
220 | |
221 | void rdar15037033() { |
222 | [Rdar15037033 forwardDeclaredMethod]; // expected-warning {{class method '+forwardDeclaredMethod' not found}} |
223 | [Rdar15037033 forwardDeclaredVariadicMethod:1, 2, 3, 0]; // expected-warning {{class method '+forwardDeclaredVariadicMethod:' not found}} |
224 | } |
225 | |
226 | @implementation Rdar15037033 |
227 | |
228 | + (void)forwardDeclaredMethod { |
229 | clang_analyzer_checkInlined(1); // expected-warning{{TRUE}} |
230 | } |
231 | |
232 | + (void)forwardDeclaredVariadicMethod:(int)x, ... { |
233 | clang_analyzer_checkInlined(0); // no-warning |
234 | } |
235 | @end |
236 | |
237 | @interface SelfClassTestParent : NSObject |
238 | -(unsigned)returns10; |
239 | +(unsigned)returns20; |
240 | +(unsigned)returns30; |
241 | @end |
242 | |
243 | @implementation SelfClassTestParent |
244 | -(unsigned)returns10 { return 100; } |
245 | +(unsigned)returns20 { return 100; } |
246 | +(unsigned)returns30 { return 100; } |
247 | @end |
248 | |
249 | @interface SelfClassTest : SelfClassTestParent |
250 | -(unsigned)returns10; |
251 | +(unsigned)returns20; |
252 | +(unsigned)returns30; |
253 | @end |
254 | |
255 | @implementation SelfClassTest |
256 | -(unsigned)returns10 { return 10; } |
257 | +(unsigned)returns20 { return 20; } |
258 | +(unsigned)returns30 { return 30; } |
259 | +(void)classMethod { |
260 | unsigned result1 = [self returns20]; |
261 | clang_analyzer_eval(result1 == 20); // expected-warning{{TRUE}} |
262 | unsigned result2 = [[self class] returns30]; |
263 | clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}} |
264 | unsigned result3 = [[super class] returns30]; |
265 | clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}} |
266 | } |
267 | -(void)instanceMethod { |
268 | unsigned result0 = [self returns10]; |
269 | clang_analyzer_eval(result0 == 10); // expected-warning{{TRUE}} |
270 | unsigned result2 = [[self class] returns30]; |
271 | clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}} |
272 | unsigned result3 = [[super class] returns30]; |
273 | clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}} |
274 | } |
275 | @end |
276 | |
277 | @interface Parent : NSObject |
278 | + (int)a; |
279 | + (int)b; |
280 | @end |
281 | @interface Child : Parent |
282 | @end |
283 | @interface Other : NSObject |
284 | +(void)run; |
285 | @end |
286 | int main(int argc, const char * argv[]) { |
287 | @autoreleasepool { |
288 | [Other run]; |
289 | } |
290 | return 0; |
291 | } |
292 | @implementation Other |
293 | +(void)run { |
294 | int result = [Child a]; |
295 | // TODO: This should return 100. |
296 | clang_analyzer_eval(result == 12); // expected-warning{{TRUE}} |
297 | } |
298 | @end |
299 | @implementation Parent |
300 | + (int)a; { |
301 | return [self b]; |
302 | } |
303 | + (int)b; { |
304 | return 12; |
305 | } |
306 | @end |
307 | @implementation Child |
308 | + (int)b; { |
309 | return 100; |
310 | } |
311 | @end |
312 | |