1 // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify %s 2 3 void clang_analyzer_checkInlined(int); 4 5 // Test inlining of ObjC class methods. 6 7 typedef signed char BOOL; 8 typedef struct objc_class *Class; 9 typedef 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 63 int 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 83 int 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 180 int 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 210 int checkSelfUsedInparentClassMethod() { 211 return 5/[SelfUsedInParentChild fooA]; 212 } 213 214 215 @interface Rdar15037033 : NSObject 216 @end 217 218 void 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