// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,debug.ExprInspection -verify -analyzer-config eagerly-assume=false %s

void clang_analyzer_eval(int);

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

void use(int);
id foo(int x) {
  if (x)
    return 0;
  static id p = foo(1); 
    clang_analyzer_eval(p == 0); // expected-warning{{TRUE}}
  return p;
}

const int &globalIntRef = 42;

void testGlobalRef() {
  // FIXME: Should be TRUE, but should at least not crash.
  clang_analyzer_eval(globalIntRef == 42); // expected-warning{{UNKNOWN}}
}

extern int globalInt;
struct IntWrapper {
  int value;
};
extern struct IntWrapper globalStruct;
extern void invalidateGlobals();

void testGlobalInvalidation() {
  clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}}
  clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}}

  if (globalInt != 42)
    return;
  if (globalStruct.value != 43)
    return;
  clang_analyzer_eval(globalInt == 42); // expected-warning{{TRUE}}
  clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{TRUE}}

  invalidateGlobals();
  clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}}
  clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}}

  // Repeat to make sure we don't get the /same/ new symbolic values.
  if (globalInt != 42)
    return;
  if (globalStruct.value != 43)
    return;
  clang_analyzer_eval(globalInt == 42); // expected-warning{{TRUE}}
  clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{TRUE}}

  invalidateGlobals();
  clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}}
  clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}}
}

void testGlobalInvalidationWithDirectBinding() {
  clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}}
  clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}}

  globalInt = 42;
  globalStruct.value = 43;
  clang_analyzer_eval(globalInt == 42); // expected-warning{{TRUE}}
  clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{TRUE}}

  invalidateGlobals();
  clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}}
  clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}}
}

void testStaticLocals(void) {
  static int i;
  int tmp;

  extern int someSymbolicValue();
  i = someSymbolicValue();

  if (i == 5) {
    clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
    scanf("%d", &tmp);
    clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
    invalidateGlobals();
    clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
  }

  i = 6;
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
  scanf("%d", &tmp);
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
  invalidateGlobals();
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}

  i = someSymbolicValue();
  if (i == 7) {
    clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
    scanf("%d", &i);
    clang_analyzer_eval(i == 7); // expected-warning{{UNKNOWN}}
  }

  i = 8;
  clang_analyzer_eval(i == 8); // expected-warning{{TRUE}}
  scanf("%d", &i);
  clang_analyzer_eval(i == 8); // expected-warning{{UNKNOWN}}
}

void testNonSystemGlobals(void) {
  extern int i;
  int tmp;

  if (i == 5) {
    clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
    scanf("%d", &tmp);
    clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
    invalidateGlobals();
    clang_analyzer_eval(i == 5); // expected-warning{{UNKNOWN}}
  }

  i = 6;
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
  scanf("%d", &tmp);
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
  invalidateGlobals();
  clang_analyzer_eval(i == 6); // expected-warning{{UNKNOWN}}

  if (i == 7) {
    clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
    scanf("%d", &i);
    clang_analyzer_eval(i == 7); // expected-warning{{UNKNOWN}}
  }

  i = 8;
  clang_analyzer_eval(i == 8); // expected-warning{{TRUE}}
  scanf("%d", &i);
  clang_analyzer_eval(i == 8); // expected-warning{{UNKNOWN}}
}

void testWrappedGlobals(void) {
  extern char c;
  SomeStruct s;

  if (c == 'C') {
    s.p = &c;
    clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}}
    fakeSystemHeaderCall(0);
    clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}}
    fakeSystemHeaderCall(&s);
    clang_analyzer_eval(c == 'C'); // expected-warning{{UNKNOWN}}
  }

  c = 'c';
  s.p = &c;
  clang_analyzer_eval(c == 'c'); // expected-warning{{TRUE}}
  fakeSystemHeaderCall(0);
  clang_analyzer_eval(c == 'c'); // expected-warning{{TRUE}}
  fakeSystemHeaderCall(&s);
  clang_analyzer_eval(c == 'c'); // expected-warning{{UNKNOWN}}

  if (c == 'C') {
    s.p = &c;
    clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}}
    fakeSystemHeaderCall(0);
    clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}}
    fakeSystemHeaderCall(&s);
    clang_analyzer_eval(c == 'C'); // expected-warning{{UNKNOWN}}
  }
}

void testWrappedStaticsViaGlobal(void) {
  static char c;
  extern SomeStruct s;

  extern char getSomeChar();
  c = getSomeChar();

  if (c == 'C') {
    s.p = &c;
    clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}}
    invalidateGlobals();
    clang_analyzer_eval(c == 'C'); // expected-warning{{UNKNOWN}}
  }

  c = 'c';
  s.p = &c;
  clang_analyzer_eval(c == 'c'); // expected-warning{{TRUE}}
  invalidateGlobals();
  clang_analyzer_eval(c == 'c'); // expected-warning{{UNKNOWN}}
}
