[llvm] 73e4b5c - [clang-tidy] Add 'readability-suspicious-call-argument' check

via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 19 01:19:21 PDT 2021


Author: Whisperity
Date: 2021-07-19T10:18:09+02:00
New Revision: 73e4b5cfa8ea6dda3624e03beb60827db12bb3a3

URL: https://github.com/llvm/llvm-project/commit/73e4b5cfa8ea6dda3624e03beb60827db12bb3a3
DIFF: https://github.com/llvm/llvm-project/commit/73e4b5cfa8ea6dda3624e03beb60827db12bb3a3.diff

LOG: [clang-tidy] Add 'readability-suspicious-call-argument' check

Finds function calls where the call arguments might be provided in an
incorrect order, based on the comparison (via string metrics) of the
parameter names and the argument names against each other.

A diagnostic is emitted if an argument name is similar to a *different*
parameter than the one currently passed to, and it is sufficiently
dissimilar to the one it **is** passed to currently.

False-positive warnings from this check are useful to indicate bad
naming convention issues, even if a swap isn't necessary.
This check does not generate FixIts.

Originally implemented by @varjujan as his Master's Thesis work.
The check was subsequently taken over by @barancsuk who added type
conformity checks to silence false positive matches.
The work by @whisperity involved driving the check's review and fixing
some more bugs in the process.

Reviewed By: aaron.ballman, alexfh

Differential Revision: http://reviews.llvm.org/D20689

Co-authored-by: János Varjú <varjujanos2 at gmail.com>
Co-authored-by: Lilla Barancsuk <barancsuklilla at gmail.com>

Added: 
    clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
    clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h
    clang-tools-extra/docs/clang-tidy/checks/readability-suspicious-call-argument.rst
    clang-tools-extra/test/clang-tidy/checkers/readability-suspicious-call-argument.cpp

