// RUN: %check_clang_tidy -std=c23-or-later --match-partial-fixes %s readability-implicit-bool-conversion %t
// RUN: %check_clang_tidy -std=c23-or-later -check-suffix=UPPER-CASE %s readability-implicit-bool-conversion %t -- \
// RUN:     -config='{CheckOptions: { \
// RUN:         readability-implicit-bool-conversion.UseUpperCaseLiteralSuffix: true \
// RUN:     }}'

#undef NULL
#define NULL 0L

void functionTakingBool(bool);
void functionTakingInt(int);
void functionTakingUnsignedLong(unsigned long);
void functionTakingChar(char);
void functionTakingFloat(float);
void functionTakingDouble(double);
void functionTakingSignedChar(signed char);


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

void implicitConversionFromBoolSimpleCases() {
  bool boolean = true;

  functionTakingBool(boolean);

  functionTakingInt(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: implicit conversion 'bool' -> 'int' [readability-implicit-bool-conversion]
  // CHECK-FIXES: functionTakingInt((int)boolean);

  functionTakingUnsignedLong(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit conversion 'bool' -> 'unsigned long'
  // CHECK-FIXES: functionTakingUnsignedLong((unsigned long)boolean);

  functionTakingChar(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'bool' -> 'char'
  // CHECK-FIXES: functionTakingChar((char)boolean);

  functionTakingFloat(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion 'bool' -> 'float'
  // CHECK-FIXES: functionTakingFloat((float)boolean);

  functionTakingDouble(boolean);
  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'bool' -> 'double'
  // CHECK-FIXES: functionTakingDouble((double)boolean);
}

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

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

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

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

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

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

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

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

  double doubleFloating = (boolean && (anotherBoolean || boolean)) * 0.3;
  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: implicit conversion 'bool' -> 'int'
  // CHECK-MESSAGES: :[[@LINE-2]]:40: warning: implicit conversion 'bool' -> 'int'
  // CHECK-MESSAGES: :[[@LINE-3]]:58: warning: implicit conversion 'bool' -> 'int'
  // CHECK-FIXES: double doubleFloating = ((int)boolean && ((int)anotherBoolean || (int)boolean)) * 0.3;
}

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

  functionTakingUnsignedLong(false);
  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit conversion 'bool' -> 'unsigned long'
  // CHECK-FIXES: functionTakingUnsignedLong(0u);
  // CHECK-FIXES-UPPER-CASE: functionTakingUnsignedLong(0U);

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

  functionTakingFloat(false);
  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion 'bool' -> 'float'
  // CHECK-FIXES: functionTakingFloat(0.0f);
  // CHECK-FIXES-UPPER-CASE: functionTakingFloat(0.0F);

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

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

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

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

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

  functionTakingBool(boolean == anotherBoolean);
  functionTakingBool(boolean != anotherBoolean);
}

void ignoreExplicitCastsFromBool() {
  bool boolean = true;

  int integer = (int)boolean + 3;
  float floating = (float)boolean * 0.3f;
  char character = (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);
}

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

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

  unsigned long unsignedLong = 10;
  functionTakingBool(unsignedLong);
  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'unsigned long' -> 'bool'
  // CHECK-FIXES: functionTakingBool(unsignedLong != 0u);
  // CHECK-FIXES-UPPER-CASE: functionTakingBool(unsignedLong != 0U);

  float floating = 0.0f;
  functionTakingBool(floating);
  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'float' -> 'bool'
  // CHECK-FIXES: functionTakingBool(floating != 0.0f);
  // CHECK-FIXES-UPPER-CASE: functionTakingBool(floating != 0.0F);

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

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

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

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

  float floating = 10.0f;
  bool boolComingFromFloat;
  boolComingFromFloat = floating;
  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit conversion 'float' -> 'bool'
  // CHECK-FIXES: boolComingFromFloat = (floating != 0.0f);
  // CHECK-FIXES-UPPER-CASE: boolComingFromFloat = (floating != 0.0F);

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

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

void implicitConversionToBoolInComplexExpressions() {
  bool boolean = true;

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

void implicitConversionInNegationExpressions() {
  int integer = 10;
  bool boolComingFromNegatedInt;
  boolComingFromNegatedInt = !integer;
  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit conversion 'int' -> 'bool'
  // CHECK-FIXES: boolComingFromNegatedInt = ((!integer) != 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() {
  functionTakingBool(0);
  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
  // CHECK-FIXES: functionTakingBool(false);

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

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

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

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

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

  functionTakingBool('\0');
  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
  // CHECK-FIXES: functionTakingBool(false);

  functionTakingBool('a');
  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
  // CHECK-FIXES: functionTakingBool(true);

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

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

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

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

  functionTakingBool(-0.0f);
  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'float' -> 'bool'
  // CHECK-FIXES: functionTakingBool((-0.0f) != 0.0f);
  // CHECK-FIXES-UPPER-CASE: functionTakingBool((-0.0f) != 0.0F);

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

void ignoreImplicitCastToBoolForComparisonResult() {
  bool boolFromComparison0 = 1 != 0;
  bool boolFromComparison1 = 1 == 0;
  bool boolFromComparison2 = 1 > 0;
  bool boolFromComparison3 = 1 >= 0;
  bool boolFromComparison4 = 1 < 0;
  bool boolFromComparison5 = 1 <= 0;
}

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

  float floating = 10.0f;
  bool boolComingFromFloat = (bool)floating;

  char character = 'a';
  bool boolComingFromChar = (bool)character;

  int* pointer = nullptr;
  bool booleanComingFromPointer = (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);
}

int implicitConversionReturnInt()
{
    return true;
    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'bool' -> 'int'
    // CHECK-FIXES: return 1
}

int implicitConversionReturnIntWithParens()
{
    return (true);
    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'bool' -> 'int'
    // CHECK-FIXES: return 1
}

bool implicitConversionReturnBool()
{
    return 1;
    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'int' -> 'bool'
    // CHECK-FIXES: return true
}

bool implicitConversionReturnBoolWithParens()
{
    return (1);
    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'int' -> 'bool'
    // CHECK-FIXES: return true
}

int keepCompactReturnInC_PR71848() {
  bool foo = false;
  return( foo );
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit conversion 'bool' -> 'int' [readability-implicit-bool-conversion]
// CHECK-FIXES: return(int)( foo );
}
