// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=core,nullability -DNOSYSTEMHEADERS=0 -verify %s
// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=core,nullability -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true -DNOSYSTEMHEADERS=1 -verify %s

#include "Inputs/system-header-simulator-for-nullability.h"

@interface TestObject : NSObject
- (int *_Nonnull)returnsNonnull;
- (int *_Nullable)returnsNullable;
- (int *)returnsUnspecified;
- (void)takesNonnull:(int *_Nonnull)p;
- (void)takesNullable:(int *_Nullable)p;
- (void)takesUnspecified:(int *)p;
@property(readonly, strong) NSString *stuff;
@end

TestObject * getUnspecifiedTestObject();
TestObject *_Nonnull getNonnullTestObject();
TestObject *_Nullable getNullableTestObject();

int getRandom();

typedef struct Dummy { int val; } Dummy;

void takesNullable(Dummy *_Nullable);
void takesNonnull(Dummy *_Nonnull);
void takesUnspecified(Dummy *);

Dummy *_Nullable returnsNullable();
Dummy *_Nonnull returnsNonnull();
Dummy *returnsUnspecified();
int *_Nullable returnsNullableInt();

template <typename T> T *eraseNullab(T *p) { return p; }

void takesAttrNonnull(Dummy *p) __attribute((nonnull(1)));

void testBasicRules() {
  Dummy *p = returnsNullable();
  int *ptr = returnsNullableInt();
  // Make every dereference a different path to avoid sinks after errors.
  switch (getRandom()) {
  case 0: {
    Dummy &r = *p; // expected-warning {{Nullable pointer is dereferenced}}
  } break;
  case 1: {
    int b = p->val; // expected-warning {{Nullable pointer is dereferenced}}
  } break;
  case 2: {
    int stuff = *ptr; // expected-warning {{Nullable pointer is dereferenced}}
  } break;
  case 3:
    takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
    break;
  case 4: {
    Dummy d;
    takesNullable(&d);
    Dummy dd(d);
    break;
  }
  case 5: takesAttrNonnull(p); break; // expected-warning {{Nullable pointer is passed to}}
  default: { Dummy d = *p; } break; // expected-warning {{Nullable pointer is dereferenced}}
  }
  if (p) {
    takesNonnull(p);
    if (getRandom()) {
      Dummy &r = *p;
    } else {
      int b = p->val;
    }
  }
  Dummy *q = 0;
  if (getRandom()) {
    takesNullable(q);
    takesNonnull(q); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}}
  }
  Dummy a;
  Dummy *_Nonnull nonnull = &a;
  nonnull = q; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}}
  q = &a;
  takesNullable(q);
  takesNonnull(q);
}

void testMultiParamChecking(Dummy *_Nonnull a, Dummy *_Nullable b,
                            Dummy *_Nonnull c);

void testArgumentTracking(Dummy *_Nonnull nonnull, Dummy *_Nullable nullable) {
  Dummy *p = nullable;
  Dummy *q = nonnull;
  switch(getRandom()) {
  case 1: nonnull = p; break; // expected-warning {{Nullable pointer is assigned to a pointer which is expected to have non-null value}}
  case 2: p = 0; break;
  case 3: q = p; break;
  case 4: testMultiParamChecking(nonnull, nullable, nonnull); break;
  case 5: testMultiParamChecking(nonnull, nonnull, nonnull); break;
  case 6: testMultiParamChecking(nonnull, nullable, nullable); break; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 3rd parameter}}
  case 7: testMultiParamChecking(nullable, nullable, nonnull); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
  case 8: testMultiParamChecking(nullable, nullable, nullable); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
  case 9: testMultiParamChecking((Dummy *_Nonnull)0, nullable, nonnull); break;
  }
}

Dummy *_Nonnull testNullableReturn(Dummy *_Nullable a) {
  Dummy *p = a;
  return p; // expected-warning {{Nullable pointer is returned from a function that is expected to return a non-null value}}
}

Dummy *_Nonnull testNullReturn() {
  Dummy *p = 0;
  return p; // expected-warning {{Null is returned from a function that is expected to return a non-null value}}
}

