| // 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 <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 |