// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.Loops,debug.ExprInspection -verify %s

void clang_analyzer_eval(int);

#define nil ((id)0)

typedef unsigned long NSUInteger;
@protocol NSFastEnumeration
- (int)countByEnumeratingWithState:(void *)state objects:(id *)objects count:(unsigned)count;
@end

@interface NSObject
+ (instancetype)testObject;
@end

@interface NSEnumerator <NSFastEnumeration>
@end

@interface NSArray : NSObject <NSFastEnumeration>
- (NSUInteger)count;
- (NSEnumerator *)objectEnumerator;
@end

@interface NSDictionary : NSObject <NSFastEnumeration>
- (NSUInteger)count;
@end

@interface NSMutableDictionary : NSDictionary
@end

@interface NSSet : NSObject <NSFastEnumeration>
- (NSUInteger)count;
@end

@interface NSPointerArray : NSObject <NSFastEnumeration>
@end

@interface NSString : NSObject
@end

void test() {
  id x;
  for (x in [NSArray testObject])
    clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}

  for (x in [NSMutableDictionary testObject])
    clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}

  for (x in [NSSet testObject])
    clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}

  for (x in [[NSArray testObject] objectEnumerator])
    clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}

  for (x in [NSPointerArray testObject])
    clang_analyzer_eval(x != nil); // expected-warning{{UNKNOWN}}
}

void testWithVarInFor() {
  for (id x in [NSArray testObject])
    clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}
  for (id x in [NSPointerArray testObject])
    clang_analyzer_eval(x != nil); // expected-warning{{UNKNOWN}}
}

void testNonNil(id a, id b) {
  clang_analyzer_eval(a != nil); // expected-warning{{UNKNOWN}}
  for (id x in a)
    clang_analyzer_eval(a != nil); // expected-warning{{TRUE}}

  if (b != nil)
    return;
  for (id x in b)
    *(volatile int *)0 = 1; // no-warning
  clang_analyzer_eval(b != nil); // expected-warning{{FALSE}}
}

void collectionIsEmpty(NSMutableDictionary *D){
  if ([D count] == 0) { // Count is zero.
    NSString *s = 0;
    for (NSString *key in D) {
      s = key;       // Loop is never entered.
    }
    clang_analyzer_eval(s == 0); //expected-warning{{TRUE}}
  }
}

void processCollection(NSMutableDictionary *D);
void collectionIsEmptyCollectionIsModified(NSMutableDictionary *D){
  if ([D count] == 0) {      // Count is zero.
    NSString *s = 0;
    processCollection(D);  // However, the collection has changed.
    for (NSString *key in D) {
      s = key;       // Loop might be entered.
    }
    clang_analyzer_eval(s == 0); //expected-warning{{FALSE}} //expected-warning{{TRUE}}
  }
}

int collectionIsEmptyNSSet(NSSet *S){
  if ([S count] == 2) { // Count is non zero.
    int tapCounts[2];
    int i = 0;
    for (NSString *elem in S) {
      tapCounts[i]= 1;       // Loop is entered.
      i++;
    }
    return (tapCounts[0]); //no warning
  }
  return 0;
}

int collectionIsNotEmptyNSArray(NSArray *A) {
  int count = [A count];
  if (count > 0) {
    int i;
    int j;
    for (NSString *a in A) {
      i = 1;
      j++;
    }
    clang_analyzer_eval(i == 1); // expected-warning {{TRUE}}
  }
  return 0;
}

void onlySuppressExitAfterZeroIterations(NSMutableDictionary *D) {
  if (D.count > 0) {
    int *x;
    int i;
    for (NSString *key in D) {
      x = 0;
      i++;
    }
    // Test that this is reachable.
    int y = *x; // expected-warning {{Dereference of null pointer}}
    y++;
  }
}

void onlySuppressLoopExitAfterZeroIterations_WithContinue(NSMutableDictionary *D) {
  if (D.count > 0) {
    int *x;
    int i;
    for (NSString *key in D) {
      x = 0;
      i++;
      continue;
    }
    // Test that this is reachable.
    int y = *x; // expected-warning {{Dereference of null pointer}}
    y++;
  }
}

int* getPtr();
void onlySuppressLoopExitAfterZeroIterations_WithBreak(NSMutableDictionary *D) {
  if (D.count > 0) {
    int *x;
    int i;
    for (NSString *key in D) {
      x = 0;
      break;
      x = getPtr();
      i++;
    }
    int y = *x; // expected-warning {{Dereference of null pointer}}
    y++;
  }
}

int consistencyBetweenLoopsWhenCountIsUnconstrained(NSMutableDictionary *D) {
  // Note, The current limitation is that we need to have a count.
  // TODO: This should work even when we do not call count.
  int count = [D count];
  int i;
  int j = 0;
  for (NSString *key in D) {
    i = 5;
    j++;
  }
  for (NSString *key in D)  {
    return i; // no-warning
  }
  return 0;
}

int consistencyBetweenLoopsWhenCountIsUnconstrained_dual(NSMutableDictionary *D) {
  int count = [D count];
  int i = 8;
  int j = 1;
  for (NSString *key in D) {
    i = 0;
    j++;
  }
  for (NSString *key in D)  {
    i = 5;
    j++;
  }
  return 5/i;
}