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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D20689: [clang-tidy] Ad... Whisperity via Phabricator via cfe-commits

Reply via email to