// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t \
// RUN: -config="{CheckOptions: [ \
// RUN:   {key: "cppcoreguidelines-narrowing-conversions.WarnOnFloatingPointNarrowingConversion", value: 0}, \
// RUN: ]}" \
// RUN: -- -target x86_64-unknown-linux -fsigned-char

float ceil(float);
namespace std {
double ceil(double);
long double floor(long double);
} // namespace std

namespace floats {

struct ConvertsToFloat {
  operator float() const { return 0.5f; }
};

float operator"" _float(unsigned long long);

void narrow_fp_to_int_not_ok(double d) {
  int i = 0;
  i = d;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
  i = 0.5f;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
  i = static_cast<float>(d);
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
  i = ConvertsToFloat();
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
  i = 15_float;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
  i += d;
  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
  i += 0.5;
  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
  i += 0.5f;
  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
  i *= 0.5f;
  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
  i /= 0.5f;
  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
  i += (double)0.5f;
  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
  i += 2.0;
  i += 2.0f;
}

double operator"" _double(unsigned long long);

float narrow_double_to_float_return() {
  return 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
}

void narrow_double_to_float_ok(double d) {
  float f;
  f = d;
  f = 15_double;
}

void narrow_fp_constants() {
  float f;
  f = 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.

  f = __builtin_huge_valf();  // max float is not narrowing.
  f = -__builtin_huge_valf(); // -max float is not narrowing.
  f = __builtin_inff();       // float infinity is not narrowing.
  f = __builtin_nanf("0");    // float NaN is not narrowing.

  f = __builtin_huge_val();  // max double is not within-range of float.
  f = -__builtin_huge_val(); // -max double is not within-range of float.
  f = __builtin_inf();       // double infinity is not within-range of float.
  f = __builtin_nan("0");    // double NaN is not narrowing.
}

void narrow_double_to_float_not_ok_binary_ops(double d) {
  float f;
  f += 0.5;          // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
  f += 2.0;          // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
  f *= 0.5;          // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
  f /= 0.5;          // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
  f += (double)0.5f; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
  f += d;            // We do not warn about floating point narrowing by default.
}

void narrow_fp_constant_to_bool_not_ok() {
  bool b1 = 1.0;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant 'double' to 'bool' [cppcoreguidelines-narrowing-conversions]
  bool b2 = 1.0f;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant 'float' to 'bool' [cppcoreguidelines-narrowing-conversions]
}

void narrow_integer_to_floating() {
  {
    long long ll; // 64 bits
    float f = ll; // doesn't fit in 24 bits
    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'long long' to 'float' [cppcoreguidelines-narrowing-conversions]
    double d = ll; // doesn't fit in 53 bits.
    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: narrowing conversion from 'long long' to 'double' [cppcoreguidelines-narrowing-conversions]
  }
  {
    int i;       // 32 bits
    float f = i; // doesn't fit in 24 bits
    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'int' to 'float' [cppcoreguidelines-narrowing-conversions]
    double d = i; // fits in 53 bits.
  }
  {
    short n1, n2;
    float f = n1 + n2; // 'n1 + n2' is of type 'int' because of integer rules
    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'int' to 'float' [cppcoreguidelines-narrowing-conversions]
  }
  {
    short s;      // 16 bits
    float f = s;  // fits in 24 bits
    double d = s; // fits in 53 bits.
  }
}

void narrow_integer_to_unsigned_integer_is_ok() {
  char c;
  short s;
  int i;
  long l;
  long long ll;

  unsigned char uc;
  unsigned short us;
  unsigned int ui;
  unsigned long ul;
  unsigned long long ull;

  ui = c;
  uc = s;
  uc = i;
  uc = l;
  uc = ll;

  uc = uc;
  uc = us;
  uc = ui;
  uc = ul;
  uc = ull;
}

void narrow_integer_to_signed_integer_is_not_ok() {
  char c;
  short s;
  int i;
  long l;
  long long ll;

  unsigned char uc;
  unsigned short us;
  unsigned int ui;
  unsigned long ul;
  unsigned long long ull;

  c = c;
  c = s;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'short' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  c = i;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  c = l;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  c = ll;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]

  c = uc;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned char' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  c = us;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned short' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  c = ui;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  c = ul;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  c = ull;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]

  i = c;
  i = s;
  i = i;
  i = l;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  i = ll;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]

  i = uc;
  i = us;
  i = ui;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned int' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  i = ul;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  i = ull;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]

  ll = c;
  ll = s;
  ll = i;
  ll = l;
  ll = ll;

  ll = uc;
  ll = us;
  ll = ui;
  ll = ul;
  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  ll = ull;
  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
}