Modified: 
    clang-tools-extra/clang-tidy/readability/CMakeLists.txt
    clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst
    llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 78a3851f66bef..0d35991d24799 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -40,6 +40,7 @@ add_clang_library(clangTidyReadabilityModule
   StaticAccessedThroughInstanceCheck.cpp
   StaticDefinitionInAnonymousNamespaceCheck.cpp
   StringCompareCheck.cpp
+  SuspiciousCallArgumentCheck.cpp
   UniqueptrDeleteReleaseCheck.cpp
   UppercaseLiteralSuffixCheck.cpp
   UseAnyOfAllOfCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index 088b9f09082e0..a05b70826e669 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/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"
 #include "UseAnyOfAllOfCheck.h"
@@ -122,6 +123,8 @@ class ReadabilityModule : public ClangTidyModule {
         "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>(

diff  --git a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
new file mode 100644
index 0000000000000..557e95bc24073
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
@@ -0,0 +1,806 @@
+//===--- SuspiciousCallArgumentCheck.cpp - clang-tidy ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousCallArgumentCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <sstream>
+
+using namespace clang::ast_matchers;
+namespace optutils = clang::tidy::utils::options;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+struct DefaultHeuristicConfiguration {
+  /// Whether the heuristic is to be enabled by default.
+  const bool Enabled;
+
+  /// The upper bound of % of similarity the two strings might have to be
+  /// considered dissimilar.
+  /// (For purposes of configuration, -1 if the heuristic is not configurable
+  /// with bounds.)
+  const int8_t DissimilarBelow;
+
+  /// The lower bound of % of similarity the two string must have to be
+  /// considered similar.
+  /// (For purposes of configuration, -1 if the heuristic is not configurable
+  /// with bounds.)
+  const int8_t SimilarAbove;
+
+  /// Can the heuristic be configured with bounds?
+  bool hasBounds() const { return DissimilarBelow > -1 && SimilarAbove > -1; }
+};
+} // namespace
+
+static constexpr std::size_t DefaultMinimumIdentifierNameLength = 3;
+
+static constexpr StringRef HeuristicToString[] = {
+    "Equality",  "Abbreviation", "Prefix",      "Suffix",
+    "Substring", "Levenshtein",  "JaroWinkler", "Dice"};
+
+static constexpr DefaultHeuristicConfiguration Defaults[] = {
+    {true, -1, -1}, // Equality.
+    {true, -1, -1}, // Abbreviation.
+    {true, 25, 30}, // Prefix.
+    {true, 25, 30}, // Suffix.
+    {true, 40, 50}, // Substring.
+    {true, 50, 66}, // Levenshtein.
+    {true, 75, 85}, // Jaro-Winkler.
+    {true, 60, 70}, // Dice.
+};
+
+static_assert(
+    sizeof(HeuristicToString) / sizeof(HeuristicToString[0]) ==
+        SuspiciousCallArgumentCheck::HeuristicCount,
+    "Ensure that every heuristic has a corresponding stringified name");
+static_assert(sizeof(Defaults) / sizeof(Defaults[0]) ==
+                  SuspiciousCallArgumentCheck::HeuristicCount,
+              "Ensure that every heuristic has a default configuration.");
+
+namespace {
+template <std::size_t I> struct HasWellConfiguredBounds {
+  static constexpr bool Value =
+      !((Defaults[I].DissimilarBelow == -1) ^ (Defaults[I].SimilarAbove == -1));
+  static_assert(Value, "A heuristic must either have a dissimilarity and "
+                       "similarity bound, or neither!");
+};
+
+template <std::size_t I> struct HasWellConfiguredBoundsFold {
+  static constexpr bool Value = HasWellConfiguredBounds<I>::Value &&
+                                HasWellConfiguredBoundsFold<I - 1>::Value;
+};
+
+template <> struct HasWellConfiguredBoundsFold<0> {
+  static constexpr bool Value = HasWellConfiguredBounds<0>::Value;
+};
+
+struct AllHeuristicsBoundsWellConfigured {
+  static constexpr bool Value =
+      HasWellConfiguredBoundsFold<SuspiciousCallArgumentCheck::HeuristicCount -
+                                  1>::Value;
+};
+
+static_assert(AllHeuristicsBoundsWellConfigured::Value, "");
+} // namespace
+
+static const std::string DefaultAbbreviations =
+    optutils::serializeStringList({"addr=address",
+                                   "arr=array",
+                                   "attr=attribute",
+                                   "buf=buffer",
+                                   "cl=client",
+                                   "cnt=count",
+                                   "col=column",
+                                   "cpy=copy",
+                                   "dest=destination",
+                                   "dist=distance"
+                                   "dst=distance",
+                                   "elem=element",
+                                   "hght=height",
+                                   "i=index",
+                                   "idx=index",
+                                   "len=length",
+                                   "ln=line",
+                                   "lst=list",
+                                   "nr=number",
+                                   "num=number",
+                                   "pos=position",
+                                   "ptr=pointer",
+                                   "ref=reference",
+                                   "src=source",
+                                   "srv=server",
+                                   "stmt=statement",
+                                   "str=string",
+                                   "val=value",
+                                   "var=variable",
+                                   "vec=vector",
+                                   "wdth=width"});
+
+static constexpr std::size_t SmallVectorSize =
+    SuspiciousCallArgumentCheck::SmallVectorSize;
+
+/// Returns how many % X is of Y.
+static inline double percentage(double X, double Y) { return X / Y * 100.0; }
+
+static bool applyEqualityHeuristic(StringRef Arg, StringRef Param) {
+  return Arg.equals_insensitive(Param);
+}
+
+static bool applyAbbreviationHeuristic(
+    const llvm::StringMap<std::string> &AbbreviationDictionary, StringRef Arg,
+    StringRef Param) {
+  if (AbbreviationDictionary.find(Arg) != AbbreviationDictionary.end() &&
+      Param.equals(AbbreviationDictionary.lookup(Arg)))
+    return true;
+
+  if (AbbreviationDictionary.find(Param) != AbbreviationDictionary.end() &&
+      Arg.equals(AbbreviationDictionary.lookup(Param)))
+    return true;
+
+  return false;
+}
+
+/// Check whether the shorter String is a prefix of the longer String.
+static bool applyPrefixHeuristic(StringRef Arg, StringRef Param,
+                                 int8_t Threshold) {
+  StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
+  StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
+
+  if (Longer.startswith_insensitive(Shorter))
+    return percentage(Shorter.size(), Longer.size()) > Threshold;
+
+  return false;
+}
+
+/// Check whether the shorter String is a suffix of the longer String.
+static bool applySuffixHeuristic(StringRef Arg, StringRef Param,
+                                 int8_t Threshold) {
+  StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
+  StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
+
+  if (Longer.endswith_insensitive(Shorter))
+    return percentage(Shorter.size(), Longer.size()) > Threshold;
+
+  return false;
+}
+
+static bool applySubstringHeuristic(StringRef Arg, StringRef Param,
+                                    int8_t Threshold) {
+
+  std::size_t MaxLength = 0;
+  SmallVector<std::size_t, SmallVectorSize> Current(Param.size());
+  SmallVector<std::size_t, SmallVectorSize> Previous(Param.size());
+  std::string ArgLower = Arg.lower();
+  std::string ParamLower = Param.lower();
+
+  for (std::size_t I = 0; I < Arg.size(); ++I) {
+    for (std::size_t J = 0; J < Param.size(); ++J) {
+      if (ArgLower[I] == ParamLower[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());
+  return percentage(MaxLength, LongerLength) > Threshold;
+}
+
+static bool applyLevenshteinHeuristic(StringRef Arg, StringRef Param,
+                                      int8_t Threshold) {
+  std::size_t LongerLength = std::max(Arg.size(), Param.size());
+  double Dist = Arg.edit_distance(Param);
+  Dist = (1.0 - Dist / LongerLength) * 100.0;
+  return Dist > Threshold;
+}
+
+// Based on http://en.wikipedia.org/wiki/Jaro–Winkler_distance.
+static bool applyJaroWinklerHeuristic(StringRef Arg, StringRef Param,
+                                      int8_t Threshold) {
+  std::size_t Match = 0, Transpos = 0;
+  std::ptr
diff _t ArgLen = Arg.size();
+  std::ptr
diff _t ParamLen = Param.size();
+  SmallVector<int, SmallVectorSize> ArgFlags(ArgLen);
+  SmallVector<int, SmallVectorSize> ParamFlags(ParamLen);
+  std::ptr
diff _t Range =
+      std::max(std::ptr
diff _t{0}, std::max(ArgLen, ParamLen) / 2 - 1);
+
+  // Calculate matching characters.
+  for (std::ptr
diff _t I = 0; I < ParamLen; ++I)
+    for (std::ptr
diff _t J = std::max(I - Range, std::ptr
diff _t{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.
+  std::ptr
diff _t L = 0;
+  for (std::ptr
diff _t I = 0; I < ParamLen; ++I) {
+    if (ParamFlags[I] == 1) {
+      std::ptr
diff _t 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 MatchD = Match;
+  double Dist = ((MatchD / ArgLen) + (MatchD / ParamLen) +
+                 ((MatchD - Transpos) / Match)) /
+                3.0;
+
+  // Calculate common string prefix up to 4 chars.
+  L = 0;
+  for (std::ptr
diff _t I = 0;
+       I < std::min(std::min(ArgLen, ParamLen), std::ptr
diff _t{4}); ++I)
+    if (tolower(Arg[I]) == tolower(Param[I]))
+      ++L;
+
+  // Jaro-Winkler distance.
+  Dist = (Dist + (L * 0.1 * (1.0 - Dist))) * 100.0;
+  return Dist > Threshold;
+}
+
+// Based on http://en.wikipedia.org/wiki/Sørensen–Dice_coefficient
+static bool applyDiceHeuristic(StringRef Arg, StringRef Param,
+                               int8_t Threshold) {
+  llvm::StringSet<> ArgBigrams;
+  llvm::StringSet<> ParamBigrams;
+
+  // Extract character bigrams from Arg.
+  for (std::ptr
diff _t I = 0; I < static_cast<std::ptr
diff _t>(Arg.size()) - 1;
+       ++I)
+    ArgBigrams.insert(Arg.substr(I, 2).lower());
+
+  // Extract character bigrams from Param.
+  for (std::ptr
diff _t I = 0; I < static_cast<std::ptr
diff _t>(Param.size()) - 1;
+       ++I)
+    ParamBigrams.insert(Param.substr(I, 2).lower());
+
+  std::size_t Intersection = 0;
+
+  // Find the intersection between the two sets.
+  for (auto IT = ParamBigrams.begin(); IT != ParamBigrams.end(); ++IT)
+    Intersection += ArgBigrams.count((IT->getKey()));
+
+  // Calculate Dice coefficient.
+  return percentage(Intersection * 2.0,
+                    ArgBigrams.size() + ParamBigrams.size()) > Threshold;
+}
+
+/// Checks if ArgType binds to ParamType regarding reference-ness and
+/// cv-qualifiers.
+static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) {
+  return !ParamType->isReferenceType() ||
+         ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
+             ArgType.getNonReferenceType());
+}
+
+static bool isPointerOrArray(QualType TypeToCheck) {
+  return TypeToCheck->isPointerType() || 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(QualType ArgType,
+                                           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 QualType 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);
+  return TypeToConvert;
+}
+
+/// Checks if multilevel pointers' qualifiers compatibility continues on the
+/// current pointer level. 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.
+    ArgType = convertToPointeeOrArrayElementQualType(ArgType);
+    ParamType = convertToPointeeOrArrayElementQualType(ParamType);
+
+    // Check whether cv-qualifiers permit compatibility on
+    // current level.
+    if (!arePointersStillQualCompatible(ArgType, ParamType,
+                                        IsParamContinuouslyConst))
+      return false;
+
+    if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
+      return true;
+
+  } while (ParamType->isPointerType() && ArgType->isPointerType());
+  // The final type does not match, or pointer levels 
diff er.
+  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 and should be removed
+  // before further checking.
+  ArgType = ArgType.getNonReferenceType();
+  ParamType = ParamType.getNonReferenceType();
+
+  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 element type.
+  if (IsParamReference && ParamType->isArrayType())
+    return isCompatibleWithArrayReference(ArgType, ParamType);
+
+  bool IsParamContinuouslyConst =
+      !IsParamReference || ParamType.getNonReferenceType().isConstQualified();
+
+  // Remove the first level of indirection.
+  ArgType = convertToPointeeOrArrayElementQualType(ArgType);
+  ParamType = 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()) {
+    const auto *ArgDecl = ArgType->getAsCXXRecordDecl();
+    const 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);
+}
+
+static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD) {
+  switch (FD->getOverloadedOperator()) {
+  case OO_None:
+  case OO_Call:
+  case OO_Subscript:
+  case OO_New:
+  case OO_Delete:
+  case OO_Array_New:
+  case OO_Array_Delete:
+  case OO_Conditional:
+  case OO_Coawait:
+    return false;
+
+  default:
+    return FD->getNumParams() <= 2;
+  }
+}
+
+SuspiciousCallArgumentCheck::SuspiciousCallArgumentCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      MinimumIdentifierNameLength(Options.get(
+          "MinimumIdentifierNameLength", DefaultMinimumIdentifierNameLength)) {
+  auto GetToggleOpt = [this](Heuristic H) -> bool {
+    auto Idx = static_cast<std::size_t>(H);
+    assert(Idx < HeuristicCount);
+    return Options.get(HeuristicToString[Idx], Defaults[Idx].Enabled);
+  };
+  auto GetBoundOpt = [this](Heuristic H, BoundKind BK) -> int8_t {
+    auto Idx = static_cast<std::size_t>(H);
+    assert(Idx < HeuristicCount);
+
+    SmallString<32> Key = HeuristicToString[Idx];
+    Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
+                                                : "SimilarAbove");
+    int8_t Default = BK == BoundKind::DissimilarBelow
+                         ? Defaults[Idx].DissimilarBelow
+                         : Defaults[Idx].SimilarAbove;
+    return Options.get(Key, Default);
+  };
+  for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
+    auto H = static_cast<Heuristic>(Idx);
+    if (GetToggleOpt(H))
+      AppliedHeuristics.emplace_back(H);
+    ConfiguredBounds.emplace_back(
+        std::make_pair(GetBoundOpt(H, BoundKind::DissimilarBelow),
+                       GetBoundOpt(H, BoundKind::SimilarAbove)));
+  }
+
+  for (const std::string &Abbreviation : optutils::parseStringList(
+           Options.get("Abbreviations", DefaultAbbreviations))) {
+    auto KeyAndValue = StringRef{Abbreviation}.split("=");
+    assert(!KeyAndValue.first.empty() && !KeyAndValue.second.empty());
+    AbbreviationDictionary.insert(
+        std::make_pair(KeyAndValue.first.str(), KeyAndValue.second.str()));
+  }
+}
+
+void SuspiciousCallArgumentCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "MinimumIdentifierNameLength",
+                MinimumIdentifierNameLength);
+  const auto &SetToggleOpt = [this, &Opts](Heuristic H) -> void {
+    auto Idx = static_cast<std::size_t>(H);
+    Options.store(Opts, HeuristicToString[Idx], isHeuristicEnabled(H));
+  };
+  const auto &SetBoundOpt = [this, &Opts](Heuristic H, BoundKind BK) -> void {
+    auto Idx = static_cast<std::size_t>(H);
+    assert(Idx < HeuristicCount);
+    if (!Defaults[Idx].hasBounds())
+      return;
+
+    SmallString<32> Key = HeuristicToString[Idx];
+    Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
+                                                : "SimilarAbove");
+    Options.store(Opts, Key, getBound(H, BK).getValue());
+  };
+
+  for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
+    auto H = static_cast<Heuristic>(Idx);
+    SetToggleOpt(H);
+    SetBoundOpt(H, BoundKind::DissimilarBelow);
+    SetBoundOpt(H, BoundKind::SimilarAbove);
+  }
+
+  SmallVector<std::string, 32> Abbreviations;
+  for (const auto &Abbreviation : AbbreviationDictionary) {
+    SmallString<32> EqualSignJoined;
+    EqualSignJoined.append(Abbreviation.first());
+    EqualSignJoined.append("=");
+    EqualSignJoined.append(Abbreviation.second);
+
+    if (!Abbreviation.second.empty())
+      Abbreviations.emplace_back(EqualSignJoined.str());
+  }
+  Options.store(Opts, "Abbreviations",
+                optutils::serializeStringList(Abbreviations));
+}
+
+bool SuspiciousCallArgumentCheck::isHeuristicEnabled(Heuristic H) const {
+  return llvm::is_contained(AppliedHeuristics, H);
+}
+
+Optional<int8_t> SuspiciousCallArgumentCheck::getBound(Heuristic H,
+                                                       BoundKind BK) const {
+  auto Idx = static_cast<std::size_t>(H);
+  assert(Idx < HeuristicCount);
+
+  if (!Defaults[Idx].hasBounds())
+    return None;
+
+  switch (BK) {
+  case BoundKind::DissimilarBelow:
+    return ConfiguredBounds[Idx].first;
+  case BoundKind::SimilarAbove:
+    return ConfiguredBounds[Idx].second;
+  }
+  llvm_unreachable("Unhandled Bound kind.");
+}
+
+void SuspiciousCallArgumentCheck::registerMatchers(MatchFinder *Finder) {
+  // Only match calls with at least 2 arguments.
+  Finder->addMatcher(
+      functionDecl(forEachDescendant(callExpr(unless(anyOf(argumentCountIs(0),
+                                                           argumentCountIs(1))))
+                                         .bind("functionCall")))
+          .bind("callingFunc"),
+      this);
+}
+
+void SuspiciousCallArgumentCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCallExpr =
+      Result.Nodes.getNodeAs<CallExpr>("functionCall");
+  const auto *Caller = Result.Nodes.getNodeAs<FunctionDecl>("callingFunc");
+  assert(MatchedCallExpr && Caller);
+
+  const Decl *CalleeDecl = MatchedCallExpr->getCalleeDecl();
+  if (!CalleeDecl)
+    return;
+
+  const FunctionDecl *CalleeFuncDecl = CalleeDecl->getAsFunction();
+  if (!CalleeFuncDecl)
+    return;
+  if (CalleeFuncDecl == Caller)
+    // Ignore recursive calls.
+    return;
+  if (isOverloadedUnaryOrBinarySymbolOperator(CalleeFuncDecl))
+    return;
+
+  // Get param attributes.
+  setParamNamesAndTypes(CalleeFuncDecl);
+
+  if (ParamNames.empty())
+    return;
+
+  // Get Arg attributes.
+  std::size_t InitialArgIndex = 0;
+
+  if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(CalleeFuncDecl)) {
+    if (MethodDecl->getParent()->isLambda())
+      // Lambda functions' first Arg are the lambda object.
+      InitialArgIndex = 1;
+    else if (MethodDecl->getOverloadedOperator() == OO_Call)
+      // For custom operator()s, the first Arg is the called object.
+      InitialArgIndex = 1;
+  }
+
+  setArgNamesAndTypes(MatchedCallExpr, InitialArgIndex);
+
+  if (ArgNames.empty())
+    return;
+
+  std::size_t ParamCount = ParamNames.size();
+
+  // Check similarity.
+  for (std::size_t I = 0; I < ParamCount; ++I) {
+    for (std::size_t 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))
+        continue;
+
+      // Warning at the call itself.
+      diag(MatchedCallExpr->getExprLoc(),
+           "%ordinal0 argument '%1' (passed to '%2') looks like it might be "
+           "swapped with the %ordinal3, '%4' (passed to '%5')")
+          << static_cast<unsigned>(I + 1) << ArgNames[I] << ParamNames[I]
+          << static_cast<unsigned>(J + 1) << ArgNames[J] << ParamNames[J]
+          << MatchedCallExpr->getArg(I)->getSourceRange()
+          << MatchedCallExpr->getArg(J)->getSourceRange();
+
+      // Note at the functions declaration.
+      SourceLocation IParNameLoc =
+          CalleeFuncDecl->getParamDecl(I)->getLocation();
+      SourceLocation JParNameLoc =
+          CalleeFuncDecl->getParamDecl(J)->getLocation();
+
+      diag(CalleeFuncDecl->getLocation(), "in the call to %0, declared here",
+           DiagnosticIDs::Note)
+          << CalleeFuncDecl
+          << CharSourceRange::getTokenRange(IParNameLoc, IParNameLoc)
+          << CharSourceRange::getTokenRange(JParNameLoc, JParNameLoc);
+    }
+  }
+}
+
+void SuspiciousCallArgumentCheck::setParamNamesAndTypes(
+    const FunctionDecl *CalleeFuncDecl) {
+  // Reset vectors, and fill them with the currently checked function's
+  // parameters' data.
+  ParamNames.clear();
+  ParamTypes.clear();
+
+  for (const ParmVarDecl *Param : CalleeFuncDecl->parameters()) {
+    ParamTypes.push_back(Param->getType());
+
+    if (IdentifierInfo *II = Param->getIdentifier())
+      ParamNames.push_back(II->getName());
+    else
+      ParamNames.push_back(StringRef());
+  }
+}
+
+void SuspiciousCallArgumentCheck::setArgNamesAndTypes(
+    const CallExpr *MatchedCallExpr, std::size_t InitialArgIndex) {
+  // Reset vectors, and fill them with the currently checked function's
+  // arguments' data.
+  ArgNames.clear();
+  ArgTypes.clear();
+
+  for (std::size_t I = InitialArgIndex, J = MatchedCallExpr->getNumArgs();
+       I < J; ++I) {
+    if (const auto *ArgExpr = dyn_cast<DeclRefExpr>(
+            MatchedCallExpr->getArg(I)->IgnoreUnlessSpelledInSource())) {
+      if (const auto *Var = dyn_cast<VarDecl>(ArgExpr->getDecl())) {
+        ArgTypes.push_back(Var->getType());
+        ArgNames.push_back(Var->getName());
+      } else if (const auto *FCall =
+                     dyn_cast<FunctionDecl>(ArgExpr->getDecl())) {
+        ArgTypes.push_back(FCall->getType());
+        ArgNames.push_back(FCall->getName());
+      } else {
+        ArgTypes.push_back(QualType());
+        ArgNames.push_back(StringRef());
+      }
+    } else {
+      ArgTypes.push_back(QualType());
+      ArgNames.push_back(StringRef());
+    }
+  }
+}
+
+bool SuspiciousCallArgumentCheck::areParamAndArgComparable(
+    std::size_t Position1, std::size_t Position2, const ASTContext &Ctx) const {
+  if (Position1 >= ArgNames.size() || Position2 >= ArgNames.size())
+    return false;
+
+  // Do not report for too short strings.
+  if (ArgNames[Position1].size() < MinimumIdentifierNameLength ||
+      ArgNames[Position2].size() < MinimumIdentifierNameLength ||
+      ParamNames[Position1].size() < MinimumIdentifierNameLength ||
+      ParamNames[Position2].size() < MinimumIdentifierNameLength)
+    return false;
+
+  if (!areTypesCompatible(ArgTypes[Position1], ParamTypes[Position2], Ctx) ||
+      !areTypesCompatible(ArgTypes[Position2], ParamTypes[Position1], Ctx))
+    return false;
+
+  return true;
+}
+
+bool SuspiciousCallArgumentCheck::areArgsSwapped(std::size_t Position1,
+                                                 std::size_t Position2) const {
+  for (Heuristic H : AppliedHeuristics) {
+    bool A1ToP2Similar = areNamesSimilar(
+        ArgNames[Position2], ParamNames[Position1], H, BoundKind::SimilarAbove);
+    bool A2ToP1Similar = areNamesSimilar(
+        ArgNames[Position1], ParamNames[Position2], H, BoundKind::SimilarAbove);
+
+    bool A1ToP1Dissimilar =
+        !areNamesSimilar(ArgNames[Position1], ParamNames[Position1], H,
+                         BoundKind::DissimilarBelow);
+    bool A2ToP2Dissimilar =
+        !areNamesSimilar(ArgNames[Position2], ParamNames[Position2], H,
+                         BoundKind::DissimilarBelow);
+
+    if ((A1ToP2Similar || A2ToP1Similar) && A1ToP1Dissimilar &&
+        A2ToP2Dissimilar)
+      return true;
+  }
+  return false;
+}
+
+bool SuspiciousCallArgumentCheck::areNamesSimilar(StringRef Arg,
+                                                  StringRef Param, Heuristic H,
+                                                  BoundKind BK) const {
+  int8_t Threshold = -1;
+  if (Optional<int8_t> GotBound = getBound(H, BK))
+    Threshold = GotBound.getValue();
+
+  switch (H) {
+  case Heuristic::Equality:
+    return applyEqualityHeuristic(Arg, Param);
+  case Heuristic::Abbreviation:
+    return applyAbbreviationHeuristic(AbbreviationDictionary, Arg, Param);
+  case Heuristic::Prefix:
+    return applyPrefixHeuristic(Arg, Param, Threshold);
+  case Heuristic::Suffix:
+    return applySuffixHeuristic(Arg, Param, Threshold);
+  case Heuristic::Substring:
+    return applySubstringHeuristic(Arg, Param, Threshold);
+  case Heuristic::Levenshtein:
+    return applyLevenshteinHeuristic(Arg, Param, Threshold);
+  case Heuristic::JaroWinkler:
+    return applyJaroWinklerHeuristic(Arg, Param, Threshold);
+  case Heuristic::Dice:
+    return applyDiceHeuristic(Arg, Param, Threshold);
+  }
+  llvm_unreachable("Unhandled heuristic kind");
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang

diff  --git a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h
new file mode 100644
index 0000000000000..3f4e011132cb7
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h
@@ -0,0 +1,100 @@
+//===--- SuspiciousCallArgumentCheck.h - clang-tidy -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUSCALLARGUMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUSCALLARGUMENTCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "llvm/ADT/StringSet.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds function calls where the arguments passed are provided out of order,
+/// based on the 
diff erence between the argument name and the parameter names
+/// of the function.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-suspicious-call-argument.html
+class SuspiciousCallArgumentCheck : public ClangTidyCheck {
+  enum class Heuristic {
+    Equality,
+    Abbreviation,
+    Prefix,
+    Suffix,
+    Substring,
+    Levenshtein,
+    JaroWinkler,
+    Dice
+  };
+
+  /// When applying a heuristic, the value of this enum decides which kind of
+  /// bound will be selected from the bounds configured for the heuristic.
+  /// This only applies to heuristics that can take bounds.
+  enum class BoundKind {
+    /// Check for dissimilarity of the names. Names are deemed dissimilar if
+    /// the similarity measurement is **below** the configured threshold.
+    DissimilarBelow,
+
+    /// Check for similarity of the names. Names are deemed similar if the
+    /// similarity measurement (the result of heuristic) is **above** the
+    /// configured threshold.
+    SimilarAbove
+  };
+
+public:
+  static constexpr std::size_t SmallVectorSize = 8;
+  static constexpr std::size_t HeuristicCount =
+      static_cast<std::size_t>(Heuristic::Dice) + 1;
+
+  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;
+
+private:
+  const std::size_t MinimumIdentifierNameLength;
+
+  /// The configuration for which heuristics were enabled.
+  SmallVector<Heuristic, HeuristicCount> AppliedHeuristics;
+
+  /// The lower and upper bounds for each heuristic, as configured by the user.
+  SmallVector<std::pair<int8_t, int8_t>, HeuristicCount> ConfiguredBounds;
+
+  /// The abbreviation-to-abbreviated map for the Abbreviation heuristic.
+  llvm::StringMap<std::string> AbbreviationDictionary;
+
+  bool isHeuristicEnabled(Heuristic H) const;
+  Optional<int8_t> getBound(Heuristic H, BoundKind BK) const;
+
+  // Runtime information of the currently analyzed function call.
+  SmallVector<QualType, SmallVectorSize> ArgTypes;
+  SmallVector<StringRef, SmallVectorSize> ArgNames;
+  SmallVector<QualType, SmallVectorSize> ParamTypes;
+  SmallVector<StringRef, SmallVectorSize> ParamNames;
+
+  void setParamNamesAndTypes(const FunctionDecl *CalleeFuncDecl);
+
+  void setArgNamesAndTypes(const CallExpr *MatchedCallExpr,
+                           std::size_t InitialArgIndex);
+
+  bool areParamAndArgComparable(std::size_t Position1, std::size_t Position2,
+                                const ASTContext &Ctx) const;
+
+  bool areArgsSwapped(std::size_t Position1, std::size_t Position2) const;
+
+  bool areNamesSimilar(StringRef Arg, StringRef Param, Heuristic H,
+                       BoundKind BK) const;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUSCALLARGUMENTCHECK_H

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index e9b186a81b391..b4d325cb553d6 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -126,6 +126,13 @@ New checks
 
   Finds calls to ``new`` with missing exception handler for ``std::bad_alloc``.
 
+- New `readability-suspicious-call-argument
+  <clang-tidy/checks/readability-suspicious-call-argument>`_ check
+
+  Finds function calls where the arguments passed are provided out of order,
+  based on the 
diff erence between the argument name and the parameter names
+  of the function.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
@@ -134,6 +141,7 @@ New check aliases
   :doc:`concurrency-thread-canceltype-asynchronous
   <clang-tidy/checks/concurrency-thread-canceltype-asynchronous>` was added.
 
+
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 83ec376c401f0..ebc99ca8f0eec 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -313,6 +313,7 @@ Clang-Tidy Checks
    `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>`_,
    `readability-uniqueptr-delete-release <readability-uniqueptr-delete-release.html>`_, "Yes"
    `readability-uppercase-literal-suffix <readability-uppercase-literal-suffix.html>`_, "Yes"
    `readability-use-anyofallof <readability-use-anyofallof.html>`_,

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/readability-suspicious-call-argument.rst b/clang-tools-extra/docs/clang-tidy/checks/readability-suspicious-call-argument.rst
new file mode 100644
index 0000000000000..a1e0f25834f34
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability-suspicious-call-argument.rst
@@ -0,0 +1,242 @@
+.. title:: clang-tidy - readability-suspicious-call-argument
+
+readability-suspicious-call-argument
+====================================
+
+Finds function calls where the arguments passed are provided out of order,
+based on the 
diff erence between the argument name and the parameter names
+of the function.
+
+Given a function call ``f(foo, bar);`` and a function signature
+``void f(T tvar, U uvar)``, the arguments ``foo`` and ``bar`` are swapped if
+``foo`` (the argument name) is more similar to ``uvar`` (the other parameter)
+than ``tvar`` (the parameter it is currently passed to) **and** ``bar`` is
+more similar to ``tvar`` than ``uvar``.
+
+Warnings might indicate either that the arguments are swapped, or that the
+names' cross-similarity might hinder code comprehension.
+
+.. _heuristics:
+
+Heuristics
+----------
+
+The following heuristics are implemented in the check.
+If **any** of the enabled heuristics deem the arguments to be provided out of
+order, a warning will be issued.
+
+The heuristics themselves are implemented by considering pairs of strings, and
+are symmetric, so in the following there is no distinction on which string is
+the argument name and which string is the parameter name.
+
+Equality
+^^^^^^^^
+
+The most trivial heuristic, which compares the two strings for case-insensitive
+equality.
+
+.. _abbreviation_heuristic:
+
+Abbreviation
+^^^^^^^^^^^^
+
+Common abbreviations can be specified which will deem the strings similar if
+the abbreviated and the abbreviation stand together.
+For example, if ``src`` is registered as an abbreviation for ``source``, then
+the following code example will be warned about.
+
+.. code-block:: c++
+
+    void foo(int source, int x);
+
+    foo(b, src);
+
+The abbreviations to recognise can be configured with the
+:ref:`Abbreviations<opt_Abbreviations>` check option.
+This heuristic is case-insensitive.
+
+Prefix
+^^^^^^
+
+The *prefix* heuristic reports if one of the strings is a sufficiently long
+prefix of the other string, e.g. ``target`` to ``targetPtr``.
+The similarity percentage is the length ratio of the prefix to the longer
+string, in the previous example, it would be `6 / 9 = 66.66...`\%.
+
+This heuristic can be configured with :ref:`bounds<opt_Bounds>`.
+The default bounds are: below `25`\% dissimilar and above `30`\% similar.
+This heuristic is case-insensitive.
+
+Suffix
+^^^^^^
+
+Analogous to the `Prefix` heuristic.
+In the case of ``oldValue`` and ``value`` compared, the similarity percentage
+is `8 / 5 = 62.5`\%.
+
+This heuristic can be configured with :ref:`bounds<opt_Bounds>`.
+The default bounds are: below `25`\% dissimilar and above `30`\% similar.
+This heuristic is case-insensitive.
+
+Substring
+^^^^^^^^^
+
+The substring heuristic combines the prefix and the suffix heuristic, and tries
+to find the *longest common substring* in the two strings provided.
+The similarity percentage is the ratio of the found longest common substring
+against the *longer* of the two input strings.
+For example, given ``val`` and ``rvalue``, the similarity is `3 / 6 = 50`\%.
+If no characters are common in the two string, `0`\%.
+
+This heuristic can be configured with :ref:`bounds<opt_Bounds>`.
+The default bounds are: below `40`\% dissimilar and above `50`\% similar.
+This heuristic is case-insensitive.
+
+Levenshtein distance (as `Levenshtein`)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The `Levenshtein distance <http://en.wikipedia.org/wiki/Levenshtein_distance>`_
+describes how many single-character changes (additions, changes, or removals)
+must be applied to transform one string into another.
+
+The Levenshtein distance is translated into a similarity percentage by dividing
+it with the length of the *longer* string, and taking its complement with
+regards to `100`\%.
+For example, given ``something`` and ``anything``, the distance is `4` edits,
+and the similarity percentage is `100`\% `- 4 / 9 = 55.55...`\%.
+
+This heuristic can be configured with :ref:`bounds<opt_Bounds>`.
+The default bounds are: below `50`\% dissimilar and above `66`\% similar.
+This heuristic is case-sensitive.
+
+Jaro–Winkler distance (as `JaroWinkler`)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The `Jaro–Winkler distance <http://en.wikipedia.org/wiki/Jaro–Winkler_distance>`_
+is an edit distance like the Levenshtein distance.
+It is calculated from the amount of common characters that are sufficiently
+close to each other in position, and to-be-changed characters.
+The original definition of Jaro has been extended by Winkler to weigh prefix
+similarities more.
+The similarity percentage is expressed as an average of the common and
+non-common characters against the length of both strings.
+
+This heuristic can be configured with :ref:`bounds<opt_Bounds>`.
+The default bounds are: below `75`\% dissimilar and above `85`\% similar.
+This heuristic is case-insensitive.
+
+Sørensen–Dice coefficient (as `Dice`)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The `Sørensen–Dice coefficient <http://en.wikipedia.org/wiki/Sørensen–Dice_coefficient>`_
+was originally defined to measure the similarity of two sets.
+Formally, the coefficient is calculated by dividing `2 * #(intersection)` with
+`#(set1) + #(set2)`, where `#()` is the cardinality function of sets.
+This metric is applied to strings by creating bigrams (substring sequences of
+length 2) of the two strings and using the set of bigrams for the two strings
+as the two sets.
+
+This heuristic can be configured with :ref:`bounds<opt_Bounds>`.
+The default bounds are: below `60`\% dissimilar and above `70`\% similar.
+This heuristic is case-insensitive.
+
+
+Options
+-------
+
+.. option:: MinimumIdentifierNameLength
+
+    Sets the minimum required length the argument and parameter names
+    need to have. Names shorter than this length will be ignored.
+    Defaults to `3`.
+
+.. _opt_Abbreviations:
+
+.. option:: Abbreviations
+
+    For the **Abbreviation** heuristic
+    (:ref:`see here<abbreviation_heuristic>`), this option configures the
+    abbreviations in the `"abbreviation=abbreviated_value"` format.
+    The option is a string, with each value joined by `";"`.
+
+    By default, the following abbreviations are set:
+
+       * `addr=address`
+       * `arr=array`
+       * `attr=attribute`
+       * `buf=buffer`
+       * `cl=client`
+       * `cnt=count`
+       * `col=column`
+       * `cpy=copy`
+       * `dest=destination`
+       * `dist=distance`
+       * `dst=distance`
+       * `elem=element`
+       * `hght=height`
+       * `i=index`
+       * `idx=index`
+       * `len=length`
+       * `ln=line`
+       * `lst=list`
+       * `nr=number`
+       * `num=number`
+       * `pos=position`
+       * `ptr=pointer`
+       * `ref=reference`
+       * `src=source`
+       * `srv=server`
+       * `stmt=statement`
+       * `str=string`
+       * `val=value`
+       * `var=variable`
+       * `vec=vector`
+       * `wdth=width`
+
+The configuration options for each implemented heuristic (see above) is
+constructed dynamically.
+In the following, `<HeuristicName>` refers to one of the keys from the
+heuristics implemented.
+
+.. option:: <HeuristicName>
+
+    `True` or `False`, whether a particular heuristic, such as `Equality` or
+    `Levenshtein` is enabled.
+
+    Defaults to `True` for every heuristic.
+
+.. _opt_Bounds:
+
+.. option:: <HeuristicName>DissimilarBelow, <HeuristicName>SimilarAbove
+
+    A value between `0` and `100`, expressing a percentage.
+    The bounds set what percentage of similarity the heuristic must deduce
+    for the two identifiers to be considered similar or dissimilar by the
+    check.
+
+    Given arguments ``arg1`` and ``arg2`` passed to ``param1`` and ``param2``,
+    respectively, the bounds check is performed in the following way:
+    If the similarity of the currently passed argument order
+    (``arg1`` to ``param1``) is **below** the `DissimilarBelow` threshold, and
+    the similarity of the suggested swapped order (``arg1`` to ``param2``) is
+    **above** the `SimilarAbove` threshold, the swap is reported.
+
+    For the defaults of each heuristic, :ref:`see above<heuristics>`.
+
+
+Name synthesis
+--------------
+
+When comparing the argument names and parameter names, the following logic is
+used to gather the names for comparison:
+
+Parameter names are the identifiers as written in the source code.
+
+Argument names are:
+
+  * If a variable is passed, the variable's name.
+  * If a subsequent function call's return value is used as argument, the called
+    function's name.
+  * Otherwise, empty string.
+
+Empty argument or parameter names are ignored by the heuristics.

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

diff  --git a/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn b/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn
index 2c8d8cb5c0fc0..89198c8b26b8e 100644
--- a/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn
@@ -48,6 +48,7 @@ static_library("readability") {
     "StaticAccessedThroughInstanceCheck.cpp",
     "StaticDefinitionInAnonymousNamespaceCheck.cpp",
     "StringCompareCheck.cpp",
+    "SuspiciousCallArgumentCheck.cpp",
     "UniqueptrDeleteReleaseCheck.cpp",
     "UppercaseLiteralSuffixCheck.cpp",
     "UseAnyOfAllOfCheck.cpp",


        


More information about the llvm-commits mailing list