// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \
// RUN:   -config='{CheckOptions: [ \
// RUN:     {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
// RUN:     {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
// RUN:     {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN:     {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0}, \
// RUN:     {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 0}, \
// RUN:     {key: bugprone-easily-swappable-parameters.SuppressParametersUsedTogether, value: 1}, \
// RUN:     {key: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold, value: 0} \
// RUN:  ]}' --

namespace std {
template <typename T>
T max(const T &A, const T &B);
} // namespace std

bool coin();
void f(int);
void g(int);
void h(int, int);
void i(int, bool);
void i(int, char);

struct Tmp {
  int f(int);
  int g(int, int);
};

struct Int {
  int I;
};

void compare(int Left, int Right) {}
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 2 adjacent parameters of 'compare' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in the range is 'Left'
// CHECK-MESSAGES: :[[@LINE-3]]:28: note: the last parameter in the range is 'Right'

int decideSequence(int A, int B) {
  if (A)
    return 1;
  if (B)
    return 2;
  return 3;
}
// CHECK-MESSAGES: :[[@LINE-7]]:20: warning: 2 adjacent parameters of 'decideSequence' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-8]]:24: note: the first parameter in the range is 'A'
// CHECK-MESSAGES: :[[@LINE-9]]:31: note: the last parameter in the range is 'B'

int myMax(int A, int B) { // NO-WARN: Appears in same expression.
  return A < B ? A : B;
}

int myMax2(int A, int B) { // NO-WARN: Appears in same expression.
  if (A < B)
    return A;
  return B;
}

int myMax3(int A, int B) { // NO-WARN: Appears in same expression.
  return std::max(A, B);
}

int binaryToUnary(int A, int) {
  return A;
}
// CHECK-MESSAGES: :[[@LINE-3]]:19: warning: 2 adjacent parameters of 'binaryToUnary' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-4]]:23: note: the first parameter in the range is 'A'
// CHECK-MESSAGES: :[[@LINE-5]]:29: note: the last parameter in the range is '<unnamed>'

int randomReturn1(int A, int B) { // NO-WARN: Appears in same expression.
  return coin() ? A : B;
}

int randomReturn2(int A, int B) { // NO-WARN: Both parameters returned.
  if (coin())
    return A;
  return B;
}

int randomReturn3(int A, int B) { // NO-WARN: Both parameters returned.
  bool Flip = coin();
  if (Flip)
    return A;
  Flip = coin();
  if (Flip)
    return B;
  Flip = coin();
  if (!Flip)
    return 0;
  return -1;
}

void passthrough1(int A, int B) { // WARN: Different functions, different params.
  f(A);
  g(B);
}
// CHECK-MESSAGES: :[[@LINE-4]]:19: warning: 2 adjacent parameters of 'passthrough1' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-5]]:23: note: the first parameter in the range is 'A'
// CHECK-MESSAGES: :[[@LINE-6]]:30: note: the last parameter in the range is 'B'

void passthrough2(int A, int B) { // NO-WARN: Passed to same index of same function.
  f(A);
  f(B);
}

void passthrough3(int A, int B) { // NO-WARN: Passed to same index of same funtion.
  h(1, A);
  h(1, B);
}

void passthrough4(int A, int B) { // WARN: Different index used.
  h(1, A);
  h(B, 2);
}
// CHECK-MESSAGES: :[[@LINE-4]]:19: warning: 2 adjacent parameters of 'passthrough4' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-5]]:23: note: the first parameter in the range is 'A'
// CHECK-MESSAGES: :[[@LINE-6]]:30: note: the last parameter in the range is 'B'

void passthrough5(int A, int B) { // WARN: Different function overload.
  i(A, false);
  i(A, '\0');
}
// CHECK-MESSAGES: :[[@LINE-4]]:19: warning: 2 adjacent parameters of 'passthrough5' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-5]]:23: note: the first parameter in the range is 'A'
// CHECK-MESSAGES: :[[@LINE-6]]:30: note: the last parameter in the range is 'B'

void passthrough6(int A, int B) { // NO-WARN: Passed to same index of same function.
  Tmp Temp;
  Temp.f(A);
  Temp.f(B);
}