void testObjCMessageResultNullability() {
  // The expected result: the most nullable of self and method return type.
  TestObject *o = getUnspecifiedTestObject();
  int *shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNonnull];
  switch (getRandom()) {
  case 0:
    // The core analyzer assumes that the receiver is non-null after a message
    // send. This is to avoid some false positives, and increase performance
    // but it also reduces the coverage and makes this checker unable to reason
    // about the nullness of the receiver. 
    [o takesNonnull:shouldBeNullable]; // No warning expected.
    break;
  case 1:
    shouldBeNullable =
        [eraseNullab(getNullableTestObject()) returnsUnspecified];
    [o takesNonnull:shouldBeNullable]; // No warning expected.
    break;
  case 3:
    shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable];
    [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
    break;
  case 4:
    shouldBeNullable = [eraseNullab(getNonnullTestObject()) returnsNullable];
    [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
    break;
  case 5:
    shouldBeNullable =
        [eraseNullab(getUnspecifiedTestObject()) returnsNullable];
    [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
    break;
  case 6:
    shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable];
    [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
    break;
  case 7: {
    int *shouldBeNonnull = [eraseNullab(getNonnullTestObject()) returnsNonnull];
    [o takesNonnull:shouldBeNonnull];
  } break;
  }
}

Dummy * _Nonnull testDirectCastNullableToNonnull() {
  Dummy *p = returnsNullable();
  takesNonnull((Dummy * _Nonnull)p);  // no-warning
  return (Dummy * _Nonnull)p;         // no-warning
}

Dummy * _Nonnull testIndirectCastNullableToNonnull() {
  Dummy *p = (Dummy * _Nonnull)returnsNullable();
  takesNonnull(p);  // no-warning
  return p;         // no-warning
}

Dummy * _Nonnull testDirectCastNilToNonnull() {
  takesNonnull((Dummy * _Nonnull)0);  // no-warning
  return (Dummy * _Nonnull)0;         // no-warning
}

void testIndirectCastNilToNonnullAndPass() {
  Dummy *p = (Dummy * _Nonnull)0;
  // FIXME: Ideally the cast above would suppress this warning.
  takesNonnull(p);  // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}}
}

void testDirectCastNilToNonnullAndAssignToLocalInInitializer() {
  Dummy * _Nonnull nonnullLocalWithAssignmentInInitializer = (Dummy * _Nonnull)0; // no-warning
  (void)nonnullLocalWithAssignmentInInitializer;

  // Since we've already had an invariant violation along this path,
  // we shouldn't warn here.
  nonnullLocalWithAssignmentInInitializer = 0;
  (void)nonnullLocalWithAssignmentInInitializer;

}

void testDirectCastNilToNonnullAndAssignToLocal(Dummy * _Nonnull p) {
  Dummy * _Nonnull nonnullLocalWithAssignment = p;
  nonnullLocalWithAssignment = (Dummy * _Nonnull)0; // no-warning
  (void)nonnullLocalWithAssignment;

  // Since we've already had an invariant violation along this path,
  // we shouldn't warn here.
  nonnullLocalWithAssignment = 0;
  (void)nonnullLocalWithAssignment;
}

void testDirectCastNilToNonnullAndAssignToParam(Dummy * _Nonnull p) {
  p = (Dummy * _Nonnull)0; // no-warning
}

@interface ClassWithNonnullIvar : NSObject {
  Dummy *_nonnullIvar;
}
@end

@implementation ClassWithNonnullIvar
-(void)testDirectCastNilToNonnullAndAssignToIvar {
  _nonnullIvar = (Dummy * _Nonnull)0; // no-warning;

  // Since we've already had an invariant violation along this path,
  // we shouldn't warn here.
  _nonnullIvar = 0;
}
@end

void testIndirectNilPassToNonnull() {
  Dummy *p = 0;
  takesNonnull(p);  // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}}
}

void testConditionalNilPassToNonnull(Dummy *p) {
  if (!p) {
    takesNonnull(p);  // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}}
  }
}

Dummy * _Nonnull testIndirectCastNilToNonnullAndReturn() {
  Dummy *p = (Dummy * _Nonnull)0;
  // FIXME: Ideally the cast above would suppress this warning.
  return p; // expected-warning {{Null is returned from a function that is expected to return a non-null value}}
}

void testInvalidPropagation() {
  Dummy *p = returnsUnspecified();
  takesNullable(p);
  takesNonnull(p);
}

void onlyReportFirstPreconditionViolationOnPath() {
  Dummy *p = returnsNullable();
  takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
  takesNonnull(p); // No warning.
  // The first warning was not a sink. The analysis expected to continue.
  int i = 0;
  i = 5 / i; // expected-warning {{Division by zero}}
  (void)i;
}

Dummy *_Nonnull doNotWarnWhenPreconditionIsViolatedInTopFunc(
    Dummy *_Nonnull p) {
  if (!p) {
    Dummy *ret =
        0; // avoid compiler warning (which is not generated by the analyzer)
    if (getRandom())
      return ret; // no warning
    else
      return p; // no warning
  } else {
    return p;
  }
}

Dummy *_Nonnull doNotWarnWhenPreconditionIsViolated(Dummy *_Nonnull p) {
  if (!p) {
    Dummy *ret =
        0; // avoid compiler warning (which is not generated by the analyzer)
    if (getRandom())
      return ret; // no warning
    else
      return p; // no warning
  } else {
    return p;
  }
}

void testPreconditionViolationInInlinedFunction(Dummy *p) {
  doNotWarnWhenPreconditionIsViolated(p);
}

@interface TestInlinedPreconditionViolationClass : NSObject
@end

@implementation TestInlinedPreconditionViolationClass
-(Dummy * _Nonnull) calleeWithParam:(Dummy * _Nonnull) p2 {
  Dummy *x = 0;
  if (!p2) // p2 binding becomes dead at this point.
    return x; // no-warning
  else
   return p2;
}

-(Dummy *)callerWithParam:(Dummy * _Nonnull) p1 {
  return [self calleeWithParam:p1];
}

@end

int * _Nonnull InlinedPreconditionViolationInFunctionCallee(int * _Nonnull p2) {
  int *x = 0;
  if (!p2) // p2 binding becomes dead at this point.
    return x; // no-warning
  else
   return p2;
}

int * _Nonnull InlinedReturnNullOverSuppressionCallee(int * _Nonnull p2) {
  int *result = 0;
  return result; // no-warning; but this is an over suppression
}

int *InlinedReturnNullOverSuppressionCaller(int * _Nonnull p1) {
  return InlinedReturnNullOverSuppressionCallee(p1);
}

void inlinedNullable(Dummy *_Nullable p) {
  if (p) return;
}
void inlinedNonnull(Dummy *_Nonnull p) {
  if (p) return;
}
void inlinedUnspecified(Dummy *p) {
  if (p) return;
}

void testNilReturnWithBlock(Dummy *p) {
  p = 0;
  Dummy *_Nonnull (^myblock)(void) = ^Dummy *_Nonnull(void) {
    return p; // TODO: We should warn in blocks.
  };
  myblock();
}

Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) {
  switch (getRandom()) {
  case 1: inlinedNullable(p); break;
  case 2: inlinedNonnull(p); break;
  case 3: inlinedUnspecified(p); break;
  }
  if (getRandom())
    takesNonnull(p);  // no-warning

  if (getRandom()) {
    Dummy *_Nonnull varWithInitializer = p; // no-warning

     Dummy *_Nonnull var1WithInitializer = p,  // no-warning
           *_Nonnull var2WithInitializer = p;  // no-warning
  }

  if (getRandom()) {
    Dummy *_Nonnull varWithoutInitializer;
    varWithoutInitializer = p; // no-warning
  }

  return p;
}


