| // RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=dynamic -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. |
| } |