// 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];
}