// RUN: %check_clang_tidy %s readability-implicit-bool-conversion %t

// We need NULL macro, but some buildbots don't like including <cstddef> header
// This is a portable way of getting it to work
#undef NULL
#define NULL 0L

template<typename T>
void functionTaking(T);

struct Struct {
  int member;
};


////////// Implicit conversion from bool.

void implicitConversionFromBoolSimpleCases() {
  bool boolean = true;

  functionTaking<bool>(boolean);

  functionTaking<int>(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion bool -> 'int' [readability-implicit-bool-conversion]
  // CHECK-FIXES: functionTaking<int>(static_cast<int>(boolean));

  functionTaking<unsigned long>(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion bool -> 'unsigned long'
  // CHECK-FIXES: functionTaking<unsigned long>(static_cast<unsigned long>(boolean));

  functionTaking<char>(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion bool -> 'char'
  // CHECK-FIXES: functionTaking<char>(static_cast<char>(boolean));

  functionTaking<float>(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit conversion bool -> 'float'
  // CHECK-FIXES: functionTaking<float>(static_cast<float>(boolean));

  functionTaking<double>(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit conversion bool -> 'double'
  // CHECK-FIXES: functionTaking<double>(static_cast<double>(boolean));
}

float implicitConversionFromBoolInReturnValue() {
  bool boolean = false;
  return boolean;
  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit conversion bool -> 'float'
  // CHECK-FIXES: return static_cast<float>(boolean);
}

void implicitConversionFromBoolInSingleBoolExpressions(bool b1, bool b2) {
  bool boolean = true;
  boolean = b1 ^ b2;
  boolean = b1 && b2;
  boolean |= !b1 || !b2;
  boolean &= b1;
  boolean = b1 == true;
  boolean = b2 != false;

  int integer = boolean - 3;
  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit conversion bool -> 'int'
  // CHECK-FIXES: int integer = static_cast<int>(boolean) - 3;

  float floating = boolean / 0.3f;
  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit conversion bool -> 'float'
  // CHECK-FIXES: float floating = static_cast<float>(boolean) / 0.3f;

  char character = boolean;
  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit conversion bool -> 'char'
  // CHECK-FIXES: char character = static_cast<char>(boolean);
}

void implicitConversionFromBoollInComplexBoolExpressions() {
  bool boolean = true;
  bool anotherBoolean = false;

  int integer = boolean && anotherBoolean;
  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit conversion bool -> 'int'
  // CHECK-FIXES: int integer = static_cast<int>(boolean && anotherBoolean);

  unsigned long unsignedLong = (! boolean) + 4ul;
  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit conversion bool -> 'unsigned long'
  // CHECK-FIXES: unsigned long unsignedLong = static_cast<unsigned long>(! boolean) + 4ul;

  float floating = (boolean || anotherBoolean) * 0.3f;
  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit conversion bool -> 'float'
  // CHECK-FIXES: float floating = static_cast<float>(boolean || anotherBoolean) * 0.3f;

  double doubleFloating = (boolean && (anotherBoolean || boolean)) * 0.3;
  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: implicit conversion bool -> 'double'
  // CHECK-FIXES: double doubleFloating = static_cast<double>(boolean && (anotherBoolean || boolean)) * 0.3;
}

void implicitConversionFromBoolLiterals() {
  functionTaking<int>(true);
  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion bool -> 'int'
  // CHECK-FIXES: functionTaking<int>(1);

  functionTaking<unsigned long>(false);
  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion bool -> 'unsigned long'
  // CHECK-FIXES: functionTaking<unsigned long>(0u);

  functionTaking<signed char>(true);
  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: implicit conversion bool -> 'signed char'
  // CHECK-FIXES: functionTaking<signed char>(1);

  functionTaking<float>(false);
  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit conversion bool -> 'float'
  // CHECK-FIXES: functionTaking<float>(0.0f);

  functionTaking<double>(true);
  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit conversion bool -> 'double'
  // CHECK-FIXES: functionTaking<double>(1.0);
}

void implicitConversionFromBoolInComparisons() {
  bool boolean = true;
  int integer = 0;

  functionTaking<bool>(boolean == integer);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion bool -> 'int'
  // CHECK-FIXES: functionTaking<bool>(static_cast<int>(boolean) == integer);

  functionTaking<bool>(integer != boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: implicit conversion bool -> 'int'
  // CHECK-FIXES: functionTaking<bool>(integer != static_cast<int>(boolean));
}

void ignoreBoolComparisons() {
  bool boolean = true;
  bool anotherBoolean = false;

  functionTaking<bool>(boolean == anotherBoolean);
  functionTaking<bool>(boolean != anotherBoolean);
}

void ignoreExplicitCastsFromBool() {
  bool boolean = true;

  int integer = static_cast<int>(boolean) + 3;
  float floating = static_cast<float>(boolean) * 0.3f;
  char character = static_cast<char>(boolean);
}

void ignoreImplicitConversionFromBoolInMacroExpansions() {
  bool boolean = true;

  #define CAST_FROM_BOOL_IN_MACRO_BODY boolean + 3
  int integerFromMacroBody = CAST_FROM_BOOL_IN_MACRO_BODY;

  #define CAST_FROM_BOOL_IN_MACRO_ARGUMENT(x) x + 3
  int integerFromMacroArgument = CAST_FROM_BOOL_IN_MACRO_ARGUMENT(boolean);
}

namespace ignoreImplicitConversionFromBoolInTemplateInstantiations {

template<typename T>
void templateFunction() {
  bool boolean = true;
  T uknownType = boolean + 3;
}

void useOfTemplateFunction() {
  templateFunction<int>();
}

} // namespace ignoreImplicitConversionFromBoolInTemplateInstantiations

////////// Implicit conversions to bool.

void implicitConversionToBoolSimpleCases() {
  int integer = 10;
  functionTaking<bool>(integer);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: functionTaking<bool>(integer != 0);

  unsigned long unsignedLong = 10;
  functionTaking<bool>(unsignedLong);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'unsigned long' -> bool
  // CHECK-FIXES: functionTaking<bool>(unsignedLong != 0u);

  float floating = 0.0f;
  functionTaking<bool>(floating);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: functionTaking<bool>(floating != 0.0f);

  double doubleFloating = 1.0f;
  functionTaking<bool>(doubleFloating);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'double' -> bool
  // CHECK-FIXES: functionTaking<bool>(doubleFloating != 0.0);

  signed char character = 'a';
  functionTaking<bool>(character);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'signed char' -> bool
  // CHECK-FIXES: functionTaking<bool>(character != 0);

  int* pointer = nullptr;
  functionTaking<bool>(pointer);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int *' -> bool
  // CHECK-FIXES: functionTaking<bool>(pointer != nullptr);

  auto pointerToMember = &Struct::member;
  functionTaking<bool>(pointerToMember);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int Struct::*' -> bool
  // CHECK-FIXES: functionTaking<bool>(pointerToMember != nullptr);
}

void implicitConversionToBoolInSingleExpressions() {
  int integer = 10;
  bool boolComingFromInt = integer;
  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: bool boolComingFromInt = integer != 0;

  float floating = 10.0f;
  bool boolComingFromFloat = floating;
  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: bool boolComingFromFloat = floating != 0.0f;

  signed char character = 'a';
  bool boolComingFromChar = character;
  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: implicit conversion 'signed char' -> bool
  // CHECK-FIXES: bool boolComingFromChar = character != 0;

  int* pointer = nullptr;
  bool boolComingFromPointer = pointer;
  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit conversion 'int *' -> bool
  // CHECK-FIXES: bool boolComingFromPointer = pointer != nullptr;
}

void implicitConversionToBoolInComplexExpressions() {
  bool boolean = true;

  int integer = 10;
  int anotherInteger = 20;
  bool boolComingFromInteger = integer + anotherInteger;
  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: bool boolComingFromInteger = (integer + anotherInteger) != 0;

  float floating = 0.2f;
  bool boolComingFromFloating = floating - 0.3f || boolean;
  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: bool boolComingFromFloating = ((floating - 0.3f) != 0.0f) || boolean;

  double doubleFloating = 0.3;
  bool boolComingFromDoubleFloating = (doubleFloating - 0.4) && boolean;
  // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit conversion 'double' -> bool
  // CHECK-FIXES: bool boolComingFromDoubleFloating = ((doubleFloating - 0.4) != 0.0) && boolean;
}

void implicitConversionInNegationExpressions() {
  int integer = 10;
  bool boolComingFromNegatedInt = !integer;
  // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: bool boolComingFromNegatedInt = integer == 0;

  float floating = 10.0f;
  bool boolComingFromNegatedFloat = ! floating;
  // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: bool boolComingFromNegatedFloat = floating == 0.0f;

  signed char character = 'a';
  bool boolComingFromNegatedChar = (! character);
  // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit conversion 'signed char' -> bool
  // CHECK-FIXES: bool boolComingFromNegatedChar = (character == 0);

  int* pointer = nullptr;
  bool boolComingFromNegatedPointer = not pointer;
  // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: implicit conversion 'int *' -> bool
  // CHECK-FIXES: bool boolComingFromNegatedPointer = pointer == nullptr;
}

void implicitConversionToBoolInControlStatements() {
  int integer = 10;
  if (integer) {}
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: if (integer != 0) {}

  long int longInteger = 0.2f;
  for (;longInteger;) {}
  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit conversion 'long' -> bool
  // CHECK-FIXES: for (;longInteger != 0;) {}

  float floating = 0.3f;
  while (floating) {}
  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: while (floating != 0.0f) {}

  double doubleFloating = 0.4;
  do {} while (doubleFloating);
  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: implicit conversion 'double' -> bool
  // CHECK-FIXES: do {} while (doubleFloating != 0.0);
}

bool implicitConversionToBoolInReturnValue() {
  float floating = 1.0f;
  return floating;
  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: return floating != 0.0f;
}

void implicitConversionToBoolFromLiterals() {
  functionTaking<bool>(0);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: functionTaking<bool>(false);

  functionTaking<bool>(1);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: functionTaking<bool>(true);

  functionTaking<bool>(2ul);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'unsigned long' -> bool
  // CHECK-FIXES: functionTaking<bool>(true);


  functionTaking<bool>(0.0f);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: functionTaking<bool>(false);

  functionTaking<bool>(1.0f);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: functionTaking<bool>(true);

  functionTaking<bool>(2.0);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'double' -> bool
  // CHECK-FIXES: functionTaking<bool>(true);


  functionTaking<bool>('\0');
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'char' -> bool
  // CHECK-FIXES: functionTaking<bool>(false);

  functionTaking<bool>('a');
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'char' -> bool
  // CHECK-FIXES: functionTaking<bool>(true);


  functionTaking<bool>("");
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'const char *' -> bool
  // CHECK-FIXES: functionTaking<bool>(true);

  functionTaking<bool>("abc");
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'const char *' -> bool
  // CHECK-FIXES: functionTaking<bool>(true);

  functionTaking<bool>(NULL);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'long' -> bool
  // CHECK-FIXES: functionTaking<bool>(false);
}

void implicitConversionToBoolFromUnaryMinusAndZeroLiterals() {
  functionTaking<bool>(-0);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: functionTaking<bool>((-0) != 0);

  functionTaking<bool>(-0.0f);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'float' -> bool
  // CHECK-FIXES: functionTaking<bool>((-0.0f) != 0.0f);

  functionTaking<bool>(-0.0);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'double' -> bool
  // CHECK-FIXES: functionTaking<bool>((-0.0) != 0.0);
}

void implicitConversionToBoolInWithOverloadedOperators() {
  struct UserStruct {
    int operator()(int x) { return x; }
    int operator+(int y) { return y; }
  };

  UserStruct s;

  functionTaking<bool>(s(0));
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: functionTaking<bool>(s(0) != 0);

  functionTaking<bool>(s + 2);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: functionTaking<bool>((s + 2) != 0);
}

int functionReturningInt();
int* functionReturningPointer();

void ignoreImplicitConversionToBoolWhenDeclaringVariableInControlStatements() {
  if (int integer = functionReturningInt()) {}

  while (int* pointer = functionReturningPointer()) {}
}

void ignoreExplicitCastsToBool() {
  int integer = 10;
  bool boolComingFromInt = static_cast<bool>(integer);

  float floating = 10.0f;
  bool boolComingFromFloat = static_cast<bool>(floating);

  char character = 'a';
  bool boolComingFromChar = static_cast<bool>(character);

  int* pointer = nullptr;
  bool booleanComingFromPointer = static_cast<bool>(pointer);
}

void ignoreImplicitConversionToBoolInMacroExpansions() {
  int integer = 3;

  #define CAST_TO_BOOL_IN_MACRO_BODY integer && false
  bool boolFromMacroBody = CAST_TO_BOOL_IN_MACRO_BODY;

  #define CAST_TO_BOOL_IN_MACRO_ARGUMENT(x) x || true
  bool boolFromMacroArgument = CAST_TO_BOOL_IN_MACRO_ARGUMENT(integer);
}

namespace ignoreImplicitConversionToBoolInTemplateInstantiations {

template<typename T>
void templateFunction() {
  T unknownType = 0;
  bool boolean = unknownType;
}

void useOfTemplateFunction() {
  templateFunction<int>();
}

} // namespace ignoreImplicitConversionToBoolInTemplateInstantiations

namespace ignoreUserDefinedConversionOperator {

struct StructWithUserConversion {
  operator bool();
};

void useOfUserConversion() {
  StructWithUserConversion structure;
  functionTaking<bool>(structure);
}

} // namespace ignoreUserDefinedConversionOperator

namespace ignore_1bit_bitfields {

struct S {
  int a;
  int b : 1;
  int c : 2;

  S(bool a, bool b, bool c) : a(a), b(b), c(c) {}
  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion bool -> 'int'
  // CHECK-MESSAGES: :[[@LINE-2]]:45: warning: implicit conversion bool -> 'int'
  // CHECK-FIXES: S(bool a, bool b, bool c) : a(static_cast<int>(a)), b(b), c(static_cast<int>(c)) {}
};

bool f(S& s) {
  functionTaking<bool>(s.a);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: functionTaking<bool>(s.a != 0);
  functionTaking<bool>(s.b);
  // CHECK-FIXES: functionTaking<bool>(s.b);
  s.a = true;
  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit conversion bool -> 'int'
  // CHECK-FIXES: s.a = 1;
  s.b = true;
  // CHECK-FIXES: s.b = true;
  s.c = true;
  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit conversion bool -> 'int'
  // CHECK-FIXES: s.c = 1;
  functionTaking<bool>(s.c);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
  // CHECK-FIXES: functionTaking<bool>(s.c != 0);
}

} // namespace ignore_1bit_bitfields