@interface SomeClass : NSObject {
  int instanceVar;
}
@end

@implementation SomeClass (MethodReturn)
- (id)initWithSomething:(int)i {
  if (self = [super init]) {
    instanceVar = i;
  }

  return self;
}

- (TestObject * _Nonnull)testReturnsNullableInNonnullIndirectly {
  TestObject *local = getNullableTestObject();
  return local; // expected-warning {{Nullable pointer is returned from a method that is expected to return a non-null value}}
}

- (TestObject * _Nonnull)testReturnsCastSuppressedNullableInNonnullIndirectly {
  TestObject *local = getNullableTestObject();
  return (TestObject * _Nonnull)local; // no-warning
}

- (TestObject * _Nonnull)testReturnsNullableInNonnullWhenPreconditionViolated:(TestObject * _Nonnull) p {
  TestObject *local = getNullableTestObject();
  if (!p) // Pre-condition violated here.
    return local; // no-warning
  else
    return p; // no-warning
}
@end

@interface ClassWithInitializers : NSObject
@end

@implementation ClassWithInitializers
- (instancetype _Nonnull)initWithNonnullReturnAndSelfCheckingIdiom {
  // This defensive check is a common-enough idiom that we filter don't want
  // to issue a diagnostic for it,
  if (self = [super init]) {
  }

  return self; // no-warning
}

