| // RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config ipa=dynamic-bifurcate -verify %s |
| |
| // Test inlining of ObjC class methods. |
| |
| typedef signed char BOOL; |
| typedef struct objc_class *Class; |
| typedef struct objc_object { |
| Class isa; |
| } *id; |
| @protocol NSObject - (BOOL)isEqual:(id)object; @end |
| @interface NSObject <NSObject> {} |
| +(id)alloc; |
| -(id)init; |
| -(id)autorelease; |
| -(id)copy; |
| - (Class)class; |
| -(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 |
| |
| |
| // False negative. |
| // ObjC class method call through a decl with a known type. |
| // We should be able to track the type of currentClass and inline this call. |
| // 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; // Would be great to get a warning here. |
| } |
| @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; // Should warn here. |
| } |
| @end |
| int foo2() { |
| int y = [MyParentSelf testSelf]; |
| return 5/y; // Should warn here. |
| } |
| |
| // 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 { |
| return [self getNum]; |
| } |
| @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]; |
| } |
| |