403 lines · plain
1// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify -analyzer-config eagerly-assume=false %s2 3void clang_analyzer_checkInlined(int);4void clang_analyzer_eval(int);5 6// Test inlining of ObjC class methods.7 8typedef signed char BOOL;9#define YES ((BOOL)1)10#define NO ((BOOL)0)11typedef struct objc_class *Class;12typedef struct objc_object {13 Class isa;14} * id;15@protocol NSObject16- (BOOL)isEqual:(id)object;17@end18@interface NSObject <NSObject> {}19+ (id)alloc;20+ (Class)class;21+ (Class)superclass;22- (id)init;23- (id)autorelease;24- (id)copy;25- (Class)class;26- (instancetype)self;27- (id)retain;28@end29 30// Vanila: ObjC class method is called by name.31@interface MyParent : NSObject32+ (int)getInt;33@end34@interface MyClass : MyParent35+ (int)getInt;36@end37@implementation MyClass38+ (int)testClassMethodByName {39 int y = [MyClass getInt];40 return 5/y; // expected-warning {{Division by zero}}41}42+ (int)getInt {43 return 0;44}45@end46 47// The definition is defined by the parent. Make sure we find it and inline.48@interface MyParentDIP : NSObject49+ (int)getInt;50@end51@interface MyClassDIP : MyParentDIP52@end53@implementation MyClassDIP54+ (int)testClassMethodByName {55 int y = [MyClassDIP getInt];56 return 5/y; // expected-warning {{Division by zero}}57}58@end59@implementation MyParentDIP60+ (int)getInt {61 return 0;62}63@end64 65// ObjC class method is called by name. Definition is in the category.66@interface AAA : NSObject67@end68@interface AAA (MyCat)69+ (int)getInt;70@end71int foo(void) {72 int y = [AAA getInt];73 return 5/y; // expected-warning {{Division by zero}}74}75@implementation AAA76@end77@implementation AAA (MyCat)78+ (int)getInt {79 return 0;80}81@end82 83// ObjC class method is called by name. Definition is in the parent category.84@interface PPP : NSObject85@end86@interface PPP (MyCat)87+ (int)getInt;88@end89@interface CCC : PPP90@end91int foo4(void) {92 int y = [CCC getInt];93 return 5/y; // expected-warning {{Division by zero}}94}95@implementation PPP96@end97@implementation PPP (MyCat)98+ (int)getInt {99 return 0;100}101@end102 103// There is no declaration in the class but there is one in the parent. Make 104// sure we pick the definition from the class and not the parent.105@interface MyParentTricky : NSObject106+ (int)getInt;107@end108@interface MyClassTricky : MyParentTricky109@end110@implementation MyParentTricky111+ (int)getInt {112 return 0;113}114@end115@implementation MyClassTricky116+ (int)getInt {117 return 1;118}119+ (int)testClassMethodByName {120 int y = [MyClassTricky getInt];121 return 5/y; // no-warning122}123@end124 125// ObjC class method is called by unknown class declaration (passed in as a 126// parameter). We should not inline in such case.127@interface MyParentUnknown : NSObject128+ (int)getInt;129@end130@interface MyClassUnknown : MyParentUnknown131+ (int)getInt;132@end133@implementation MyClassUnknown134+ (int)testClassVariableByUnknownVarDecl: (Class)cl {135 int y = [cl getInt];136 return 3/y; // no-warning137}138+ (int)getInt {139 return 0;140}141@end142 143// ObjC class method call through a decl with a known type.144// Note, [self class] could be a subclass. Do we still want to inline here?145@interface MyClassKT : NSObject146@end147@interface MyClassKT (MyCatKT)148+ (int)getInt;149@end150@implementation MyClassKT (MyCatKT)151+ (int)getInt {152 return 0;153}154@end155@implementation MyClassKT156- (int)testClassMethodByKnownVarDecl {157 Class currentClass = [self class];158 int y = [currentClass getInt];159 return 5 / y; // expected-warning{{Division by zero}}160}161@end162 163// Another false negative due to us not reasoning about self, which in this 164// case points to the object of the class in the call site and should be equal 165// to [MyParent class].166@interface MyParentSelf : NSObject167+ (int)testSelf;168@end169@implementation MyParentSelf170+ (int)testSelf {171 if (self == [MyParentSelf class])172 return 0;173 else174 return 1;175}176@end177@interface MyClassSelf : MyParentSelf178@end179@implementation MyClassSelf180+ (int)testClassMethodByKnownVarDecl {181 int y = [MyParentSelf testSelf];182 return 5/y; // expected-warning{{Division by zero}}183}184@end185int foo2(void) {186 int y = [MyParentSelf testSelf];187 return 5/y; // expected-warning{{Division by zero}}188}189 190// TODO: We do not inline 'getNum' in the following case, where the value of 191// 'self' in call '[self getNum]' is available and evaualtes to 192// 'SelfUsedInParentChild' if it's called from fooA.193// Self region should get created before we call foo and yje call to super 194// should keep it live. 195@interface SelfUsedInParent : NSObject196+ (int)getNum;197+ (int)foo;198@end199@implementation SelfUsedInParent200+ (int)getNum {return 5;}201+ (int)foo {202 int r = [self getNum];203 clang_analyzer_eval(r == 5); // expected-warning{{TRUE}}204 return r;205}206@end207@interface SelfUsedInParentChild : SelfUsedInParent208+ (int)getNum;209+ (int)fooA;210@end211@implementation SelfUsedInParentChild212+ (int)getNum {return 0;}213+ (int)fooA {214 return [super foo];215}216@end217int checkSelfUsedInparentClassMethod(void) {218 return 5/[SelfUsedInParentChild fooA];219}220 221 222@interface Rdar15037033 : NSObject223@end224 225void rdar15037033(void) {226 [Rdar15037033 forwardDeclaredMethod]; // expected-warning {{class method '+forwardDeclaredMethod' not found}}227 [Rdar15037033 forwardDeclaredVariadicMethod:1, 2, 3, 0]; // expected-warning {{class method '+forwardDeclaredVariadicMethod:' not found}}228}229 230@implementation Rdar15037033231 232+ (void)forwardDeclaredMethod {233 clang_analyzer_checkInlined(1); // expected-warning{{TRUE}}234}235 236+ (void)forwardDeclaredVariadicMethod:(int)x, ... {237 clang_analyzer_checkInlined(0); // no-warning238}239@end240 241@interface SelfClassTestParent : NSObject242-(unsigned)returns10;243+(unsigned)returns20;244+(unsigned)returns30;245@end246 247@interface SelfClassTest : SelfClassTestParent248- (unsigned)returns10;249+ (unsigned)returns20;250+ (unsigned)returns30;251@end252 253@implementation SelfClassTestParent254- (unsigned)returns10 {255 return 100;256}257+ (unsigned)returns20 {258 return 100;259}260+ (unsigned)returns30 {261 return 100;262}263 264- (void)testSelfReassignment {265 // Check that we didn't hardcode type for self.266 self = [[[SelfClassTest class] alloc] init];267 Class actuallyChildClass = [self class];268 unsigned result = [actuallyChildClass returns30];269 clang_analyzer_eval(result == 30); // expected-warning{{TRUE}}270}271@end272 273@implementation SelfClassTest274- (unsigned)returns10 {275 return 10;276}277+ (unsigned)returns20 {278 return 20;279}280+ (unsigned)returns30 {281 return 30;282}283+ (BOOL)isClass {284 return YES;285}286- (BOOL)isClass {287 return NO;288}289+ (SelfClassTest *)create {290 return [[self alloc] init];291}292+ (void)classMethod {293 unsigned result1 = [self returns20];294 clang_analyzer_eval(result1 == 20); // expected-warning{{TRUE}}295 296 unsigned result2 = [[self class] returns30];297 clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}}298 299 unsigned result3 = [[super class] returns30];300 clang_analyzer_eval(result3 == 100); // expected-warning{{TRUE}}301 302 // Check that class info is propagated with data303 Class class41 = [self class];304 Class class42 = class41;305 unsigned result4 = [class42 returns30];306 clang_analyzer_eval(result4 == 30); // expected-warning{{TRUE}}307 308 Class class51 = [super class];309 Class class52 = class51;310 unsigned result5 = [class52 returns30];311 clang_analyzer_eval(result5 == 100); // expected-warning{{TRUE}}312}313- (void)instanceMethod {314 unsigned result0 = [self returns10];315 clang_analyzer_eval(result0 == 10); // expected-warning{{TRUE}}316 317 unsigned result2 = [[self class] returns30];318 clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}}319 320 unsigned result3 = [[super class] returns30];321 clang_analyzer_eval(result3 == 100); // expected-warning{{TRUE}}322 323 // Check that class info is propagated with data324 Class class41 = [self class];325 Class class42 = class41;326 unsigned result4 = [class42 returns30];327 clang_analyzer_eval(result4 == 30); // expected-warning{{TRUE}}328 329 Class class51 = [super class];330 Class class52 = class51;331 unsigned result5 = [class52 returns30];332 clang_analyzer_eval(result5 == 100); // expected-warning{{TRUE}}333 334 // Check that we inline class methods when class object is a receiver335 Class class6 = [self class];336 BOOL calledClassMethod = [class6 isClass];337 clang_analyzer_eval(calledClassMethod == YES); // expected-warning{{TRUE}}338 339 // Check that class info is propagated through the 'self' method340 Class class71 = [self class];341 Class class72 = [class71 self];342 unsigned result7 = [class72 returns30];343 clang_analyzer_eval(result7 == 30); // expected-warning{{TRUE}}344 345 // Check that 'class' and 'super' info from direct invocation of the346 // corresponding class methods is propagated with data347 Class class8 = [SelfClassTest class];348 unsigned result8 = [class8 returns30];349 clang_analyzer_eval(result8 == 30); // expected-warning{{TRUE}}350 351 Class class9 = [SelfClassTest superclass];352 unsigned result9 = [class9 returns30];353 clang_analyzer_eval(result9 == 100); // expected-warning{{TRUE}}354 355 // Check that we get class from a propagated type356 SelfClassTestParent *selfAsParent10 = [[SelfClassTest alloc] init];357 Class class10 = [selfAsParent10 class];358 unsigned result10 = [class10 returns30];359 clang_analyzer_eval(result10 == 30); // expected-warning{{TRUE}}360 361 SelfClassTestParent *selfAsParent11 = [[[self class] alloc] init];362 Class class11 = [selfAsParent11 class];363 unsigned result11 = [class11 returns30];364 clang_analyzer_eval(result11 == 30); // expected-warning{{TRUE}}365}366@end367 368@interface Parent : NSObject369+ (int)a;370+ (int)b;371@end372@interface Child : Parent373@end374@interface Other : NSObject375+(void)run;376@end377int main(int argc, const char * argv[]) {378 @autoreleasepool {379 [Other run];380 }381 return 0;382}383@implementation Other384+(void)run {385 int result = [Child a];386 // TODO: This should return 100.387 clang_analyzer_eval(result == 12); // expected-warning{{TRUE}}388}389@end390@implementation Parent391+ (int)a; {392 return [self b];393}394+ (int)b; {395 return 12;396}397@end398@implementation Child399+ (int)b; {400 return 100;401}402@end403