InlineObjCClassMethod.m revision 9b072b31ee2f41b8e30d1d22142c9ab72ac5ff1f
1// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify %s
2
3void clang_analyzer_checkInlined(int);
4
5// Test inlining of ObjC class methods.
6
7typedef signed char BOOL;
8typedef struct objc_class *Class;
9typedef struct objc_object {
10    Class isa;
11} *id;
12@protocol NSObject  - (BOOL)isEqual:(id)object; @end
13@interface NSObject <NSObject> {}
14+(id)alloc;
15-(id)init;
16-(id)autorelease;
17-(id)copy;
18- (Class)class;
19-(id)retain;
20@end
21
22// Vanila: ObjC class method is called by name.
23@interface MyParent : NSObject
24+ (int)getInt;
25@end
26@interface MyClass : MyParent
27+ (int)getInt;
28@end
29@implementation MyClass
30+ (int)testClassMethodByName {
31    int y = [MyClass getInt];
32    return 5/y; // expected-warning {{Division by zero}}
33}
34+ (int)getInt {
35  return 0;
36}
37@end
38
39// The definition is defined by the parent. Make sure we find it and inline.
40@interface MyParentDIP : NSObject
41+ (int)getInt;
42@end
43@interface MyClassDIP : MyParentDIP
44@end
45@implementation MyClassDIP
46+ (int)testClassMethodByName {
47    int y = [MyClassDIP getInt];
48    return 5/y; // expected-warning {{Division by zero}}
49}
50@end
51@implementation MyParentDIP
52+ (int)getInt {
53    return 0;
54}
55@end
56
57// ObjC class method is called by name. Definition is in the category.
58@interface AAA : NSObject
59@end
60@interface AAA (MyCat)
61+ (int)getInt;
62@end
63int foo() {
64    int y = [AAA getInt];
65    return 5/y; // expected-warning {{Division by zero}}
66}
67@implementation AAA
68@end
69@implementation AAA (MyCat)
70+ (int)getInt {
71    return 0;
72}
73@end
74
75// ObjC class method is called by name. Definition is in the parent category.
76@interface PPP : NSObject
77@end
78@interface PPP (MyCat)
79+ (int)getInt;
80@end
81@interface CCC : PPP
82@end
83int foo4() {
84    int y = [CCC getInt];
85    return 5/y; // expected-warning {{Division by zero}}
86}
87@implementation PPP
88@end
89@implementation PPP (MyCat)
90+ (int)getInt {
91    return 0;
92}
93@end
94
95// There is no declaration in the class but there is one in the parent. Make 
96// sure we pick the definition from the class and not the parent.
97@interface MyParentTricky : NSObject
98+ (int)getInt;
99@end
100@interface MyClassTricky : MyParentTricky
101@end
102@implementation MyParentTricky
103+ (int)getInt {
104    return 0;
105}
106@end
107@implementation MyClassTricky
108+ (int)getInt {
109  return 1;
110}
111+ (int)testClassMethodByName {
112    int y = [MyClassTricky getInt];
113    return 5/y; // no-warning
114}
115@end
116
117// ObjC class method is called by unknown class declaration (passed in as a 
118// parameter). We should not inline in such case.
119@interface MyParentUnknown : NSObject
120+ (int)getInt;
121@end
122@interface MyClassUnknown : MyParentUnknown
123+ (int)getInt;
124@end
125@implementation MyClassUnknown
126+ (int)testClassVariableByUnknownVarDecl: (Class)cl  {
127  int y = [cl getInt];
128  return 3/y; // no-warning
129}
130+ (int)getInt {
131  return 0;
132}
133@end
134
135
136// False negative.
137// ObjC class method call through a decl with a known type.
138// We should be able to track the type of currentClass and inline this call.
139// Note, [self class] could be a subclass. Do we still want to inline here?
140@interface MyClassKT : NSObject
141@end
142@interface MyClassKT (MyCatKT)
143+ (int)getInt;
144@end
145@implementation MyClassKT (MyCatKT)
146+ (int)getInt {
147    return 0;
148}
149@end
150@implementation MyClassKT
151- (int)testClassMethodByKnownVarDecl {
152  Class currentClass = [self class];
153  int y = [currentClass getInt];
154  return 5/y; // Would be great to get a warning here.
155}
156@end
157
158// Another false negative due to us not reasoning about self, which in this 
159// case points to the object of the class in the call site and should be equal 
160// to [MyParent class].
161@interface MyParentSelf : NSObject
162+ (int)testSelf;
163@end
164@implementation MyParentSelf
165+ (int)testSelf {
166  if (self == [MyParentSelf class])
167      return 0;
168    else
169      return 1;
170}
171@end
172@interface MyClassSelf : MyParentSelf
173@end
174@implementation MyClassSelf
175+ (int)testClassMethodByKnownVarDecl {
176  int y = [MyParentSelf testSelf];
177  return 5/y; // Should warn here.
178}
179@end
180int foo2() {
181  int y = [MyParentSelf testSelf];
182  return 5/y; // Should warn here.
183}
184
185// TODO: We do not inline 'getNum' in the following case, where the value of 
186// 'self' in call '[self getNum]' is available and evaualtes to 
187// 'SelfUsedInParentChild' if it's called from fooA.
188// Self region should get created before we call foo and yje call to super 
189// should keep it live. 
190@interface SelfUsedInParent : NSObject
191+ (int)getNum;
192+ (int)foo;
193@end
194@implementation SelfUsedInParent
195+ (int)getNum {return 5;}
196+ (int)foo {
197  return [self getNum];
198}
199@end
200@interface SelfUsedInParentChild : SelfUsedInParent
201+ (int)getNum;
202+ (int)fooA;
203@end
204@implementation SelfUsedInParentChild
205+ (int)getNum {return 0;}
206+ (int)fooA {
207  return [super foo];
208}
209@end
210int checkSelfUsedInparentClassMethod() {
211    return 5/[SelfUsedInParentChild fooA];
212}
213
214
215@interface Rdar15037033 : NSObject
216@end
217
218void rdar15037033() {
219  [Rdar15037033 forwardDeclaredMethod]; // expected-warning {{class method '+forwardDeclaredMethod' not found}}
220  [Rdar15037033 forwardDeclaredVariadicMethod:1, 2, 3, 0]; // expected-warning {{class method '+forwardDeclaredVariadicMethod:' not found}}
221}
222
223@implementation Rdar15037033
224
225+ (void)forwardDeclaredMethod {
226  clang_analyzer_checkInlined(1); // expected-warning{{TRUE}}
227}
228
229+ (void)forwardDeclaredVariadicMethod:(int)x, ... {
230  clang_analyzer_checkInlined(0); // no-warning
231}
232
233@end
234
235
236
237