- (instancetype _Nonnull)initWithNonnullReturnAndNilReturnViaLocal {
  self = [super init];
  // This leaks, but we're not checking for that here.

  ClassWithInitializers *other = nil;
  // False negative. Once we have more subtle suppression of defensive checks in
  // initializers we should warn here.
  return other;
}
@end

@interface SubClassWithInitializers : ClassWithInitializers
@end

@implementation SubClassWithInitializers
// Note: Because this is overridding
// -[ClassWithInitializers initWithNonnullReturnAndSelfCheckingIdiom],
// the return type of this method becomes implicitly id _Nonnull.
- (id)initWithNonnullReturnAndSelfCheckingIdiom {
  if (self = [super initWithNonnullReturnAndSelfCheckingIdiom]) {
  }

  return self; // no-warning
}

- (id _Nonnull)initWithNonnullReturnAndSelfCheckingIdiomV2; {
  // Another common return-checking idiom
  self = [super initWithNonnullReturnAndSelfCheckingIdiom];
  if (!self) {
    return nil; // no-warning
  }

  return self;
}
@end

@interface ClassWithCopyWithZone : NSObject<NSCopying,NSMutableCopying> {
  id i;
}

@end

@implementation ClassWithCopyWithZone
-(id)copyWithZone:(NSZone *)zone {
  ClassWithCopyWithZone *newInstance = [[ClassWithCopyWithZone alloc] init];
  if (!newInstance)
    return nil;

  newInstance->i = i;
  return newInstance;
}

-(id)mutableCopyWithZone:(NSZone *)zone {
  ClassWithCopyWithZone *newInstance = [[ClassWithCopyWithZone alloc] init];
  if (newInstance) {
    newInstance->i = i;
  }

  return newInstance;
}
@end

NSString * _Nullable returnsNullableString();

void callFunctionInSystemHeader() {
  NSString *s = returnsNullableString();

  NSSystemFunctionTakingNonnull(s);
  #if !NOSYSTEMHEADERS
  // expected-warning@-2{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
  #endif
}

void callMethodInSystemHeader() {
  NSString *s = returnsNullableString();

  NSSystemClass *sc = [[NSSystemClass alloc] init];
  [sc takesNonnull:s];
  #if !NOSYSTEMHEADERS
  // expected-warning@-2{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
  #endif
}

// Test to make sure the analyzer doesn't warn when an a nullability invariant
// has already been found to be violated on an instance variable.

@class MyInternalClass;
@interface MyClass : NSObject {
  MyInternalClass * _Nonnull _internal;
}
@end

@interface MyInternalClass : NSObject {
  @public
  id _someIvar;
}
-(id _Nonnull)methodWithInternalImplementation;
@end

@interface MyClass () {
  MyInternalClass * _Nonnull _nilledOutInternal;
}
@end

@implementation MyClass
-(id _Nonnull)methodWithInternalImplementation {
  if (!_internal)
    return nil; // no-warning

  return [_internal methodWithInternalImplementation];
}

- (id _Nonnull)methodReturningIvarInImplementation; {
  return _internal == 0 ? nil : _internal->_someIvar; // no-warning
}

-(id _Nonnull)methodWithNilledOutInternal {
  _nilledOutInternal = (id _Nonnull)nil;

  return nil; // no-warning
}
@end
