whisperity updated this revision to Diff 259508.
whisperity retitled this revision from "[clang-tidy] Suspicious Call Argument
checker" to "[clang-tidy] Add 'readability-suspicious-call-argument' check".
whisperity edited the summary of this revision.
whisperity removed reviewers: varjujan, barancsuk.
whisperity set the repository for this revision to rG LLVM Github Monorepo.
whisperity edited subscribers, added: varjujan, zporky; removed: gsd.
whisperity added a comment.
Herald added a project: clang.
First things first, we were 50 thousand (!) patches behind reality. Rebased to
`master`. Fixed it to compile, too. Otherwise, NFC so far.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D20689/new/
https://reviews.llvm.org/D20689
Files:
clang-tools-extra/clang-tidy/readability/CMakeLists.txt
clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/docs/clang-tidy/checks/readability-suspicious-call-argument.rst
clang-tools-extra/test/clang-tidy/checkers/readability-suspicious-call-argument.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/readability-suspicious-call-argument.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/readability-suspicious-call-argument.cpp
@@ -0,0 +1,392 @@
+// 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) {}
+
+// 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: kkkkkk (llllll) is swapped with llllll (kkkkkk). [readability-suspicious-call-argument]
+}
+
+// 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-3]]:3: warning: nnnnnn (mmmmmm) is swapped with mmmmmm (nnnnnn). [readability-suspicious-call-argument]
+}
+
+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: nnnnnn (mmmmmm) is swapped with mmmmmm (nnnnnn). [readability-suspicious-call-argument]
+}
+
+// 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: qqqqqq (pppppp) is swapped with pppppp (qqqqqq). [readability-suspicious-call-argument]
+}
+
+// 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: qqqqqq (pppppp) is swapped with pppppp (qqqqqq). [readability-suspicious-call-argument]
+
+ char *****mmmmmm;
+ char *****nnnnnn;
+ multilevel_pointer_parameters2(mmmmmm, nnnnnn);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: mmmmmm (nnnnnn) is swapped with nnnnnn (mmmmmm). [readability-suspicious-call-argument]
+
+ float **const volatile ***rrrrrr;
+ float **const volatile ***ssssss;
+ multilevel_pointer_parameters3(ssssss, rrrrrr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: ssssss (rrrrrr) is swapped with rrrrrr (ssssss). [readability-suspicious-call-argument]
+}
+
+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: qqqqqq (pppppp) is swapped with pppppp (qqqqqq). [readability-suspicious-call-argument]
+
+ 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: cccccc (bbbbbb) is swapped with bbbbbb (cccccc). [readability-suspicious-call-argument]
+}
+
+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: bbbbbb (aaaaaa) is swapped with aaaaaa (bbbbbb). [readability-suspicious-call-argument]
+}
+
+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: wwwwww (vvvvvv) is swapped with vvvvvv (wwwwww). [readability-suspicious-call-argument]
+}
+
+void arithmetic_types_swap3() {
+ char wwwwww;
+ unsigned long long int vvvvvv;
+ arithmetic_type_parameters(wwwwww, vvvvvv);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: wwwwww (vvvvvv) is swapped with vvvvvv (wwwwww). [readability-suspicious-call-argument]
+}
+
+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: wwwwww (vvvvvv) is swapped with vvvvvv (wwwwww). [readability-suspicious-call-argument]
+}
+
+void arithmetic_types_swap5() {
+ wchar_t vvvvvv;
+ float wwwwww;
+ arithmetic_type_parameters(wwwwww, vvvvvv);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: wwwwww (vvvvvv) is swapped with vvvvvv (wwwwww). [readability-suspicious-call-argument]
+}
+
+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: bbbbbb (aaaaaa) is swapped with aaaaaa (bbbbbb). [readability-suspicious-call-argument]
+}
+
+// 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: bbbbbb (aaaaaa) is swapped with aaaaaa (bbbbbb). [readability-suspicious-call-argument]
+}
+
+// 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: gggggg (ffffff) is swapped with ffffff (gggggg). [readability-suspicious-call-argument]
+}
+
+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);
+}
+
+int main() {
+
+ // Equality test.
+ int aaaaaa, cccccc = 0;
+ foo_1(cccccc, aaaaaa); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (aaaaaa) is swapped with aaaaaa (bbbbbb). [readability-suspicious-call-argument]
+
+ // Abbreviation test.
+ int src = 0;
+ foo_2(aaaaaa, src); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: aaaaaa (source) is swapped with src (aaaaaa). [readability-suspicious-call-argument]
+
+ // Levenshtein test.
+ int aaaabb = 0;
+ foo_1(cccccc, aaaabb); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (aaaaaa) is swapped with aaaabb (bbbbbb). [readability-suspicious-call-argument]
+
+ // Prefix test.
+ int aaaa = 0;
+ foo_1(cccccc, aaaa); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (aaaaaa) is swapped with aaaa (bbbbbb). [readability-suspicious-call-argument]
+
+ // Suffix test.
+ int urce = 0;
+ foo_2(cccccc, urce); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (source) is swapped with urce (aaaaaa). [readability-suspicious-call-argument]
+
+ // Substring test.
+ int ourc = 0;
+ foo_2(cccccc, ourc); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (source) is swapped with ourc (aaaaaa). [readability-suspicious-call-argument]
+
+ // Jaro-Winkler test.
+ int iPonter = 0;
+ foo_4(cccccc, iPonter); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (pointer) is swapped with iPonter (aaaaaa). [readability-suspicious-call-argument]
+
+ // Dice test.
+ int aaabaa = 0;
+ foo_1(cccccc, aaabaa); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (aaaaaa) is swapped with aaabaa (bbbbbb). [readability-suspicious-call-argument]
+
+ // Variadic function test.
+ int bbbbbb = 0;
+ foo_5(src, bbbbbb, cccccc, aaaaaa); // Should pass.
+ foo_5(cccccc, bbbbbb, aaaaaa, src); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (aaaaaa) is swapped with aaaaaa (cccccc). [readability-suspicious-call-argument]
+
+ // Test function with default argument.
+ foo_7(src, bbbbbb, cccccc, aaaaaa); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: src (aaaaaa) is swapped with aaaaaa (ffffff). [readability-suspicious-call-argument]
+
+ foo_7(cccccc, bbbbbb, aaaaaa, src); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: cccccc (aaaaaa) is swapped with aaaaaa (cccccc). [readability-suspicious-call-argument]
+
+ int ffffff = 0;
+ foo_7(ffffff, bbbbbb, cccccc); // Should fail.
+
+ // Type match
+ bool dddddd = false;
+ int eeeeee = 0;
+ auto szam = 0;
+ foo_6(eeeeee, dddddd); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: eeeeee (dddddd) is swapped with dddddd (eeeeee). [readability-suspicious-call-argument]
+ foo_1(szam, aaaaaa); // Should fail.
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: szam (aaaaaa) is swapped with aaaaaa (bbbbbb). [readability-suspicious-call-argument]
+
+ // 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.
+
+ return 0;
+}
Index: clang-tools-extra/docs/clang-tidy/checks/readability-suspicious-call-argument.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/readability-suspicious-call-argument.rst
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - readability-suspicious-call-argument
+
+readability-suspicious-call-argument
+====================================
+
+This checker finds those function calls where the function arguments are
+provided in an incorrect order. It compares the name of the given variable
+to the argument name in the function definition.
+
+It issues a message if the given variable name is similar to an another
+function argument in a function call. It uses case insensitive comparison.
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -295,6 +295,7 @@
`readability-static-accessed-through-instance <readability-static-accessed-through-instance.html>`_, "Yes"
`readability-static-definition-in-anonymous-namespace <readability-static-definition-in-anonymous-namespace.html>`_, "Yes"
`readability-string-compare <readability-string-compare.html>`_, "Yes"
+ `readability-suspicious-call-argument <readability-suspicious-call-argument.html>`_, "Yes"
`readability-uniqueptr-delete-release <readability-uniqueptr-delete-release.html>`_, "Yes"
`readability-uppercase-literal-suffix <readability-uppercase-literal-suffix.html>`_, "Yes"
`zircon-temporary-objects <zircon-temporary-objects.html>`_,
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -140,6 +140,12 @@
Finds calls to ``NSInvocation`` methods under ARC that don't have proper
argument object lifetimes.
+- New `readability-suspicious-call-argument
+ <clang-tidy/checks/readability-suspicious-call-argument>`_ check
+
+ This checker finds those function calls where the function arguments are
+ provided in an incorrect order.
+
New check aliases
^^^^^^^^^^^^^^^^^
Index: clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h
@@ -0,0 +1,97 @@
+//===--- SuspiciousCallArgumentCheck.h - clang-tidy--------------*- C -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUS_CALL_ARGUMENT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUS_CALL_ARGUMENT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// This checker finds those function calls where the function arguments are
+/// provided in an incorrect order. It compares the name of the given variable
+/// to the argument name in the function definition.
+/// It issues a message if the given variable name is similar to an another
+/// function argument in a function call. It uses case insensitive comparison.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-suspicious-call-argument.html
+class SuspiciousCallArgumentCheck : public ClangTidyCheck {
+public:
+ SuspiciousCallArgumentCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+ const static unsigned VectorSmallSize = 8;
+
+private:
+ enum class Heuristic {
+ Equality,
+ Abbreviation,
+ Prefix,
+ Suffix,
+ Levenshtein,
+ Substring,
+ JaroWinkler,
+ Dice
+ };
+
+ enum class Bound { Lower, Upper };
+
+ SmallVector<Heuristic, VectorSmallSize> AppliedHeuristics;
+
+ SmallVector<QualType, VectorSmallSize> ArgTypes;
+ SmallVector<StringRef, VectorSmallSize> ArgNames;
+ SmallVector<QualType, VectorSmallSize> ParamTypes;
+ SmallVector<StringRef, VectorSmallSize> ParamNames;
+
+ const bool Equality;
+ const bool Abbreviation;
+ const bool Levenshtein;
+ const bool Prefix;
+ const bool Suffix;
+ const bool Substring;
+ const bool JaroWinkler;
+ const bool Dice;
+
+ const int LevenshteinUpperBound;
+ const int PrefixUpperBound;
+ const int SuffixUpperBound;
+ const int SubstringUpperBound;
+ const int JaroWinklerUpperBound;
+ const int DiceUpperBound;
+
+ const int LevenshteinLowerBound;
+ const int PrefixLowerBound;
+ const int SuffixLowerBound;
+ const int SubstringLowerBound;
+ const int JaroWinklerLowerBound;
+ const int DiceLowerBound;
+
+ void setParamNamesAndTypes(const FunctionDecl *CalleeFuncDecl);
+
+ void setArgNamesAndTypes(const CallExpr *MatchedCallExpr,
+ unsigned InitialArgIndex);
+
+ bool areParamAndArgComparable(unsigned Position1, unsigned Position2,
+ const ASTContext *Ctx) const;
+
+ bool areArgsSwapped(unsigned Position1, unsigned Position2) const;
+
+ bool areNamesSimilar(StringRef Arg, StringRef Param, Bound bound) const;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUS_CALL_ARGUMENT_H
Index: clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
@@ -0,0 +1,690 @@
+//===--- SuspiciousCallArgumentCheck.cpp - clang-tidy----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousCallArgumentCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <sstream>
+
+using namespace clang::ast_matchers;
+
+namespace {
+
+llvm::StringMap<llvm::StringRef> AbbreviationDictionary;
+
+} // namespace
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+static bool applyEqualityHeuristic(StringRef Arg, StringRef Param) {
+ return Arg.equals_lower(Param);
+}
+
+static bool applyAbbreviationHeuristic(StringRef Arg, StringRef Param) {
+ if (AbbreviationDictionary.find(Arg) != AbbreviationDictionary.end()) {
+ if (Param.compare(AbbreviationDictionary.lookup(Arg)) == 0)
+ return true;
+ }
+
+ if (AbbreviationDictionary.find(Param) != AbbreviationDictionary.end()) {
+ if (Arg.compare(AbbreviationDictionary.lookup(Param)) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+// Check whether the shorter String is a prefix of the longer String.
+static bool applyPrefixHeuristic(StringRef Arg, StringRef Param,
+ unsigned threshold) {
+ StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
+ StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
+
+ double PrefixMatch = 0;
+ if (Longer.startswith_lower(Shorter))
+ PrefixMatch = (double)Shorter.size() / Longer.size() * 100;
+
+ return PrefixMatch > threshold;
+}
+
+// Check whether the shorter String is a suffix of the longer String.
+static bool applySuffixHeuristic(StringRef Arg, StringRef Param,
+ unsigned threshold) {
+ StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
+ StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
+
+ double SuffixMatch = 0;
+ if (Longer.endswith_lower(Shorter))
+ SuffixMatch = (double)Shorter.size() / Longer.size() * 100;
+
+ return SuffixMatch > threshold;
+}
+
+static bool applySubstringHeuristic(StringRef Arg, StringRef Param,
+ unsigned threshold) {
+ unsigned MaxLength = 0;
+ llvm::SmallVector<unsigned, SuspiciousCallArgumentCheck::VectorSmallSize>
+ Current(Param.size());
+ llvm::SmallVector<unsigned, SuspiciousCallArgumentCheck::VectorSmallSize>
+ Previous(Param.size());
+
+ for (unsigned i = 0; i < Arg.size(); ++i) {
+ for (unsigned j = 0; j < Param.size(); ++j) {
+ if (Arg[i] == Param[j]) {
+ if (i == 0 || j == 0) {
+ Current[j] = 1;
+ } else {
+ Current[j] = 1 + Previous[j - 1];
+ }
+
+ MaxLength = std::max(MaxLength, Current[j]);
+ } else {
+ Current[j] = 0;
+ }
+ }
+
+ Current.swap(Previous);
+ }
+
+ size_t LongerLength = std::max(Arg.size(), Param.size());
+ double SubstringRatio = (double)MaxLength / LongerLength * 100;
+
+ return SubstringRatio > threshold;
+}
+
+static bool applyLevenshteinHeuristic(StringRef Arg, StringRef Param,
+ unsigned threshold) {
+ unsigned Dist = Arg.edit_distance(Param);
+ size_t LongerLength = std::max(Arg.size(), Param.size());
+ Dist = (1 - (double)Dist / LongerLength) * 100;
+ return Dist > threshold;
+}
+
+// https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance
+static bool applyJaroWinklerHeuristic(StringRef Arg, StringRef Param,
+ unsigned threshold) {
+ int Match = 0, Transpos = 0;
+ int ArgLen = Arg.size();
+ int ParamLen = Param.size();
+ llvm::SmallVector<int, SuspiciousCallArgumentCheck::VectorSmallSize> ArgFlags(
+ ArgLen);
+ llvm::SmallVector<int, SuspiciousCallArgumentCheck::VectorSmallSize>
+ ParamFlags(ParamLen);
+ int Range = std::max(0, std::max(ArgLen, ParamLen) / 2 - 1);
+
+ // Calculate matching characters.
+ for (int i = 0; i < ParamLen; i++) {
+ for (int j = std::max(i - Range, 0), l = std::min(i + Range + 1, ArgLen);
+ j < l; j++) {
+ if (tolower(Param[i]) == tolower(Arg[j]) && !ArgFlags[j]) {
+ ArgFlags[j] = 1;
+ ParamFlags[i] = 1;
+ Match++;
+ break;
+ }
+ }
+ }
+
+ if (!Match)
+ return false;
+
+ // Calculate character transpositions.
+ int l = 0;
+ for (int i = 0; i < ParamLen; i++) {
+ if (ParamFlags[i] == 1) {
+ int j;
+ for (j = l; j < ArgLen; j++) {
+ if (ArgFlags[j] == 1) {
+ l = j + 1;
+ break;
+ }
+ }
+ if (tolower(Param[i]) != tolower(Arg[j]))
+ Transpos++;
+ }
+ }
+ Transpos /= 2;
+
+ // Jaro distance.
+ double Dist = (((double)Match / ArgLen) + ((double)Match / ParamLen) +
+ ((double)(Match - Transpos) / Match)) /
+ 3.0;
+
+ // Calculate common string prefix up to 4 chars.
+ l = 0;
+ for (int i = 0; i < std::min(std::min(ArgLen, ParamLen), 4); i++)
+ if (tolower(Arg[i]) == tolower(Param[i]))
+ l++;
+
+ // Jaro-Winkler distance.
+ Dist = (Dist + (l * 0.1 * (1 - Dist))) * 100;
+
+ return Dist > threshold;
+}
+
+// https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient
+static bool applyDiceHeuristic(StringRef Arg, StringRef Param,
+ unsigned threshold) {
+ double Dice = 0;
+
+ llvm::StringSet<> Arg_bigrams;
+ llvm::StringSet<> Param_bigrams;
+
+ // Extract character bigrams from Arg.
+ for (unsigned i = 0; i < (Arg.size() - 1); i++) {
+ Arg_bigrams.insert(Arg.substr(i, 2));
+ }
+
+ // Extract character bigrams from Param.
+ for (unsigned i = 0; i < (Param.size() - 1); i++) {
+ Param_bigrams.insert(Param.substr(i, 2));
+ }
+
+ int Intersection = 0;
+
+ // Find the intersection between the two sets.
+ for (auto IT = Param_bigrams.begin(); IT != Param_bigrams.end(); IT++) {
+ Intersection += Arg_bigrams.count((IT->getKey()));
+ }
+
+ // Calculate dice coefficient.
+ Dice =
+ (Intersection * 2.0) / (Arg_bigrams.size() + Param_bigrams.size()) * 100;
+
+ return Dice > threshold;
+}
+
+// Checks if ArgType binds to ParamType ragerding reference-ness and
+// cv-qualifiers.
+static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) {
+ return !ParamType->isReferenceType() ||
+ ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
+ ArgType.getNonReferenceType());
+}
+
+static bool isPointerOrArray(const QualType &TypeToCheck) {
+ return TypeToCheck->isAnyPointerType() || TypeToCheck->isArrayType();
+}
+
+// Checks whether ArgType is an array type identical to ParamType`s array type.
+// Enforces array elements` qualifier compatibility as well.
+static bool isCompatibleWithArrayReference(const QualType &ArgType,
+ const QualType &ParamType) {
+ if (!ArgType->isArrayType())
+ return false;
+ // Here, qualifiers belong to the elements of the arrays.
+ if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
+ return false;
+
+ return ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType();
+}
+
+static void convertToPointeeOrArrayElementQualType(QualType &TypeToConvert) {
+ unsigned CVRqualifiers = 0;
+ // Save array element qualifiers, since getElementType() removes qualifiers
+ // from array elements.
+ if (TypeToConvert->isArrayType())
+ CVRqualifiers = TypeToConvert.getLocalQualifiers().getCVRQualifiers();
+ TypeToConvert = TypeToConvert->isPointerType()
+ ? TypeToConvert->getPointeeType()
+ : TypeToConvert->getAsArrayTypeUnsafe()->getElementType();
+ TypeToConvert = TypeToConvert.withCVRQualifiers(CVRqualifiers);
+}
+
+// Checks if multilevel pointers` qualifiers compatibility continues on the
+// current pointer evel.
+// For multilevel pointers, C++ permits conversion, if every cv-qualifier in
+// ArgType also appears in the corresponding position in ParamType,
+// and if PramType has a cv-qualifier that's not in ArgType, then every * in
+// ParamType to the right
+// of that cv-qualifier, except the last one, must also be const-qualified.
+static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
+ bool &IsParamContinuouslyConst) {
+ // The types are compatible, if the parameter is at least as qualified as the
+ // argument, and if it is more qualified, it has to be const on upper pointer
+ // levels.
+ bool AreTypesQualCompatible =
+ ParamType.isAtLeastAsQualifiedAs(ArgType) &&
+ (!ParamType.hasQualifiers() || IsParamContinuouslyConst);
+ // Check whether the parameter's constness continues at the current pointer
+ // level.
+ IsParamContinuouslyConst &= ParamType.isConstQualified();
+
+ return AreTypesQualCompatible;
+}
+
+// Checks whether multilevel pointers are compatible in terms of levels,
+// qualifiers and pointee type.
+static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
+ bool IsParamContinuouslyConst) {
+
+ if (!arePointersStillQualCompatible(ArgType, ParamType,
+ IsParamContinuouslyConst))
+ return false;
+
+ do {
+ // Step down one pointer level.
+ convertToPointeeOrArrayElementQualType(ArgType);
+ convertToPointeeOrArrayElementQualType(ParamType);
+
+ // Check whether cv-qualifiers premit compatibility on
+ // current level.
+ if (!arePointersStillQualCompatible(ArgType, ParamType,
+ IsParamContinuouslyConst))
+ return false;
+
+ if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
+ return true;
+
+ } while (ParamType->isAnyPointerType() && ArgType->isAnyPointerType());
+ // The final type does not match, or pointer levels differ.
+ return false;
+}
+
+// Checks whether ArgType converts implicitly to ParamType.
+static bool areTypesCompatible(QualType ArgType, QualType ParamType,
+ const ASTContext *Ctx) {
+ if (ArgType.isNull() || ParamType.isNull())
+ return false;
+
+ ArgType = ArgType.getCanonicalType();
+ ParamType = ParamType.getCanonicalType();
+
+ if (ArgType == ParamType)
+ return true;
+
+ // Check for constness and reference compatibility.
+ if (!areRefAndQualCompatible(ArgType, ParamType))
+ return false;
+
+ bool IsParamReference = ParamType->isReferenceType();
+
+ // Reference-ness has already been checked ad should be removed
+ // before further checking.
+ ArgType = ArgType.getNonReferenceType();
+ ParamType = ParamType.getNonReferenceType();
+
+ bool IsParamContinuouslyConst =
+ !IsParamReference || ParamType.getNonReferenceType().isConstQualified();
+
+ if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
+ return true;
+
+ // Arithmetic types are interconvertible, except scoped enums.
+ if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) {
+ if ((ParamType->isEnumeralType() &&
+ ParamType->getAs<EnumType>()->getDecl()->isScoped()) ||
+ (ArgType->isEnumeralType() &&
+ ArgType->getAs<EnumType>()->getDecl()->isScoped()))
+ return false;
+
+ return true;
+ }
+
+ // Check if the argument and the param are both function types (the parameter
+ // decayed to
+ // a function pointer).
+ if (ArgType->isFunctionType() && ParamType->isFunctionPointerType()) {
+ ParamType = ParamType->getPointeeType();
+ return ArgType == ParamType;
+ }
+
+ // Arrays or pointer arguments convert to array or pointer parameters.
+ if (!(isPointerOrArray(ArgType) && isPointerOrArray(ParamType)))
+ return false;
+
+ // When ParamType is an array reference, ArgType has to be of the same sized,
+ // array type with cv-compatible elements.
+ if (IsParamReference && ParamType->isArrayType())
+ return isCompatibleWithArrayReference(ArgType, ParamType);
+
+ // Remove the first level of indirection.
+ convertToPointeeOrArrayElementQualType(ArgType);
+ convertToPointeeOrArrayElementQualType(ParamType);
+
+ // Check qualifier compatibility on the next level.
+ if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
+ return false;
+
+ if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
+ return true;
+
+ // At this point, all possible C language implicit conversion were checked
+ if (!Ctx->getLangOpts().CPlusPlus)
+ return false;
+
+ // Check whether ParamType and ArgType were both pointers to a class or a
+ // struct, and check for inheritance.
+ if (ParamType->isStructureOrClassType() &&
+ ArgType->isStructureOrClassType()) {
+ auto *ArgDecl = ArgType->getAsCXXRecordDecl();
+ auto *ParamDecl = ParamType->getAsCXXRecordDecl();
+ if (!ArgDecl || !ArgDecl->hasDefinition() || !ParamDecl ||
+ !ParamDecl->hasDefinition())
+ return false;
+
+ return ArgDecl->isDerivedFrom(ParamDecl);
+ }
+
+ // Unless argument and param are both multilevel pointers, the types are not
+ // convertible.
+ if (!(ParamType->isAnyPointerType() && ArgType->isAnyPointerType()))
+ return false;
+
+ return arePointerTypesCompatible(ArgType, ParamType,
+ IsParamContinuouslyConst);
+}
+
+// Constructor.
+SuspiciousCallArgumentCheck::SuspiciousCallArgumentCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context), Equality(Options.get("Equality", true)),
+ Abbreviation(Options.get("Abbreviation", true)),
+ Levenshtein(Options.get("Levenshtein", true)),
+ Prefix(Options.get("Prefix", true)), Suffix(Options.get("Suffix", true)),
+ Substring(Options.get("Substring", true)),
+ JaroWinkler(Options.get("JaroWinkler", true)),
+ Dice(Options.get("Dice", true)),
+ LevenshteinUpperBound(Options.get("LevenshteinUpperBound", 66)),
+ PrefixUpperBound(Options.get("PrefixUpperBound", 30)),
+ SuffixUpperBound(Options.get("SuffixUpperBound", 30)),
+ SubstringUpperBound(Options.get("SubstringUpperBound", 50)),
+ JaroWinklerUpperBound(Options.get("JaroWinklerUpperBound", 85)),
+ DiceUpperBound(Options.get("DiceUpperBound", 70)),
+ LevenshteinLowerBound(Options.get("LevenshteinLowerBound", 50)),
+ PrefixLowerBound(Options.get("PrefixLowerBound", 25)),
+ SuffixLowerBound(Options.get("SuffixLowerBound", 25)),
+ SubstringLowerBound(Options.get("SubstringLowerBound", 40)),
+ JaroWinklerLowerBound(Options.get("JaroWinklerLowerBound", 75)),
+ DiceLowerBound(Options.get("DiceLowerBound", 60)) {
+
+ AbbreviationDictionary.insert(std::make_pair("ptr", "pointer"));
+ AbbreviationDictionary.insert(std::make_pair("len", "length"));
+ AbbreviationDictionary.insert(std::make_pair("addr", "address"));
+ AbbreviationDictionary.insert(std::make_pair("arr", "array"));
+ AbbreviationDictionary.insert(std::make_pair("cpy", "copy"));
+ AbbreviationDictionary.insert(std::make_pair("src", "source"));
+ AbbreviationDictionary.insert(std::make_pair("val", "value"));
+ AbbreviationDictionary.insert(std::make_pair("ln", "line"));
+ AbbreviationDictionary.insert(std::make_pair("col", "column"));
+ AbbreviationDictionary.insert(std::make_pair("num", "number"));
+ AbbreviationDictionary.insert(std::make_pair("nr", "number"));
+ AbbreviationDictionary.insert(std::make_pair("stmt", "statement"));
+ AbbreviationDictionary.insert(std::make_pair("var", "variable"));
+ AbbreviationDictionary.insert(std::make_pair("vec", "vector"));
+ AbbreviationDictionary.insert(std::make_pair("buf", "buffer"));
+ AbbreviationDictionary.insert(std::make_pair("txt", "text"));
+ AbbreviationDictionary.insert(std::make_pair("dest", "destination"));
+ AbbreviationDictionary.insert(std::make_pair("elem", "element"));
+ AbbreviationDictionary.insert(std::make_pair("wdth", "width"));
+ AbbreviationDictionary.insert(std::make_pair("hght", "height"));
+ AbbreviationDictionary.insert(std::make_pair("cnt", "count"));
+ AbbreviationDictionary.insert(std::make_pair("i", "index"));
+ AbbreviationDictionary.insert(std::make_pair("idx", "index"));
+ AbbreviationDictionary.insert(std::make_pair("ind", "index"));
+ AbbreviationDictionary.insert(std::make_pair("attr", "atttribute"));
+ AbbreviationDictionary.insert(std::make_pair("pos", "position"));
+ AbbreviationDictionary.insert(std::make_pair("lst", "list"));
+ AbbreviationDictionary.insert(std::make_pair("str", "string"));
+ AbbreviationDictionary.insert(std::make_pair("srvr", "server"));
+ AbbreviationDictionary.insert(std::make_pair("clnt", "client"));
+ AbbreviationDictionary.insert(std::make_pair("ref", "reference"));
+ AbbreviationDictionary.insert(std::make_pair("dst", "distance"));
+ AbbreviationDictionary.insert(std::make_pair("dist", "distance"));
+
+ if (Equality)
+ AppliedHeuristics.push_back(Heuristic::Equality);
+ if (Abbreviation)
+ AppliedHeuristics.push_back(Heuristic::Abbreviation);
+ if (Levenshtein)
+ AppliedHeuristics.push_back(Heuristic::Levenshtein);
+ if (Prefix)
+ AppliedHeuristics.push_back(Heuristic::Prefix);
+ if (Suffix)
+ AppliedHeuristics.push_back(Heuristic::Suffix);
+ if (Substring)
+ AppliedHeuristics.push_back(Heuristic::Substring);
+ if (JaroWinkler)
+ AppliedHeuristics.push_back(Heuristic::JaroWinkler);
+ if (Dice)
+ AppliedHeuristics.push_back(Heuristic::Dice);
+}
+
+// Options.
+void SuspiciousCallArgumentCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+
+ Options.store(Opts, "Equality", Equality);
+ Options.store(Opts, "Abbreviation", Abbreviation);
+ Options.store(Opts, "Levenshtein", Levenshtein);
+ Options.store(Opts, "Prefix", Prefix);
+ Options.store(Opts, "Suffix", Suffix);
+ Options.store(Opts, "Substring", Substring);
+ Options.store(Opts, "JaroWinkler", JaroWinkler);
+ Options.store(Opts, "Dice", Dice);
+
+ Options.store(Opts, "LevenshteinUpperBound", LevenshteinUpperBound);
+ Options.store(Opts, "PrefixUpperBound", PrefixUpperBound);
+ Options.store(Opts, "SuffixUpperBound", SuffixUpperBound);
+ Options.store(Opts, "SubstringUpperBound", SubstringUpperBound);
+ Options.store(Opts, "JaroWinklerUpperBound", JaroWinklerUpperBound);
+ Options.store(Opts, "DiceUpperBound", DiceUpperBound);
+
+ Options.store(Opts, "LevenshteinLowerBound", LevenshteinLowerBound);
+ Options.store(Opts, "PrefixLowerBound", PrefixLowerBound);
+ Options.store(Opts, "SuffixLowerBound", SuffixLowerBound);
+ Options.store(Opts, "SubstringLowerBound", SubstringLowerBound);
+ Options.store(Opts, "JaroWinklerLowerBound", JaroWinklerLowerBound);
+ Options.store(Opts, "DiceLowerBound", DiceLowerBound);
+}
+
+// Matcher.
+void SuspiciousCallArgumentCheck::registerMatchers(MatchFinder *Finder) {
+ // Only match calls with at least 2 arguments.
+ Finder->addMatcher(
+ callExpr(unless(argumentCountIs(0)), unless(argumentCountIs(1)))
+ .bind("functionCall"),
+ this);
+}
+
+void SuspiciousCallArgumentCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCallExpr =
+ Result.Nodes.getNodeAs<CallExpr>("functionCall");
+
+ const Decl *CalleeDecl = MatchedCallExpr->getCalleeDecl();
+ if (!CalleeDecl)
+ return;
+
+ const FunctionDecl *CalleeFuncDecl = CalleeDecl->getAsFunction();
+ if (!CalleeFuncDecl)
+ return;
+
+ // Get param attributes.
+ setParamNamesAndTypes(CalleeFuncDecl);
+
+ if (ParamNames.empty())
+ return;
+
+ // Get Arg attributes.
+ // Lambda functions first Args are themselves.
+ unsigned InitialArgIndex = 0;
+
+ if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(CalleeFuncDecl)) {
+ if (MethodDecl->getParent()->isLambda())
+ InitialArgIndex = 1;
+ }
+
+ setArgNamesAndTypes(MatchedCallExpr, InitialArgIndex);
+
+ if (ArgNames.empty())
+ return;
+
+ // In case of variadic functions.
+ unsigned ParamCount = ParamNames.size();
+
+ // Check similarity.
+ for (unsigned i = 0; i < ParamCount; i++) {
+ for (unsigned j = i + 1; j < ParamCount; j++) {
+ // Do not check if param or arg names are short, or not convertible.
+ if (!areParamAndArgComparable(i, j, Result.Context))
+ continue;
+
+ if (areArgsSwapped(i, j)) {
+ // Warning at the function call.
+ diag(MatchedCallExpr->getBeginLoc(), "%0 (%1) is swapped with %2 (%3).")
+ << ArgNames[i] << ParamNames[i] << ArgNames[j] << ParamNames[j];
+
+ // Note at the functions declaration.
+ diag(CalleeFuncDecl->getBeginLoc(),
+ "%0 is declared here:", DiagnosticIDs::Note)
+ << CalleeFuncDecl->getNameInfo().getName().getAsString();
+
+ return; // TODO: Address this return later
+ }
+ }
+ }
+}
+
+void SuspiciousCallArgumentCheck::setParamNamesAndTypes(
+ const FunctionDecl *CalleeFuncDecl) {
+ // Reset vectors, and fill them with the currently checked function's
+ // attributes.
+ ParamNames.clear();
+ ParamTypes.clear();
+
+ for (unsigned i = 0, e = CalleeFuncDecl->getNumParams(); i != e; ++i) {
+
+ if (const ParmVarDecl *PVD = CalleeFuncDecl->getParamDecl(i)) {
+ ParamTypes.push_back(PVD->getType());
+
+ if (IdentifierInfo *II = PVD->getIdentifier()) {
+ ParamNames.push_back(II->getName());
+ continue;
+ } else {
+ ParamNames.push_back(StringRef());
+ continue;
+ }
+ }
+ ParamTypes.push_back(QualType());
+ ParamNames.push_back(StringRef());
+ }
+}
+
+void SuspiciousCallArgumentCheck::setArgNamesAndTypes(
+ const CallExpr *MatchedCallExpr, unsigned InitialArgIndex) {
+ // Reset vectors, and fill them with the currently checked function's
+ // attributes.
+ ArgNames.clear();
+ ArgTypes.clear();
+
+ for (unsigned i = InitialArgIndex, j = MatchedCallExpr->getNumArgs(); i < j;
+ i++) {
+ if (const auto *DeclRef = dyn_cast<DeclRefExpr>(
+ MatchedCallExpr->getArg(i)->IgnoreParenImpCasts())) {
+ if (const auto *Var = dyn_cast<VarDecl>(DeclRef->getDecl())) {
+
+ ArgTypes.push_back(Var->getType());
+ ArgNames.push_back(Var->getName());
+ continue;
+ } else if (const auto *Var = dyn_cast<FunctionDecl>(DeclRef->getDecl())) {
+ ArgTypes.push_back(Var->getType());
+ ArgNames.push_back(Var->getName());
+ continue;
+ }
+ }
+ ArgTypes.push_back(QualType());
+ ArgNames.push_back(StringRef());
+ }
+}
+
+bool SuspiciousCallArgumentCheck::areParamAndArgComparable(
+ unsigned Position1, unsigned Position2, const ASTContext *Ctx) const {
+ if (Position1 >= ArgNames.size() || Position2 >= ArgNames.size())
+ return false;
+ if (ArgNames[Position1].size() < 3 || ArgNames[Position2].size() < 3 ||
+ ParamNames[Position1].size() < 3 || ParamNames[Position2].size() < 3)
+ return false;
+ if (!areTypesCompatible(ArgTypes[Position1], ParamTypes[Position2], Ctx) ||
+ !areTypesCompatible(ArgTypes[Position2], ParamTypes[Position1], Ctx))
+ return false;
+ return true;
+}
+
+bool SuspiciousCallArgumentCheck::areArgsSwapped(unsigned Position1,
+ unsigned Position2) const {
+ bool param1Arg2NamesSimilar =
+ areNamesSimilar(ArgNames[Position2], ParamNames[Position1], Bound::Upper);
+ bool param2Arg1NamesSimilar =
+ areNamesSimilar(ArgNames[Position1], ParamNames[Position2], Bound::Upper);
+ bool param1Arg1NamesSimilar =
+ areNamesSimilar(ArgNames[Position1], ParamNames[Position1], Bound::Lower);
+ bool param2Arg2NamesSimilar =
+ areNamesSimilar(ArgNames[Position2], ParamNames[Position2], Bound::Lower);
+
+ return (param1Arg2NamesSimilar || param2Arg1NamesSimilar) &&
+ !param1Arg1NamesSimilar && !param2Arg2NamesSimilar;
+}
+
+bool SuspiciousCallArgumentCheck::areNamesSimilar(StringRef Arg,
+ StringRef Param,
+ Bound bound) const {
+
+ bool areNamesSimilar = false;
+ for (Heuristic Heur : AppliedHeuristics)
+ switch (Heur) {
+ case Heuristic::Equality:
+ areNamesSimilar |= applyEqualityHeuristic(Arg, Param);
+ break;
+ case Heuristic::Abbreviation:
+ areNamesSimilar |= applyAbbreviationHeuristic(Arg, Param);
+ break;
+ case Heuristic::Levenshtein:
+ areNamesSimilar |= applyLevenshteinHeuristic(Arg, Param,
+ bound == Bound::Upper
+ ? LevenshteinUpperBound
+ : LevenshteinLowerBound);
+ break;
+ case Heuristic::Prefix:
+ areNamesSimilar |= applyPrefixHeuristic(
+ Arg, Param,
+ bound == Bound::Upper ? PrefixUpperBound : PrefixLowerBound);
+ break;
+ case Heuristic::Suffix:
+ areNamesSimilar |= applySuffixHeuristic(
+ Arg, Param,
+ bound == Bound::Upper ? SuffixUpperBound : SuffixLowerBound);
+ break;
+ case Heuristic::Substring:
+ areNamesSimilar |= applySubstringHeuristic(
+ Arg, Param,
+ bound == Bound::Upper ? SubstringUpperBound : SubstringLowerBound);
+ break;
+ case Heuristic::JaroWinkler:
+ areNamesSimilar |= applyJaroWinklerHeuristic(Arg, Param,
+ bound == Bound::Upper
+ ? JaroWinklerUpperBound
+ : JaroWinklerLowerBound);
+ break;
+ case Heuristic::Dice:
+ areNamesSimilar |= applyDiceHeuristic(
+ Arg, Param, bound == Bound::Upper ? DiceUpperBound : DiceLowerBound);
+ break;
+ default:
+ break;
+ }
+ return areNamesSimilar;
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -43,6 +43,7 @@
#include "StaticAccessedThroughInstanceCheck.h"
#include "StaticDefinitionInAnonymousNamespaceCheck.h"
#include "StringCompareCheck.h"
+#include "SuspiciousCallArgumentCheck.h"
#include "UniqueptrDeleteReleaseCheck.h"
#include "UppercaseLiteralSuffixCheck.h"
@@ -121,6 +122,8 @@
"readability-redundant-string-init");
CheckFactories.registerCheck<SimplifyBooleanExprCheck>(
"readability-simplify-boolean-expr");
+ CheckFactories.registerCheck<SuspiciousCallArgumentCheck>(
+ "readability-suspicious-call-argument");
CheckFactories.registerCheck<UniqueptrDeleteReleaseCheck>(
"readability-uniqueptr-delete-release");
CheckFactories.registerCheck<UppercaseLiteralSuffixCheck>(
Index: clang-tools-extra/clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -40,6 +40,7 @@
StaticAccessedThroughInstanceCheck.cpp
StaticDefinitionInAnonymousNamespaceCheck.cpp
StringCompareCheck.cpp
+ SuspiciousCallArgumentCheck.cpp
UniqueptrDeleteReleaseCheck.cpp
UppercaseLiteralSuffixCheck.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits