// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify -analyzer-config eagerly-assume=false %s void clang_analyzer_checkInlined(int); void clang_analyzer_eval(int); // Test inlining of ObjC class methods. typedef signed char BOOL; #define YES ((BOOL)1) #define NO ((BOOL)0) typedef struct objc_class *Class; typedef struct objc_object { Class isa; } * id; @protocol NSObject - (BOOL)isEqual:(id)object; @end @interface NSObject {} + (id)alloc; + (Class)class; + (Class)superclass; - (id)init; - (id)autorelease; - (id)copy; - (Class)class; - (instancetype)self; - (id)retain; @end // Vanila: ObjC class method is called by name. @interface MyParent : NSObject + (int)getInt; @end @interface MyClass : MyParent + (int)getInt; @end @implementation MyClass + (int)testClassMethodByName { int y = [MyClass getInt]; return 5/y; // expected-warning {{Division by zero}} } + (int)getInt { return 0; } @end // The definition is defined by the parent. Make sure we find it and inline. @interface MyParentDIP : NSObject + (int)getInt; @end @interface MyClassDIP : MyParentDIP @end @implementation MyClassDIP + (int)testClassMethodByName { int y = [MyClassDIP getInt]; return 5/y; // expected-warning {{Division by zero}} } @end @implementation MyParentDIP + (int)getInt { return 0; } @end // ObjC class method is called by name. Definition is in the category. @interface AAA : NSObject @end @interface AAA (MyCat) + (int)getInt; @end int foo() { int y = [AAA getInt]; return 5/y; // expected-warning {{Division by zero}} } @implementation AAA @end @implementation AAA (MyCat) + (int)getInt { return 0; } @end // ObjC class method is called by name. Definition is in the parent category. @interface PPP : NSObject @end @interface PPP (MyCat) + (int)getInt; @end @interface CCC : PPP @end int foo4() { int y = [CCC getInt]; return 5/y; // expected-warning {{Division by zero}} } @implementation PPP @end @implementation PPP (MyCat) + (int)getInt { return 0; } @end // There is no declaration in the class but there is one in the parent. Make // sure we pick the definition from the class and not the parent. @interface MyParentTricky : NSObject + (int)getInt; @end @interface MyClassTricky : MyParentTricky @end @implementation MyParentTricky + (int)getInt { return 0; } @end @implementation MyClassTricky + (int)getInt { return 1; } + (int)testClassMethodByName { int y = [MyClassTricky getInt]; return 5/y; // no-warning } @end // ObjC class method is called by unknown class declaration (passed in as a // parameter). We should not inline in such case. @interface MyParentUnknown : NSObject + (int)getInt; @end @interface MyClassUnknown : MyParentUnknown + (int)getInt; @end @implementation MyClassUnknown + (int)testClassVariableByUnknownVarDecl: (Class)cl { int y = [cl getInt]; return 3/y; // no-warning } + (int)getInt { return 0; } @end // ObjC class method call through a decl with a known type. // Note, [self class] could be a subclass. Do we still want to inline here? @interface MyClassKT : NSObject @end @interface MyClassKT (MyCatKT) + (int)getInt; @end @implementation MyClassKT (MyCatKT) + (int)getInt { return 0; } @end @implementation MyClassKT - (int)testClassMethodByKnownVarDecl { Class currentClass = [self class]; int y = [currentClass getInt]; return 5 / y; // expected-warning{{Division by zero}} } @end // Another false negative due to us not reasoning about self, which in this // case points to the object of the class in the call site and should be equal // to [MyParent class]. @interface MyParentSelf : NSObject + (int)testSelf; @end @implementation MyParentSelf + (int)testSelf { if (self == [MyParentSelf class]) return 0; else return 1; } @end @interface MyClassSelf : MyParentSelf @end @implementation MyClassSelf + (int)testClassMethodByKnownVarDecl { int y = [MyParentSelf testSelf]; return 5/y; // expected-warning{{Division by zero}} } @end int foo2() { int y = [MyParentSelf testSelf]; return 5/y; // expected-warning{{Division by zero}} } // TODO: We do not inline 'getNum' in the following case, where the value of // 'self' in call '[self getNum]' is available and evaualtes to // 'SelfUsedInParentChild' if it's called from fooA. // Self region should get created before we call foo and yje call to super // should keep it live. @interface SelfUsedInParent : NSObject + (int)getNum; + (int)foo; @end @implementation SelfUsedInParent + (int)getNum {return 5;} + (int)foo { int r = [self getNum]; clang_analyzer_eval(r == 5); // expected-warning{{TRUE}} return r; } @end @interface SelfUsedInParentChild : SelfUsedInParent + (int)getNum; + (int)fooA; @end @implementation SelfUsedInParentChild + (int)getNum {return 0;} + (int)fooA { return [super foo]; } @end int checkSelfUsedInparentClassMethod() { return 5/[SelfUsedInParentChild fooA]; } @interface Rdar15037033 : NSObject @end void rdar15037033() { [Rdar15037033 forwardDeclaredMethod]; // expected-warning {{class method '+forwardDeclaredMethod' not found}} [Rdar15037033 forwardDeclaredVariadicMethod:1, 2, 3, 0]; // expected-warning {{class method '+forwardDeclaredVariadicMethod:' not found}} } @implementation Rdar15037033 + (void)forwardDeclaredMethod { clang_analyzer_checkInlined(1); // expected-warning{{TRUE}} } + (void)forwardDeclaredVariadicMethod:(int)x, ... { clang_analyzer_checkInlined(0); // no-warning } @end @interface SelfClassTestParent : NSObject -(unsigned)returns10; +(unsigned)returns20; +(unsigned)returns30; @end @interface SelfClassTest : SelfClassTestParent - (unsigned)returns10; + (unsigned)returns20; + (unsigned)returns30; @end @implementation SelfClassTestParent - (unsigned)returns10 { return 100; } + (unsigned)returns20 { return 100; } + (unsigned)returns30 { return 100; } - (void)testSelfReassignment { // Check that we didn't hardcode type for self. self = [[[SelfClassTest class] alloc] init]; Class actuallyChildClass = [self class]; unsigned result = [actuallyChildClass returns30]; clang_analyzer_eval(result == 30); // expected-warning{{TRUE}} } @end @implementation SelfClassTest - (unsigned)returns10 { return 10; } + (unsigned)returns20 { return 20; } + (unsigned)returns30 { return 30; } + (BOOL)isClass { return YES; } - (BOOL)isClass { return NO; } + (SelfClassTest *)create { return [[self alloc] init]; } + (void)classMethod { unsigned result1 = [self returns20]; clang_analyzer_eval(result1 == 20); // expected-warning{{TRUE}} unsigned result2 = [[self class] returns30]; clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}} unsigned result3 = [[super class] returns30]; clang_analyzer_eval(result3 == 100); // expected-warning{{TRUE}} // Check that class info is propagated with data Class class41 = [self class]; Class class42 = class41; unsigned result4 = [class42 returns30]; clang_analyzer_eval(result4 == 30); // expected-warning{{TRUE}} Class class51 = [super class]; Class class52 = class51; unsigned result5 = [class52 returns30]; clang_analyzer_eval(result5 == 100); // expected-warning{{TRUE}} } - (void)instanceMethod { unsigned result0 = [self returns10]; clang_analyzer_eval(result0 == 10); // expected-warning{{TRUE}} unsigned result2 = [[self class] returns30]; clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}} unsigned result3 = [[super class] returns30]; clang_analyzer_eval(result3 == 100); // expected-warning{{TRUE}} // Check that class info is propagated with data Class class41 = [self class]; Class class42 = class41; unsigned result4 = [class42 returns30]; clang_analyzer_eval(result4 == 30); // expected-warning{{TRUE}} Class class51 = [super class]; Class class52 = class51; unsigned result5 = [class52 returns30]; clang_analyzer_eval(result5 == 100); // expected-warning{{TRUE}} // Check that we inline class methods when class object is a receiver Class class6 = [self class]; BOOL calledClassMethod = [class6 isClass]; clang_analyzer_eval(calledClassMethod == YES); // expected-warning{{TRUE}} // Check that class info is propagated through the 'self' method Class class71 = [self class]; Class class72 = [class71 self]; unsigned result7 = [class72 returns30]; clang_analyzer_eval(result7 == 30); // expected-warning{{TRUE}} // Check that 'class' and 'super' info from direct invocation of the // corresponding class methods is propagated with data Class class8 = [SelfClassTest class]; unsigned result8 = [class8 returns30]; clang_analyzer_eval(result8 == 30); // expected-warning{{TRUE}} Class class9 = [SelfClassTest superclass]; unsigned result9 = [class9 returns30]; clang_analyzer_eval(result9 == 100); // expected-warning{{TRUE}} // Check that we get class from a propagated type SelfClassTestParent *selfAsParent10 = [[SelfClassTest alloc] init]; Class class10 = [selfAsParent10 class]; unsigned result10 = [class10 returns30]; clang_analyzer_eval(result10 == 30); // expected-warning{{TRUE}} SelfClassTestParent *selfAsParent11 = [[[self class] alloc] init]; Class class11 = [selfAsParent11 class]; unsigned result11 = [class11 returns30]; clang_analyzer_eval(result11 == 30); // expected-warning{{TRUE}} } @end @interface Parent : NSObject + (int)a; + (int)b; @end @interface Child : Parent @end @interface Other : NSObject +(void)run; @end int main(int argc, const char * argv[]) { @autoreleasepool { [Other run]; } return 0; } @implementation Other +(void)run { int result = [Child a]; // TODO: This should return 100. clang_analyzer_eval(result == 12); // expected-warning{{TRUE}} } @end @implementation Parent + (int)a; { return [self b]; } + (int)b; { return 12; } @end @implementation Child + (int)b; { return 100; } @end