// RUN: %check_clang_tidy %s hicpp-signed-bitwise %t -- -- --target=x86_64-linux

// These could cause false positives and should not be considered.
struct StreamClass {
};
StreamClass &operator<<(StreamClass &os, unsigned int i) {
  return os;
}
StreamClass &operator<<(StreamClass &os, int i) {
  return os;
}
StreamClass &operator>>(StreamClass &os, unsigned int i) {
  return os;
}
StreamClass &operator>>(StreamClass &os, int i) {
  return os;
}
struct AnotherStream {
  AnotherStream &operator<<(unsigned char c) { return *this; }
  AnotherStream &operator<<(signed char c) { return *this; }

  AnotherStream &operator>>(unsigned char c) { return *this; }
  AnotherStream &operator>>(signed char c) { return *this; }
};

void binary_bitwise() {
  int SValue = 42;
  int SResult;

  unsigned int UValue = 42;
  unsigned int UResult;

  SResult = SValue & 1;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator
  SResult = SValue & -1;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator
  SResult = SValue & SValue;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator

  UResult = SValue & 1;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator
  UResult = SValue & -1;
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator
  UResult&= 1;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use of a signed integer operand with a binary bitwise operator

  UResult = UValue & 1u;     // Ok
  UResult = UValue & UValue; // Ok
  UResult&= 2u;              // Ok

  unsigned char UByte1 = 0u;
  unsigned char UByte2 = 16u;
  signed char SByte1 = 0;
  signed char SByte2 = 16;

  UByte1 = UByte1 & UByte2; // Ok
  UByte1 = SByte1 & UByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator
  UByte1 = SByte1 & SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator
  SByte1 = SByte1 & SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator

  // More complex expressions.
  UResult = UValue & (SByte1 + (SByte1 | SByte2));
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator
  // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: use of a signed integer operand with a binary bitwise operator

  // The rest is to demonstrate functionality but all operators are matched equally.
  // Therefore functionality is the same for all binary operations.
  UByte1 = UByte1 | UByte2; // Ok
  UByte1 = UByte1 | SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator
  UByte1|= SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use of a signed integer operand with a binary bitwise operator
  UByte1|= UByte2; // Ok

  UByte1 = UByte1 ^ UByte2; // Ok
  UByte1 = UByte1 ^ SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator
  UByte1^= SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use of a signed integer operand with a binary bitwise operator
  UByte1^= UByte2; // Ok

  UByte1 = UByte1 >> UByte2; // Ok
  UByte1 = UByte1 >> SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator
  UByte1>>= SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use of a signed integer operand with a binary bitwise operator
  UByte1>>= UByte2; // Ok

  UByte1 = UByte1 << UByte2; // Ok
  UByte1 = UByte1 << SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator
  UByte1<<= SByte2;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use of a signed integer operand with a binary bitwise operator
  UByte1<<= UByte2; // Ok

  int SignedInt1 = 1 << 12;
  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use of a signed integer operand with a binary bitwise operator
  int SignedInt2 = 1u << 12;
  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use of a signed integer operand with a binary bitwise operator
}

void f1(unsigned char c) {}
void f2(signed char c) {}
void f3(int c) {}

void unary_bitwise() {
  unsigned char UByte1 = 0u;
  signed char SByte1 = 0;

  UByte1 = ~UByte1; // Ok
  SByte1 = ~UByte1;
  SByte1 = ~SByte1;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a unary bitwise operator
  UByte1 = ~SByte1;
  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a unary bitwise operator

  unsigned int UInt = 0u;
  int SInt = 0;

  f1(~UByte1); // Ok
  f1(~SByte1);
  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator
  f1(~UInt);
  f1(~SInt);
  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator
  f2(~UByte1);
  f2(~SByte1);
  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator
  f2(~UInt);
  f2(~SInt);
  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator
  f3(~UByte1); // Ok
  f3(~SByte1);
  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator
}

/// HICPP uses these examples to demonstrate the rule.
void standard_examples() {
  int i = 3;
  unsigned int k = 0u;

  int r = i << -1; // Emits -Wshift-count-negative from clang
  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use of a signed integer operand with a binary bitwise operator
  r = i << 1;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator

  r = -1 >> -1; // Emits -Wshift-count-negative from clang
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator
  r = -1 >> 1;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator

  r = -1 >> i;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator
  r = -1 >> -i;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator

  r = ~0;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a unary bitwise operator
  r = ~0u; // Ok
  k = ~k;  // Ok

  unsigned int u = (-1) & 2u;
  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use of a signed integer operand with a binary bitwise operator
  u = (-1) | 1u;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator
  u = (-1) ^ 1u;
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator
}

void streams_should_work() {
  StreamClass s;
  s << 1u; // Ok
  s << 1;  // Ok
  s >> 1;  // Ok
  s >> 1u; // Ok

  AnotherStream as;
  unsigned char uc = 1u;
  signed char sc = 1;
  as << uc; // Ok
  as << sc; // Ok
  as >> uc; // Ok
  as >> sc; // Ok
}

enum OldEnum {
  ValueOne,
  ValueTwo,
};

enum OldSigned : int {
  IntOne,
  IntTwo,
};

void classicEnums() {
  OldEnum e1 = ValueOne, e2 = ValueTwo;
  int e3;                   // Using the enum type, results in an error.
  e3 = ValueOne | ValueTwo; // Ok
  e3 = ValueOne & ValueTwo; // Ok
  e3 = ValueOne ^ ValueTwo; // Ok
  e3 = e1 | e2;             // Ok
  e3 = e1 & e2;             // Ok
  e3 = e1 ^ e2;             // Ok

  OldSigned s1 = IntOne, s2 = IntTwo;
  int s3;
  s3 = IntOne | IntTwo; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator
  s3|= IntTwo; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: use of a signed integer operand with a binary bitwise operator
  s3 = IntOne & IntTwo; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator
  s3&= IntTwo; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: use of a signed integer operand with a binary bitwise operator
  s3 = IntOne ^ IntTwo; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator
  s3^= IntTwo; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: use of a signed integer operand with a binary bitwise operator
  s3 = s1 | s2; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator
  s3 = s1 & s2; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator
  s3 = s1 ^ s2; // Signed
  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator
}

enum EnumConstruction {
  one = 1,
  two = 2,
  test1 = 1 << 12,
  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: use of a signed integer operand with a binary bitwise operator
  test2 = one << two,
  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: use of a signed integer operand with a binary bitwise operator
  test3 = 1u << 12,
  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: use of a signed integer operand with a binary bitwise operator
};
