// RUN: %clang_analyze_cc1 -Wno-conversion -Wno-tautological-constant-compare -analyzer-checker=core,apiModeling,alpha.core.Conversion -verify %s

unsigned char U8;
signed char S8;

void assign(unsigned U, signed S) {
  if (S < -10)
    U8 = S; // expected-warning {{Loss of sign in implicit conversion}}
  if (U > 300)
    S8 = U; // expected-warning {{Loss of precision in implicit conversion}}
  if (S > 10)
    U8 = S; // no-warning
  if (U < 200)
    S8 = U; // no-warning
}

void addAssign() {
  unsigned long L = 1000;
  int I = -100;
  U8 += L; // expected-warning {{Loss of precision in implicit conversion}}
  L += I; // no-warning
}

void subAssign() {
  unsigned long L = 1000;
  int I = -100;
  U8 -= L; // expected-warning {{Loss of precision in implicit conversion}}
  L -= I; // no-warning
}

void mulAssign() {
  unsigned long L = 1000;
  int I = -1;
  U8 *= L; // expected-warning {{Loss of precision in implicit conversion}}
  L *= I;  // expected-warning {{Loss of sign in implicit conversion}}
  I = 10;
  L *= I; // no-warning
}

void divAssign() {
  unsigned long L = 1000;
  int I = -1;
  U8 /= L; // no-warning
  L /= I; // expected-warning {{Loss of sign in implicit conversion}}
}

void remAssign() {
  unsigned long L = 1000;
  int I = -1;
  U8 %= L; // no-warning
  L %= I; // expected-warning {{Loss of sign in implicit conversion}}
}

void andAssign() {
  unsigned long L = 1000;
  int I = -1;
  U8 &= L; // no-warning
  L &= I; // expected-warning {{Loss of sign in implicit conversion}}
}

void orAssign() {
  unsigned long L = 1000;
  int I = -1;
  U8 |= L; // expected-warning {{Loss of precision in implicit conversion}}
  L |= I;  // expected-warning {{Loss of sign in implicit conversion}}
}

void xorAssign() {
  unsigned long L = 1000;
  int I = -1;
  U8 ^= L; // expected-warning {{Loss of precision in implicit conversion}}
  L ^= I;  // expected-warning {{Loss of sign in implicit conversion}}
}

void init1() {
  long long A = 1LL << 60;
  short X = A; // expected-warning {{Loss of precision in implicit conversion}}
}

void relational(unsigned U, signed S) {
  if (S > 10) {
    if (U < S) { // no-warning
    }
  }
  if (S < -10) {
    if (U < S) { // expected-warning {{Loss of sign in implicit conversion}}
    }
  }
}

void multiplication(unsigned U, signed S) {
  if (S > 5)
    S = U * S; // no-warning
  if (S < -10)
    S = U * S; // expected-warning {{Loss of sign}}
}

void division(unsigned U, signed S) {
  if (S > 5)
    S = U / S; // no-warning
  if (S < -10)
    S = U / S; // expected-warning {{Loss of sign}}
}

void dontwarn1(unsigned U, signed S) {
  U8 = S; // It might be known that S is always 0x00-0xff.
  S8 = U; // It might be known that U is always 0x00-0xff.

  U8 = -1;  // Explicit conversion.
  S8 = ~0U; // Explicit conversion.
  if (U > 300)
    U8 &= U; // No loss of precision since there is &=.
}

void dontwarn2(unsigned int U) {
  if (U <= 4294967295) {
  }
  if (U <= (2147483647 * 2U + 1U)) {
  }
}

void dontwarn3(int X) {
  S8 = X ? 'a' : 'b';
}

// don't warn for macros
#define DOSTUFF ({ unsigned X = 1000; U8 = X; })
void dontwarn4() {
  DOSTUFF;
}

// don't warn for calculations
// seen some fp. For instance:  c2 = (c2 >= 'A' && c2 <= 'Z') ? c2 - 'A' + 'a' : c2;
// there is a todo in the checker to handle calculations
void dontwarn5() {
  signed S = -32;
  U8 = S + 10;
}

char dontwarn6(long long x) {
  long long y = 42;
  y += x;
  return y == 42;
}


// C library functions, handled via apiModeling.StdCLibraryFunctions

int isascii(int c);
void libraryFunction1() {
  char kb2[5];
  int X = 1000;
  if (isascii(X)) {
    kb2[0] = X; // no-warning
  }
}


typedef struct FILE {} FILE; int getc(FILE *stream);
# define EOF (-1)
char reply_string[8192];
FILE *cin;
extern int dostuff(void);
int libraryFunction2() {
  int c, n;
  int dig;
  char *cp = reply_string;
  int pflag = 0;
  int code;

  for (;;) {
    dig = n = code = 0;
    while ((c = getc(cin)) != '\n') {
      if (dig < 4 && dostuff())
        code = code * 10 + (c - '0');
      if (!pflag && code == 227)
        pflag = 1;
      if (n == 0)
        n = c;
      if (c == EOF)
        return(4);
      if (cp < &reply_string[sizeof(reply_string) - 1])
        *cp++ = c; // no-warning
    }
  }
}

double floating_point(long long a, int b) {
  if (a > 1LL << 55) {
    double r = a; // expected-warning {{Loss of precision}}
    return r;
  } else if (b > 1 << 25) {
    float f = b; // expected-warning {{Loss of precision}}
    return f;
  }
  return 137;
}

double floating_point2() {
  int a = 1 << 24;
  long long b = 1LL << 53;
  float f = a; // no-warning
  double d = b; // no-warning
  return d - f;
}

int floating_point_3(unsigned long long a) {
  double b = a; // no-warning
  return 42;
}