void passthrough7(int A, int B) { // NO-WARN: Passed to same index of same function.
  // Clang-Tidy isn't path sensitive, the fact that the two objects we call the
  // function on is different is not modelled.
  Tmp Temp1, Temp2;
  Temp1.f(A);
  Temp2.f(B);
}

void passthrough8(int A, int B) { // WARN: Different functions used.
  f(A);
  Tmp{}.f(B);
}
// CHECK-MESSAGES: :[[@LINE-4]]:19: warning: 2 adjacent parameters of 'passthrough8' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-5]]:23: note: the first parameter in the range is 'A'
// CHECK-MESSAGES: :[[@LINE-6]]:30: note: the last parameter in the range is 'B'

// Test that the matching of "passed-to-function" is done to the proper node.
// Put simply, this test should not crash here.
void forwardDeclared(int X);

void passthrough9(int A, int B) { // NO-WARN: Passed to same index of same function.
  forwardDeclared(A);
  forwardDeclared(B);
}

void forwardDeclared(int X) {}

void passthrough10(int A, int B) { // NO-WARN: Passed to same index of same function.
  forwardDeclared(A);
  forwardDeclared(B);
}

bool compare1(Int I, Int J) { // NO-WARN: Same member accessed.
  int Val1 = I.I;
  int Val2 = J.I;
  return Val1 < Val2;
}

bool compare2(Tmp T1, Tmp T2) { // NO-WARN: Same member accessed.
  int Val1 = T1.g(0, 1);
  int Val2 = T2.g(2, 3);
  return Val1 < Val2;
}

bool compare3(Tmp T1, Tmp T2) { // WARN: Different member accessed.
  int Val1 = T1.f(0);
  int Val2 = T2.g(1, 2);
  return Val1 < Val2;
}
// CHECK-MESSAGES: :[[@LINE-5]]:15: warning: 2 adjacent parameters of 'compare3' of similar type ('Tmp')
// CHECK-MESSAGES: :[[@LINE-6]]:19: note: the first parameter in the range is 'T1'
// CHECK-MESSAGES: :[[@LINE-7]]:27: note: the last parameter in the range is 'T2'

int rangeBreaker(int I, int J, int K, int L, int M, int N) {
  // (I, J) swappable.

  if (J == K) // (J, K) related.
    return -1;

  if (K + 2 > Tmp{}.f(K))
    return M;

  // (K, L, M) swappable.

  return N; // (M, N) related.
}
// CHECK-MESSAGES: :[[@LINE-13]]:18: warning: 2 adjacent parameters of 'rangeBreaker' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-14]]:22: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-15]]:29: note: the last parameter in the range is 'J'
// CHECK-MESSAGES: :[[@LINE-16]]:32: warning: 3 adjacent parameters of 'rangeBreaker' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-17]]:36: note: the first parameter in the range is 'K'
// CHECK-MESSAGES: :[[@LINE-18]]:50: note: the last parameter in the range is 'M'

int returnsNotOwnParameter(int I, int J, int K) {
  const auto &Lambda = [&K](int L, int M, int N) {
    if (K)
      return L;
    return M; // (L, M) related.
  };

  if (Lambda(-1, 0, 1))
    return I;
  return J; // (I, J) related.
}
// CHECK-MESSAGES: :[[@LINE-11]]:35: warning: 2 adjacent parameters of 'returnsNotOwnParameter' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-12]]:39: note: the first parameter in the range is 'J'
// CHECK-MESSAGES: :[[@LINE-13]]:46: note: the last parameter in the range is 'K'
// CHECK-MESSAGES: :[[@LINE-13]]:36: warning: 2 adjacent parameters of 'operator()' of similar type ('int')
// CHECK-MESSAGES: :[[@LINE-14]]:40: note: the first parameter in the range is 'M'
// CHECK-MESSAGES: :[[@LINE-15]]:47: note: the last parameter in the range is 'N'

int usedTogetherInCapture(int I, int J, int K) { // NO-WARN: Used together.
  const auto &Lambda = [I, J, K]() {
    int A = I + 1;
    int B = J - 2;
    int C = K * 3;
    return A + B + C;
  };
  return Lambda();
}