void narrow_constant_to_unsigned_integer_is_ok() {
  unsigned char uc1 = 0;
  unsigned char uc2 = 255;
  unsigned char uc3 = -1;  // unsigned dst type is well defined.
  unsigned char uc4 = 256; // unsigned dst type is well defined.
  unsigned short us1 = 0;
  unsigned short us2 = 65535;
  unsigned short us3 = -1;    // unsigned dst type is well defined.
  unsigned short us4 = 65536; // unsigned dst type is well defined.
}

void narrow_constant_to_signed_integer_is_not_ok() {
  char c1 = -128;
  char c2 = 127;
  char c3 = -129;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant value -129 (0xFFFFFF7F) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  char c4 = 128;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant value 128 (0x00000080) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]

  short s1 = -32768;
  short s2 = 32767;
  short s3 = -32769;
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: narrowing conversion from constant value -32769 (0xFFFF7FFF) of type 'int' to signed type 'short' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  short s4 = 32768;
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: narrowing conversion from constant value 32768 (0x00008000) of type 'int' to signed type 'short' is implementation-defined [cppcoreguidelines-narrowing-conversions]
}

void narrow_conditional_operator_contant_to_unsigned_is_ok(bool b) {
  // conversion to unsigned dst type is well defined.
  unsigned char c1 = b ? 1 : 0;
  unsigned char c2 = b ? 1 : 256;
  unsigned char c3 = b ? -1 : 0;
}

void narrow_conditional_operator_contant_to_signed_is_not_ok(bool b) {
  char uc1 = b ? 1 : 0;
  char uc2 = b ? 1 : 128;
  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: narrowing conversion from constant value 128 (0x00000080) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  char uc3 = b ? -129 : 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: narrowing conversion from constant value -129 (0xFFFFFF7F) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  unsigned long long ysize;
  long long mirror = b ? -1 : ysize - 1;
  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: narrowing conversion from constant value 18446744073709551615 (0xFFFFFFFFFFFFFFFF) of type 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
  // CHECK-MESSAGES: :[[@LINE-2]]:37: warning: narrowing conversion from 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
}

void narrow_constant_to_floating_point() {
  float f_ok = 1ULL << 24;              // fits in 24 bits mantissa.
  float f_not_ok = (1ULL << 24) + 1ULL; // doesn't fit in 24 bits mantissa.
  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: narrowing conversion from constant value 16777217 of type 'unsigned long long' to 'float' [cppcoreguidelines-narrowing-conversions]
  double d_ok = 1ULL << 53;              // fits in 53 bits mantissa.
  double d_not_ok = (1ULL << 53) + 1ULL; // doesn't fit in 53 bits mantissa.
  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: narrowing conversion from constant value 9007199254740993 of type 'unsigned long long' to 'double' [cppcoreguidelines-narrowing-conversions]
}

void casting_integer_to_bool_is_ok() {
  int i;
  while (i) {
  }
  for (; i;) {
  }
  if (i) {
  }
}

void casting_float_to_bool_is_not_ok() {
  float f;
  while (f) {
    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions]
  }
  for (; f;) {
    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions]
  }
  if (f) {
    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions]
  }
}

void legitimate_comparison_do_not_warn(unsigned long long size) {
  for (int i = 0; i < size; ++i) {
  }
}

void ok(double d) {
  int i = 0;
  i = 1;
  i = static_cast<int>(0.5);
  i = static_cast<int>(d);
  i = std::ceil(0.5);
  i = ::std::floor(0.5);
  {
    using std::ceil;
    i = ceil(0.5f);
  }
  i = ceil(0.5f);
}

void ok_binary_ops(double d) {
  int i = 0;
  i += 1;
  i += static_cast<int>(0.5);
  i += static_cast<int>(d);
  i += (int)d;
  i += std::ceil(0.5);
  i += ::std::floor(0.5);
  {
    using std::ceil;
    i += ceil(0.5f);
  }
  i += ceil(0.5f);
}

// We're bailing out in templates and macros.
template <typename T1, typename T2>
void f(T1 one, T2 two) {
  one += two;
}

void template_context() {
  f(1, 2);
  f(1, .5f);
  f(1, .5);
  f(1, .5l);
}

#define DERP(i, j) (i += j)

void macro_context() {
  int i = 0;
  DERP(i, 2);
  DERP(i, .5f);
  DERP(i, .5);
  DERP(i, .5l);
}

} // namespace floats
