// RUN: %check_clang_tidy %s readability-suspicious-call-argument %t -- -- -std=c++11 void foo_1(int aaaaaa, int bbbbbb) {} void foo_2(int source, int aaaaaa) {} void foo_3(int valToRet, int aaaaaa) {} void foo_4(int pointer, int aaaaaa) {} void foo_5(int aaaaaa, int bbbbbb, int cccccc, ...) {} void foo_6(const int dddddd, bool &eeeeee) {} void foo_7(int aaaaaa, int bbbbbb, int cccccc, int ffffff = 7) {} void foo_8(int frobble1, int frobble2) {} // Test functions for convertible argument--parameter types. void fun(const int &m); void fun2() { int m = 3; fun(m); } // Test cases for parameters of const reference and value. void value_const_reference(int llllll, const int &kkkkkk); void const_ref_value_swapped() { const int &kkkkkk = 42; const int &llllll = 42; value_const_reference(kkkkkk, llllll); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'kkkkkk' (passed to 'llllll') looks like it might be swapped with the 2nd, 'llllll' (passed to 'kkkkkk') [readability-suspicious-call-argument] // CHECK-MESSAGES: :[[@LINE-7]]:6: note: in the call to 'value_const_reference', declared here } // Const, non const references. void const_nonconst_parameters(const int &mmmmmm, int &nnnnnn); void const_nonconst_swap1() { const int &nnnnnn = 42; int mmmmmm; // Do not check, because non-const reference parameter cannot bind to const reference argument. const_nonconst_parameters(nnnnnn, mmmmmm); } void const_nonconst_swap3() { const int nnnnnn = 42; int m = 42; int &mmmmmm = m; // Do not check, const int does not bind to non const reference. const_nonconst_parameters(nnnnnn, mmmmmm); } void const_nonconst_swap2() { int nnnnnn; int mmmmmm; // Check for swapped arguments. (Both arguments are non-const.) const_nonconst_parameters(nnnnnn, mmmmmm); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'nnnnnn' (passed to 'mmmmmm') looks like it might be swapped with the 2nd, 'mmmmmm' (passed to 'nnnnnn') } void const_nonconst_pointers(const int *mmmmmm, int *nnnnnn); void const_nonconst_pointers2(const int *mmmmmm, const int *nnnnnn); void const_nonconst_pointers_swapped() { int *mmmmmm; const int *nnnnnn; const_nonconst_pointers(nnnnnn, mmmmmm); } void const_nonconst_pointers_swapped2() { const int *mmmmmm; int *nnnnnn; const_nonconst_pointers2(nnnnnn, mmmmmm); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'nnnnnn' (passed to 'mmmmmm') looks like it might be swapped with the 2nd, 'mmmmmm' (passed to 'nnnnnn') } // Test cases for pointers and arrays. void pointer_array_parameters( int *pppppp, int qqqqqq[4]); void pointer_array_swap() { int qqqqqq[5]; int *pppppp; // Check for swapped arguments. An array implicitly converts to a pointer. pointer_array_parameters(qqqqqq, pppppp); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'qqqqqq' (passed to 'pppppp') looks like it might be swapped with the 2nd, 'pppppp' (passed to 'qqqqqq') } // Test cases for multilevel pointers. void multilevel_pointer_parameters(int *const **pppppp, const int *const *volatile const *qqqqqq); void multilevel_pointer_parameters2( char *****nnnnnn, char *volatile *const *const *const *const &mmmmmm); typedef float T; typedef T *S; typedef S *const volatile R; typedef R *Q; typedef Q *P; typedef P *O; void multilevel_pointer_parameters3(float **const volatile ***rrrrrr, O &ssssss); void multilevel_pointer_swap() { int *const **qqqqqq; int *const **pppppp; multilevel_pointer_parameters(qqqqqq, pppppp); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'qqqqqq' (passed to 'pppppp') looks like it might be swapped with the 2nd, 'pppppp' (passed to 'qqqqqq') char *****mmmmmm; char *****nnnnnn; multilevel_pointer_parameters2(mmmmmm, nnnnnn); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'mmmmmm' (passed to 'nnnnnn') looks like it might be swapped with the 2nd, 'nnnnnn' (passed to 'mmmmmm') float **const volatile ***rrrrrr; float **const volatile ***ssssss; multilevel_pointer_parameters3(ssssss, rrrrrr); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'ssssss' (passed to 'rrrrrr') looks like it might be swapped with the 2nd, 'rrrrrr' (passed to 'ssssss') } void multilevel_pointer_parameters4(char ****pppppp, char *const volatile **const *qqqqqq); void multilevel_pointer_parameters5( bool *****nnnnnn, bool *volatile *const *const *const *&mmmmmm); void multilevel_pointer_parameters6(double **llllll, char **&kkkkkk); void multilevel_pointer_parameters7(const volatile int ***iiiiii, const int *const *const *jjjjjj); void multilevel_pointer_swap3() { char ****qqqqqq; char *const volatile **const *pppppp; // Do not check. multilevel_pointer_parameters4(qqqqqq, pppppp); bool *****mmmmmm; bool *volatile *const *const *const *nnnnnn; // Do not check. multilevel_pointer_parameters5(mmmmmm, nnnnnn); double **kkkkkk; char **llllll; multilevel_pointer_parameters6(kkkkkk, llllll); const volatile int ***jjjjjj; const int *const *const *iiiiii; multilevel_pointer_parameters7(jjjjjj, iiiiii); } // Test cases for multidimesional arrays. void multilevel_array_parameters(int pppppp[2][2][2], const int qqqqqq[][2][2]); void multilevel_array_parameters2(int (*mmmmmm)[2][2], int nnnnnn[9][2][23]); void multilevel_array_parameters3(int (*eeeeee)[2][2], int (&ffffff)[1][2][2]); void multilevel_array_parameters4(int (*llllll)[2][2], int kkkkkk[2][2]); void multilevel_array_parameters5(int iiiiii[2][2], char jjjjjj[2][2]); void multilevel_array_parameters6(int (*bbbbbb)[2][2], int cccccc[1][2][2]); void multilevel_array_swap() { int qqqqqq[1][2][2]; int pppppp[][2][2] = {{{1, 2}, {1, 2}}, {{1, 2}, {1, 2}}}; // int [2][2][2] multilevel_array_parameters(qqqqqq, pppppp); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'qqqqqq' (passed to 'pppppp') looks like it might be swapped with the 2nd, 'pppppp' (passed to 'qqqqqq') int(*nnnnnn)[2][2]; int mmmmmm[9][2][23]; // Do not check, array sizes has to match in every dimension, except the first. multilevel_array_parameters2(nnnnnn, mmmmmm); int ffffff[][2][2] = {{{1, 2}, {1, 2}}, {{1, 2}, {1, 2}}}; // int [2][2][2] int eeeeee[1][2][2] = {{{1, 2}, {1, 2}}}; // int [1][2][2] // Do not check, for array references, size has to match in every dimension. multilevel_array_parameters3(ffffff, eeeeee); int kkkkkk[2][2][2]; int(*llllll)[2]; // Do not check, argument dimensions differ. multilevel_array_parameters4(kkkkkk, llllll); int jjjjjj[2][2]; char iiiiii[2][2]; // Do not check, array element types differ. multilevel_array_parameters5(jjjjjj, iiiiii); int t[][2][2] = {{{1, 2}, {1, 2}}, {{1, 2}, {1, 2}}}; // int [2][2][2] int(*cccccc)[2][2] = t; // int (*)[2][2] int bbbbbb[][2][2] = {{{1, 2}, {1, 2}}}; // int [1][2][2] multilevel_array_parameters6(cccccc, bbbbbb); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'bbbbbb') looks like it might be swapped with the 2nd, 'bbbbbb' (passed to 'cccccc') } void multilevel_array_swap2() { int qqqqqq[2][2][2]; const int pppppp[][2][2] = {{{1, 2}, {1, 2}}, {{1, 2}, {1, 2}}}; // Do not check, pppppp is const and cannot bind to an array with nonconst elements. multilevel_array_parameters(qqqqqq, pppppp); } // Complex test case. void multilevel_pointer_array_parameters(const int(*const (*volatile const (*const (*const (*const &aaaaaa)[1])[32])[4])[3][2][2]), const int(*const (*volatile const (*const (*const (*&bbbbbb)[1])[32])[4])[3][2][2])); void multilevel_pointer_array_swap() { const int( *const(*volatile const(*const(*const(*aaaaaa)[1])[32])[4])[3][2][2]); const int( *const(*volatile const(*const(*const(*bbbbbb)[1])[32])[4])[3][2][2]); multilevel_pointer_array_parameters(bbbbbb, aaaaaa); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'bbbbbb' (passed to 'aaaaaa') looks like it might be swapped with the 2nd, 'aaaaaa' (passed to 'bbbbbb') } enum class numbers_scoped { one, two }; // Test cases for arithmetic types. void arithmetic_type_parameters(float vvvvvv, int wwwwww); void arithmetic_type_parameters2(numbers_scoped vvvvvv, int wwwwww); void arithmetic_types_swap1() { bool wwwwww; float vvvvvv; arithmetic_type_parameters(wwwwww, vvvvvv); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'wwwwww' (passed to 'vvvvvv') looks like it might be swapped with the 2nd, 'vvvvvv' (passed to 'wwwwww') } void arithmetic_types_swap3() { char wwwwww; unsigned long long int vvvvvv; arithmetic_type_parameters(wwwwww, vvvvvv); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'wwwwww' (passed to 'vvvvvv') looks like it might be swapped with the 2nd, 'vvvvvv' (passed to 'wwwwww') } void arithmetic_types_swap4() { enum numbers { one, two }; numbers wwwwww = numbers::one; int vvvvvv; arithmetic_type_parameters(wwwwww, vvvvvv); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'wwwwww' (passed to 'vvvvvv') looks like it might be swapped with the 2nd, 'vvvvvv' (passed to 'wwwwww') } void arithmetic_types_swap5() { wchar_t vvvvvv; float wwwwww; arithmetic_type_parameters(wwwwww, vvvvvv); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'wwwwww' (passed to 'vvvvvv') looks like it might be swapped with the 2nd, 'vvvvvv' (passed to 'wwwwww') } void arithmetic_types_swap6() { wchar_t vvvvvv; numbers_scoped wwwwww = numbers_scoped::one; // Do not check, numers is a scoped enum type. arithmetic_type_parameters2(wwwwww, vvvvvv); } // Base, derived class TestClass { public: void thisFunction(int integerParam, int thisIsPARAM) {} }; class DerivedTestClass : public TestClass {}; void base_derived_pointer_parameters(TestClass *aaaaaa, DerivedTestClass *bbbbbb); void base_derived_swap1() { TestClass *bbbbbb; DerivedTestClass *aaaaaa; // Do not check, because TestClass does not convert implicitly to DerivedTestClass. base_derived_pointer_parameters(bbbbbb, aaaaaa); } void base_derived_swap2() { DerivedTestClass *bbbbbb, *aaaaaa; // Check for swapped arguments, DerivedTestClass converts to TestClass implicitly. base_derived_pointer_parameters(bbbbbb, aaaaaa); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'bbbbbb' (passed to 'aaaaaa') looks like it might be swapped with the 2nd, 'aaaaaa' (passed to 'bbbbbb') } class PrivateDerivedClass : private TestClass {}; void private_derived_pointer_parameters(TestClass *aaaaaa, PrivateDerivedClass *bbbbbb); void private_base_swap1() { TestClass *bbbbbb; PrivateDerivedClass *aaaaaa; private_derived_pointer_parameters(bbbbbb, aaaaaa); } // Multilevel inheritance class DerivedOfDerivedTestClass : public DerivedTestClass {}; void multi_level_inheritance_swap() { DerivedOfDerivedTestClass *aaaaaa, *bbbbbb; // Check for swapped arguments. Derived classes implicitly convert to their base. base_derived_pointer_parameters( bbbbbb, aaaaaa); // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: 1st argument 'bbbbbb' (passed to 'aaaaaa') looks like it might be swapped with the 2nd, 'aaaaaa' (passed to 'bbbbbb') } // Tests for function pointer swaps void funct_ptr_params(double (*ffffff)(int, int), double (*gggggg)(int, int)); void funct_ptr_params(double (*ffffff)(int, int), int (*gggggg)(int, int)); double ffffff(int a, int b) { return 0; } double gggggg(int a, int b) { return 0; } void funtionc_ptr_params_swap() { funct_ptr_params(gggggg, ffffff); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'gggggg' (passed to 'ffffff') looks like it might be swapped with the 2nd, 'ffffff' (passed to 'gggggg') } int fffff(int a, int b) { return 0; } void function_ptr_swap2() { // Do not check, because the function `ffffff` cannot convert to a function // with prototype: double(int,int). funct_ptr_params(gggggg, fffff); } // Paraphrased example from Z3 (src/qe/qe_arrays.cpp) which originally produced // a false positive. Operator() calls should ignore the called object // "argument". struct type1; struct type2; struct type3; struct callable1 { void operator()(type1 &mdl, type2 &arr_vars, type3 &fml, type2 &aux_vars) const {} }; struct callable2 { void operator()(type1 &mdl, type2 &arr_vars, type3 &fml, type2 &aux_vars, bool reduce_all_selects) const { (void)reduce_all_selects; callable1 pe; pe(mdl, arr_vars, fml, aux_vars); // NO-WARN: Argument and parameter names match perfectly, "pe" should be // ignored! } }; struct binop_t {}; binop_t operator+(const binop_t &lhs, const binop_t &rhs) { return lhs; } bool operator<(const binop_t &lhs, const binop_t &rhs) { return true; } bool operator>(const binop_t &aaaaaa, const binop_t &bbbbbb) { return false; } void binop_test() { // NO-WARN: Binary operators are ignored. binop_t lhs, rhs; if (lhs + rhs < rhs) return; if (operator<(rhs, lhs)) return; binop_t aaaaaa, cccccc; if (operator>(cccccc, aaaaaa)) return; } int recursion(int aaaa, int bbbb) { if (aaaa) return 0; int cccc = 0; return recursion(bbbb, cccc); // NO-WARN: Recursive calls usually shuffle with arguments and we ignore those. } void pass_by_copy(binop_t xxxx, binop_t yyyy) {} // Paraphrased example from LLVM's code (lib/Analysis/InstructionSimplify.cpp) // that generated a false positive. struct value; enum opcode { Foo, Bar }; static value *SimplifyRightShift( opcode Opcode, value *Op0, value *Op1, bool isExact, const type1 &Q, unsigned MaxRecurse) {} static value *SimplifyLShrInst(value *Op0, value *Op1, bool isExact, const type1 &Q, unsigned MaxRecurse) { if (value *V = SimplifyRightShift(Foo, Op0, Op1, isExact, Q, MaxRecurse)) return V; // NO-WARN: Argument names perfectly match parameter names, sans the enum. return nullptr; } void has_unnamed(int aaaaaa, int) {} int main() { // Equality test. int aaaaaa, cccccc = 0; foo_1(cccccc, aaaaaa); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'aaaaaa') looks like it might be swapped with the 2nd, 'aaaaaa' (passed to 'bbbbbb') binop_t xxxx, yyyy; pass_by_copy(yyyy, xxxx); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'yyyy' (passed to 'xxxx') looks like it might be swapped with the 2nd, 'xxxx' (passed to 'yyyy') // Abbreviation test. int src = 0; foo_2(aaaaaa, src); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'aaaaaa' (passed to 'source') looks like it might be swapped with the 2nd, 'src' (passed to 'aaaaaa') // Levenshtein test. int aaaabb = 0; foo_1(cccccc, aaaabb); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'aaaaaa') looks like it might be swapped with the 2nd, 'aaaabb' (passed to 'bbbbbb') // Prefix test. int aaaa = 0; foo_1(cccccc, aaaa); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'aaaaaa') looks like it might be swapped with the 2nd, 'aaaa' (passed to 'bbbbbb') // Suffix test. int urce = 0; foo_2(cccccc, urce); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'source') looks like it might be swapped with the 2nd, 'urce' (passed to 'aaaaaa') // Substring test. int ourc = 0; foo_2(cccccc, ourc); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'source') looks like it might be swapped with the 2nd, 'ourc' (passed to 'aaaaaa') // Jaro-Winkler test. int iPonter = 0; foo_4(cccccc, iPonter); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'pointer') looks like it might be swapped with the 2nd, 'iPonter' (passed to 'aaaaaa') // Dice test. int aaabaa = 0; foo_1(cccccc, aaabaa); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'aaaaaa') looks like it might be swapped with the 2nd, 'aaabaa' (passed to 'bbbbbb') // Variadic function test. int bbbbbb = 0; foo_5(src, bbbbbb, cccccc, aaaaaa); // Should pass. foo_5(cccccc, bbbbbb, aaaaaa, src); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'aaaaaa') looks like it might be swapped with the 3rd, 'aaaaaa' (passed to 'cccccc') // Test function with default argument. foo_7(src, bbbbbb, cccccc, aaaaaa); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'src' (passed to 'aaaaaa') looks like it might be swapped with the 4th, 'aaaaaa' (passed to 'ffffff') foo_7(cccccc, bbbbbb, aaaaaa, src); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'cccccc' (passed to 'aaaaaa') looks like it might be swapped with the 3rd, 'aaaaaa' (passed to 'cccccc') int ffffff = 0; foo_7(ffffff, bbbbbb, cccccc); // NO-WARN: Even though 'ffffff' is passed to 'aaaaaa' and there is a 4th parameter 'ffffff', there isn't a **swap** here. int frobble1 = 1, frobble2 = 2; foo_8(frobble2, frobble1); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'frobble2' (passed to 'frobble1') looks like it might be swapped with the 2nd, 'frobble1' (passed to 'frobble2') int bar1 = 1, bar2 = 2; foo_8(bar2, bar1); // NO-WARN. // Type match bool dddddd = false; int eeeeee = 0; auto szam = 0; foo_6(eeeeee, dddddd); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'eeeeee' (passed to 'dddddd') looks like it might be swapped with the 2nd, 'dddddd' (passed to 'eeeeee') foo_1(szam, aaaaaa); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'szam' (passed to 'aaaaaa') looks like it might be swapped with the 2nd, 'aaaaaa' (passed to 'bbbbbb') // Test lambda. auto testMethod = [&](int method, int randomParam) { return 0; }; int method = 0; testMethod(method, 0); // Should pass. // Member function test. TestClass test; int integ, thisIsAnArg = 0; test.thisFunction(integ, thisIsAnArg); // Should pass. has_unnamed(1, bbbbbb); return 0; } namespace Issue_54074 { class T {}; using OperatorTy = int(const T &, const T &); int operator-(const T &, const T &); template struct Wrap { Wrap(U); }; template void wrapTaker(V, Wrap); template void wrapTaker(V aaaaa, V bbbbb, Wrap); void test() { wrapTaker(0, operator-); // NO-WARN. No crash! int aaaaa = 4, bbbbb = 8; wrapTaker(bbbbb, aaaaa, operator-); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 1st argument 'bbbbb' (passed to 'aaaaa') looks like it might be swapped with the 2nd, 'aaaaa' (passed to 'bbbbb') // CHECK-MESSAGES: :[[@LINE-9]]:6: note: in the call to 'wrapTaker', declared here } } // namespace Issue_54074