xref: /llvm-project/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp (revision b9d678d22f74ebd6e34f0a3501fb01d3d80984e7)
173e4b5cfSWhisperity //===--- SuspiciousCallArgumentCheck.cpp - clang-tidy ---------------------===//
273e4b5cfSWhisperity //
373e4b5cfSWhisperity // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
473e4b5cfSWhisperity // See https://llvm.org/LICENSE.txt for license information.
573e4b5cfSWhisperity // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
673e4b5cfSWhisperity //
773e4b5cfSWhisperity //===----------------------------------------------------------------------===//
873e4b5cfSWhisperity 
973e4b5cfSWhisperity #include "SuspiciousCallArgumentCheck.h"
1073e4b5cfSWhisperity #include "../utils/OptionsUtils.h"
1173e4b5cfSWhisperity #include "clang/AST/ASTContext.h"
1273e4b5cfSWhisperity #include "clang/AST/Type.h"
1373e4b5cfSWhisperity #include "clang/ASTMatchers/ASTMatchFinder.h"
1471f55735SKazu Hirata #include <optional>
1573e4b5cfSWhisperity #include <sstream>
1673e4b5cfSWhisperity 
1773e4b5cfSWhisperity using namespace clang::ast_matchers;
1873e4b5cfSWhisperity namespace optutils = clang::tidy::utils::options;
1973e4b5cfSWhisperity 
207d2ea6c4SCarlos Galvez namespace clang::tidy::readability {
2173e4b5cfSWhisperity 
2273e4b5cfSWhisperity namespace {
2373e4b5cfSWhisperity struct DefaultHeuristicConfiguration {
2473e4b5cfSWhisperity   /// Whether the heuristic is to be enabled by default.
2573e4b5cfSWhisperity   const bool Enabled;
2673e4b5cfSWhisperity 
2773e4b5cfSWhisperity   /// The upper bound of % of similarity the two strings might have to be
2873e4b5cfSWhisperity   /// considered dissimilar.
2973e4b5cfSWhisperity   /// (For purposes of configuration, -1 if the heuristic is not configurable
3073e4b5cfSWhisperity   /// with bounds.)
3173e4b5cfSWhisperity   const int8_t DissimilarBelow;
3273e4b5cfSWhisperity 
3373e4b5cfSWhisperity   /// The lower bound of % of similarity the two string must have to be
3473e4b5cfSWhisperity   /// considered similar.
3573e4b5cfSWhisperity   /// (For purposes of configuration, -1 if the heuristic is not configurable
3673e4b5cfSWhisperity   /// with bounds.)
3773e4b5cfSWhisperity   const int8_t SimilarAbove;
3873e4b5cfSWhisperity 
3973e4b5cfSWhisperity   /// Can the heuristic be configured with bounds?
4073e4b5cfSWhisperity   bool hasBounds() const { return DissimilarBelow > -1 && SimilarAbove > -1; }
4173e4b5cfSWhisperity };
4273e4b5cfSWhisperity } // namespace
4373e4b5cfSWhisperity 
4473e4b5cfSWhisperity static constexpr std::size_t DefaultMinimumIdentifierNameLength = 3;
4573e4b5cfSWhisperity 
4673e4b5cfSWhisperity static constexpr StringRef HeuristicToString[] = {
4773e4b5cfSWhisperity     "Equality",  "Abbreviation", "Prefix",      "Suffix",
4873e4b5cfSWhisperity     "Substring", "Levenshtein",  "JaroWinkler", "Dice"};
4973e4b5cfSWhisperity 
5073e4b5cfSWhisperity static constexpr DefaultHeuristicConfiguration Defaults[] = {
5173e4b5cfSWhisperity     {true, -1, -1}, // Equality.
5273e4b5cfSWhisperity     {true, -1, -1}, // Abbreviation.
5373e4b5cfSWhisperity     {true, 25, 30}, // Prefix.
5473e4b5cfSWhisperity     {true, 25, 30}, // Suffix.
5573e4b5cfSWhisperity     {true, 40, 50}, // Substring.
5673e4b5cfSWhisperity     {true, 50, 66}, // Levenshtein.
5773e4b5cfSWhisperity     {true, 75, 85}, // Jaro-Winkler.
5873e4b5cfSWhisperity     {true, 60, 70}, // Dice.
5973e4b5cfSWhisperity };
6073e4b5cfSWhisperity 
6173e4b5cfSWhisperity static_assert(
6273e4b5cfSWhisperity     sizeof(HeuristicToString) / sizeof(HeuristicToString[0]) ==
6373e4b5cfSWhisperity         SuspiciousCallArgumentCheck::HeuristicCount,
6473e4b5cfSWhisperity     "Ensure that every heuristic has a corresponding stringified name");
6573e4b5cfSWhisperity static_assert(sizeof(Defaults) / sizeof(Defaults[0]) ==
6673e4b5cfSWhisperity                   SuspiciousCallArgumentCheck::HeuristicCount,
6773e4b5cfSWhisperity               "Ensure that every heuristic has a default configuration.");
6873e4b5cfSWhisperity 
6973e4b5cfSWhisperity namespace {
7073e4b5cfSWhisperity template <std::size_t I> struct HasWellConfiguredBounds {
7173e4b5cfSWhisperity   static constexpr bool Value =
7273e4b5cfSWhisperity       !((Defaults[I].DissimilarBelow == -1) ^ (Defaults[I].SimilarAbove == -1));
7373e4b5cfSWhisperity   static_assert(Value, "A heuristic must either have a dissimilarity and "
7473e4b5cfSWhisperity                        "similarity bound, or neither!");
7573e4b5cfSWhisperity };
7673e4b5cfSWhisperity 
7773e4b5cfSWhisperity template <std::size_t I> struct HasWellConfiguredBoundsFold {
7873e4b5cfSWhisperity   static constexpr bool Value = HasWellConfiguredBounds<I>::Value &&
7973e4b5cfSWhisperity                                 HasWellConfiguredBoundsFold<I - 1>::Value;
8073e4b5cfSWhisperity };
8173e4b5cfSWhisperity 
8273e4b5cfSWhisperity template <> struct HasWellConfiguredBoundsFold<0> {
8373e4b5cfSWhisperity   static constexpr bool Value = HasWellConfiguredBounds<0>::Value;
8473e4b5cfSWhisperity };
8573e4b5cfSWhisperity 
8673e4b5cfSWhisperity struct AllHeuristicsBoundsWellConfigured {
8773e4b5cfSWhisperity   static constexpr bool Value =
8873e4b5cfSWhisperity       HasWellConfiguredBoundsFold<SuspiciousCallArgumentCheck::HeuristicCount -
8973e4b5cfSWhisperity                                   1>::Value;
9073e4b5cfSWhisperity };
9173e4b5cfSWhisperity 
9232aa35b5SKazu Hirata static_assert(AllHeuristicsBoundsWellConfigured::Value);
9373e4b5cfSWhisperity } // namespace
9473e4b5cfSWhisperity 
95a4bdeb9aSNathan James static constexpr llvm::StringLiteral DefaultAbbreviations = "addr=address;"
96a4bdeb9aSNathan James                                                             "arr=array;"
97a4bdeb9aSNathan James                                                             "attr=attribute;"
98a4bdeb9aSNathan James                                                             "buf=buffer;"
99a4bdeb9aSNathan James                                                             "cl=client;"
100a4bdeb9aSNathan James                                                             "cnt=count;"
101a4bdeb9aSNathan James                                                             "col=column;"
102a4bdeb9aSNathan James                                                             "cpy=copy;"
103a4bdeb9aSNathan James                                                             "dest=destination;"
10473e4b5cfSWhisperity                                                             "dist=distance"
105a4bdeb9aSNathan James                                                             "dst=distance;"
106a4bdeb9aSNathan James                                                             "elem=element;"
107a4bdeb9aSNathan James                                                             "hght=height;"
108a4bdeb9aSNathan James                                                             "i=index;"
109a4bdeb9aSNathan James                                                             "idx=index;"
110a4bdeb9aSNathan James                                                             "len=length;"
111a4bdeb9aSNathan James                                                             "ln=line;"
112a4bdeb9aSNathan James                                                             "lst=list;"
113a4bdeb9aSNathan James                                                             "nr=number;"
114a4bdeb9aSNathan James                                                             "num=number;"
115a4bdeb9aSNathan James                                                             "pos=position;"
116a4bdeb9aSNathan James                                                             "ptr=pointer;"
117a4bdeb9aSNathan James                                                             "ref=reference;"
118a4bdeb9aSNathan James                                                             "src=source;"
119a4bdeb9aSNathan James                                                             "srv=server;"
120a4bdeb9aSNathan James                                                             "stmt=statement;"
121a4bdeb9aSNathan James                                                             "str=string;"
122a4bdeb9aSNathan James                                                             "val=value;"
123a4bdeb9aSNathan James                                                             "var=variable;"
124a4bdeb9aSNathan James                                                             "vec=vector;"
125a4bdeb9aSNathan James                                                             "wdth=width";
12673e4b5cfSWhisperity 
12773e4b5cfSWhisperity static constexpr std::size_t SmallVectorSize =
12873e4b5cfSWhisperity     SuspiciousCallArgumentCheck::SmallVectorSize;
12973e4b5cfSWhisperity 
13073e4b5cfSWhisperity /// Returns how many % X is of Y.
13173e4b5cfSWhisperity static inline double percentage(double X, double Y) { return X / Y * 100.0; }
13273e4b5cfSWhisperity 
13373e4b5cfSWhisperity static bool applyEqualityHeuristic(StringRef Arg, StringRef Param) {
13473e4b5cfSWhisperity   return Arg.equals_insensitive(Param);
13573e4b5cfSWhisperity }
13673e4b5cfSWhisperity 
13773e4b5cfSWhisperity static bool applyAbbreviationHeuristic(
13873e4b5cfSWhisperity     const llvm::StringMap<std::string> &AbbreviationDictionary, StringRef Arg,
13973e4b5cfSWhisperity     StringRef Param) {
14015aa9653SKazu Hirata   if (AbbreviationDictionary.contains(Arg) &&
141f841ca0cSKazu Hirata       Param == AbbreviationDictionary.lookup(Arg))
14273e4b5cfSWhisperity     return true;
14373e4b5cfSWhisperity 
14415aa9653SKazu Hirata   if (AbbreviationDictionary.contains(Param) &&
145f841ca0cSKazu Hirata       Arg == AbbreviationDictionary.lookup(Param))
14673e4b5cfSWhisperity     return true;
14773e4b5cfSWhisperity 
14873e4b5cfSWhisperity   return false;
14973e4b5cfSWhisperity }
15073e4b5cfSWhisperity 
15173e4b5cfSWhisperity /// Check whether the shorter String is a prefix of the longer String.
15273e4b5cfSWhisperity static bool applyPrefixHeuristic(StringRef Arg, StringRef Param,
15373e4b5cfSWhisperity                                  int8_t Threshold) {
15473e4b5cfSWhisperity   StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
15573e4b5cfSWhisperity   StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
15673e4b5cfSWhisperity 
157ed1539c6SKazu Hirata   if (Longer.starts_with_insensitive(Shorter))
15873e4b5cfSWhisperity     return percentage(Shorter.size(), Longer.size()) > Threshold;
15973e4b5cfSWhisperity 
16073e4b5cfSWhisperity   return false;
16173e4b5cfSWhisperity }
16273e4b5cfSWhisperity 
16373e4b5cfSWhisperity /// Check whether the shorter String is a suffix of the longer String.
16473e4b5cfSWhisperity static bool applySuffixHeuristic(StringRef Arg, StringRef Param,
16573e4b5cfSWhisperity                                  int8_t Threshold) {
16673e4b5cfSWhisperity   StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
16773e4b5cfSWhisperity   StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
16873e4b5cfSWhisperity 
169ed1539c6SKazu Hirata   if (Longer.ends_with_insensitive(Shorter))
17073e4b5cfSWhisperity     return percentage(Shorter.size(), Longer.size()) > Threshold;
17173e4b5cfSWhisperity 
17273e4b5cfSWhisperity   return false;
17373e4b5cfSWhisperity }
17473e4b5cfSWhisperity 
17573e4b5cfSWhisperity static bool applySubstringHeuristic(StringRef Arg, StringRef Param,
17673e4b5cfSWhisperity                                     int8_t Threshold) {
17773e4b5cfSWhisperity 
17873e4b5cfSWhisperity   std::size_t MaxLength = 0;
17973e4b5cfSWhisperity   SmallVector<std::size_t, SmallVectorSize> Current(Param.size());
18073e4b5cfSWhisperity   SmallVector<std::size_t, SmallVectorSize> Previous(Param.size());
18173e4b5cfSWhisperity   std::string ArgLower = Arg.lower();
18273e4b5cfSWhisperity   std::string ParamLower = Param.lower();
18373e4b5cfSWhisperity 
18473e4b5cfSWhisperity   for (std::size_t I = 0; I < Arg.size(); ++I) {
18573e4b5cfSWhisperity     for (std::size_t J = 0; J < Param.size(); ++J) {
18673e4b5cfSWhisperity       if (ArgLower[I] == ParamLower[J]) {
18773e4b5cfSWhisperity         if (I == 0 || J == 0)
18873e4b5cfSWhisperity           Current[J] = 1;
18973e4b5cfSWhisperity         else
19073e4b5cfSWhisperity           Current[J] = 1 + Previous[J - 1];
19173e4b5cfSWhisperity 
19273e4b5cfSWhisperity         MaxLength = std::max(MaxLength, Current[J]);
19373e4b5cfSWhisperity       } else
19473e4b5cfSWhisperity         Current[J] = 0;
19573e4b5cfSWhisperity     }
19673e4b5cfSWhisperity 
19773e4b5cfSWhisperity     Current.swap(Previous);
19873e4b5cfSWhisperity   }
19973e4b5cfSWhisperity 
20073e4b5cfSWhisperity   size_t LongerLength = std::max(Arg.size(), Param.size());
20173e4b5cfSWhisperity   return percentage(MaxLength, LongerLength) > Threshold;
20273e4b5cfSWhisperity }
20373e4b5cfSWhisperity 
20473e4b5cfSWhisperity static bool applyLevenshteinHeuristic(StringRef Arg, StringRef Param,
20573e4b5cfSWhisperity                                       int8_t Threshold) {
20673e4b5cfSWhisperity   std::size_t LongerLength = std::max(Arg.size(), Param.size());
20773e4b5cfSWhisperity   double Dist = Arg.edit_distance(Param);
20873e4b5cfSWhisperity   Dist = (1.0 - Dist / LongerLength) * 100.0;
20973e4b5cfSWhisperity   return Dist > Threshold;
21073e4b5cfSWhisperity }
21173e4b5cfSWhisperity 
21273e4b5cfSWhisperity // Based on http://en.wikipedia.org/wiki/Jaro–Winkler_distance.
21373e4b5cfSWhisperity static bool applyJaroWinklerHeuristic(StringRef Arg, StringRef Param,
21473e4b5cfSWhisperity                                       int8_t Threshold) {
21573e4b5cfSWhisperity   std::size_t Match = 0, Transpos = 0;
21673e4b5cfSWhisperity   std::ptrdiff_t ArgLen = Arg.size();
21773e4b5cfSWhisperity   std::ptrdiff_t ParamLen = Param.size();
21873e4b5cfSWhisperity   SmallVector<int, SmallVectorSize> ArgFlags(ArgLen);
21973e4b5cfSWhisperity   SmallVector<int, SmallVectorSize> ParamFlags(ParamLen);
22073e4b5cfSWhisperity   std::ptrdiff_t Range =
22173e4b5cfSWhisperity       std::max(std::ptrdiff_t{0}, std::max(ArgLen, ParamLen) / 2 - 1);
22273e4b5cfSWhisperity 
22373e4b5cfSWhisperity   // Calculate matching characters.
22473e4b5cfSWhisperity   for (std::ptrdiff_t I = 0; I < ParamLen; ++I)
22573e4b5cfSWhisperity     for (std::ptrdiff_t J = std::max(I - Range, std::ptrdiff_t{0}),
22673e4b5cfSWhisperity                         L = std::min(I + Range + 1, ArgLen);
22773e4b5cfSWhisperity          J < L; ++J)
22873e4b5cfSWhisperity       if (tolower(Param[I]) == tolower(Arg[J]) && !ArgFlags[J]) {
22973e4b5cfSWhisperity         ArgFlags[J] = 1;
23073e4b5cfSWhisperity         ParamFlags[I] = 1;
23173e4b5cfSWhisperity         ++Match;
23273e4b5cfSWhisperity         break;
23373e4b5cfSWhisperity       }
23473e4b5cfSWhisperity 
23573e4b5cfSWhisperity   if (!Match)
23673e4b5cfSWhisperity     return false;
23773e4b5cfSWhisperity 
23873e4b5cfSWhisperity   // Calculate character transpositions.
23973e4b5cfSWhisperity   std::ptrdiff_t L = 0;
24073e4b5cfSWhisperity   for (std::ptrdiff_t I = 0; I < ParamLen; ++I) {
24173e4b5cfSWhisperity     if (ParamFlags[I] == 1) {
242cbdc3e1bSPiotr Zegar       std::ptrdiff_t J = 0;
24373e4b5cfSWhisperity       for (J = L; J < ArgLen; ++J)
24473e4b5cfSWhisperity         if (ArgFlags[J] == 1) {
24573e4b5cfSWhisperity           L = J + 1;
24673e4b5cfSWhisperity           break;
24773e4b5cfSWhisperity         }
24873e4b5cfSWhisperity 
24973e4b5cfSWhisperity       if (tolower(Param[I]) != tolower(Arg[J]))
25073e4b5cfSWhisperity         ++Transpos;
25173e4b5cfSWhisperity     }
25273e4b5cfSWhisperity   }
25373e4b5cfSWhisperity   Transpos /= 2;
25473e4b5cfSWhisperity 
25573e4b5cfSWhisperity   // Jaro distance.
25673e4b5cfSWhisperity   double MatchD = Match;
25773e4b5cfSWhisperity   double Dist = ((MatchD / ArgLen) + (MatchD / ParamLen) +
25873e4b5cfSWhisperity                  ((MatchD - Transpos) / Match)) /
25973e4b5cfSWhisperity                 3.0;
26073e4b5cfSWhisperity 
26173e4b5cfSWhisperity   // Calculate common string prefix up to 4 chars.
26273e4b5cfSWhisperity   L = 0;
26373e4b5cfSWhisperity   for (std::ptrdiff_t I = 0;
26473e4b5cfSWhisperity        I < std::min(std::min(ArgLen, ParamLen), std::ptrdiff_t{4}); ++I)
26573e4b5cfSWhisperity     if (tolower(Arg[I]) == tolower(Param[I]))
26673e4b5cfSWhisperity       ++L;
26773e4b5cfSWhisperity 
26873e4b5cfSWhisperity   // Jaro-Winkler distance.
26973e4b5cfSWhisperity   Dist = (Dist + (L * 0.1 * (1.0 - Dist))) * 100.0;
27073e4b5cfSWhisperity   return Dist > Threshold;
27173e4b5cfSWhisperity }
27273e4b5cfSWhisperity 
27373e4b5cfSWhisperity // Based on http://en.wikipedia.org/wiki/Sørensen–Dice_coefficient
27473e4b5cfSWhisperity static bool applyDiceHeuristic(StringRef Arg, StringRef Param,
27573e4b5cfSWhisperity                                int8_t Threshold) {
27673e4b5cfSWhisperity   llvm::StringSet<> ArgBigrams;
27773e4b5cfSWhisperity   llvm::StringSet<> ParamBigrams;
27873e4b5cfSWhisperity 
27973e4b5cfSWhisperity   // Extract character bigrams from Arg.
28073e4b5cfSWhisperity   for (std::ptrdiff_t I = 0; I < static_cast<std::ptrdiff_t>(Arg.size()) - 1;
28173e4b5cfSWhisperity        ++I)
28273e4b5cfSWhisperity     ArgBigrams.insert(Arg.substr(I, 2).lower());
28373e4b5cfSWhisperity 
28473e4b5cfSWhisperity   // Extract character bigrams from Param.
28573e4b5cfSWhisperity   for (std::ptrdiff_t I = 0; I < static_cast<std::ptrdiff_t>(Param.size()) - 1;
28673e4b5cfSWhisperity        ++I)
28773e4b5cfSWhisperity     ParamBigrams.insert(Param.substr(I, 2).lower());
28873e4b5cfSWhisperity 
28973e4b5cfSWhisperity   std::size_t Intersection = 0;
29073e4b5cfSWhisperity 
29173e4b5cfSWhisperity   // Find the intersection between the two sets.
29273e4b5cfSWhisperity   for (auto IT = ParamBigrams.begin(); IT != ParamBigrams.end(); ++IT)
29373e4b5cfSWhisperity     Intersection += ArgBigrams.count((IT->getKey()));
29473e4b5cfSWhisperity 
29573e4b5cfSWhisperity   // Calculate Dice coefficient.
29673e4b5cfSWhisperity   return percentage(Intersection * 2.0,
29773e4b5cfSWhisperity                     ArgBigrams.size() + ParamBigrams.size()) > Threshold;
29873e4b5cfSWhisperity }
29973e4b5cfSWhisperity 
30073e4b5cfSWhisperity /// Checks if ArgType binds to ParamType regarding reference-ness and
30173e4b5cfSWhisperity /// cv-qualifiers.
302*b9d678d2SJoseph Huber static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType,
303*b9d678d2SJoseph Huber                                     const ASTContext &Ctx) {
30473e4b5cfSWhisperity   return !ParamType->isReferenceType() ||
30573e4b5cfSWhisperity          ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
306*b9d678d2SJoseph Huber              ArgType.getNonReferenceType(), Ctx);
30773e4b5cfSWhisperity }
30873e4b5cfSWhisperity 
30973e4b5cfSWhisperity static bool isPointerOrArray(QualType TypeToCheck) {
31073e4b5cfSWhisperity   return TypeToCheck->isPointerType() || TypeToCheck->isArrayType();
31173e4b5cfSWhisperity }
31273e4b5cfSWhisperity 
31373e4b5cfSWhisperity /// Checks whether ArgType is an array type identical to ParamType's array type.
31473e4b5cfSWhisperity /// Enforces array elements' qualifier compatibility as well.
315*b9d678d2SJoseph Huber static bool isCompatibleWithArrayReference(QualType ArgType, QualType ParamType,
316*b9d678d2SJoseph Huber                                            const ASTContext &Ctx) {
31773e4b5cfSWhisperity   if (!ArgType->isArrayType())
31873e4b5cfSWhisperity     return false;
31973e4b5cfSWhisperity   // Here, qualifiers belong to the elements of the arrays.
320*b9d678d2SJoseph Huber   if (!ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx))
32173e4b5cfSWhisperity     return false;
32273e4b5cfSWhisperity 
32373e4b5cfSWhisperity   return ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType();
32473e4b5cfSWhisperity }
32573e4b5cfSWhisperity 
32673e4b5cfSWhisperity static QualType convertToPointeeOrArrayElementQualType(QualType TypeToConvert) {
32773e4b5cfSWhisperity   unsigned CVRqualifiers = 0;
32873e4b5cfSWhisperity   // Save array element qualifiers, since getElementType() removes qualifiers
32973e4b5cfSWhisperity   // from array elements.
33073e4b5cfSWhisperity   if (TypeToConvert->isArrayType())
33173e4b5cfSWhisperity     CVRqualifiers = TypeToConvert.getLocalQualifiers().getCVRQualifiers();
33273e4b5cfSWhisperity   TypeToConvert = TypeToConvert->isPointerType()
33373e4b5cfSWhisperity                       ? TypeToConvert->getPointeeType()
33473e4b5cfSWhisperity                       : TypeToConvert->getAsArrayTypeUnsafe()->getElementType();
33573e4b5cfSWhisperity   TypeToConvert = TypeToConvert.withCVRQualifiers(CVRqualifiers);
33673e4b5cfSWhisperity   return TypeToConvert;
33773e4b5cfSWhisperity }
33873e4b5cfSWhisperity 
33973e4b5cfSWhisperity /// Checks if multilevel pointers' qualifiers compatibility continues on the
34073e4b5cfSWhisperity /// current pointer level. For multilevel pointers, C++ permits conversion, if
34173e4b5cfSWhisperity /// every cv-qualifier in ArgType also appears in the corresponding position in
34273e4b5cfSWhisperity /// ParamType, and if PramType has a cv-qualifier that's not in ArgType, then
34373e4b5cfSWhisperity /// every * in ParamType to the right of that cv-qualifier, except the last
34473e4b5cfSWhisperity /// one, must also be const-qualified.
34573e4b5cfSWhisperity static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
346*b9d678d2SJoseph Huber                                            bool &IsParamContinuouslyConst,
347*b9d678d2SJoseph Huber                                            const ASTContext &Ctx) {
34873e4b5cfSWhisperity   // The types are compatible, if the parameter is at least as qualified as the
34973e4b5cfSWhisperity   // argument, and if it is more qualified, it has to be const on upper pointer
35073e4b5cfSWhisperity   // levels.
35173e4b5cfSWhisperity   bool AreTypesQualCompatible =
352*b9d678d2SJoseph Huber       ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx) &&
35373e4b5cfSWhisperity       (!ParamType.hasQualifiers() || IsParamContinuouslyConst);
35473e4b5cfSWhisperity   // Check whether the parameter's constness continues at the current pointer
35573e4b5cfSWhisperity   // level.
35673e4b5cfSWhisperity   IsParamContinuouslyConst &= ParamType.isConstQualified();
35773e4b5cfSWhisperity 
35873e4b5cfSWhisperity   return AreTypesQualCompatible;
35973e4b5cfSWhisperity }
36073e4b5cfSWhisperity 
36173e4b5cfSWhisperity /// Checks whether multilevel pointers are compatible in terms of levels,
36273e4b5cfSWhisperity /// qualifiers and pointee type.
36373e4b5cfSWhisperity static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
364*b9d678d2SJoseph Huber                                       bool IsParamContinuouslyConst,
365*b9d678d2SJoseph Huber                                       const ASTContext &Ctx) {
36673e4b5cfSWhisperity   if (!arePointersStillQualCompatible(ArgType, ParamType,
367*b9d678d2SJoseph Huber                                       IsParamContinuouslyConst, Ctx))
36873e4b5cfSWhisperity     return false;
36973e4b5cfSWhisperity 
37073e4b5cfSWhisperity   do {
37173e4b5cfSWhisperity     // Step down one pointer level.
37273e4b5cfSWhisperity     ArgType = convertToPointeeOrArrayElementQualType(ArgType);
37373e4b5cfSWhisperity     ParamType = convertToPointeeOrArrayElementQualType(ParamType);
37473e4b5cfSWhisperity 
37573e4b5cfSWhisperity     // Check whether cv-qualifiers permit compatibility on
37673e4b5cfSWhisperity     // current level.
37773e4b5cfSWhisperity     if (!arePointersStillQualCompatible(ArgType, ParamType,
378*b9d678d2SJoseph Huber                                         IsParamContinuouslyConst, Ctx))
37973e4b5cfSWhisperity       return false;
38073e4b5cfSWhisperity 
38173e4b5cfSWhisperity     if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
38273e4b5cfSWhisperity       return true;
38373e4b5cfSWhisperity 
38473e4b5cfSWhisperity   } while (ParamType->isPointerType() && ArgType->isPointerType());
38573e4b5cfSWhisperity   // The final type does not match, or pointer levels differ.
38673e4b5cfSWhisperity   return false;
38773e4b5cfSWhisperity }
38873e4b5cfSWhisperity 
38973e4b5cfSWhisperity /// Checks whether ArgType converts implicitly to ParamType.
39073e4b5cfSWhisperity static bool areTypesCompatible(QualType ArgType, QualType ParamType,
39173e4b5cfSWhisperity                                const ASTContext &Ctx) {
39273e4b5cfSWhisperity   if (ArgType.isNull() || ParamType.isNull())
39373e4b5cfSWhisperity     return false;
39473e4b5cfSWhisperity 
39573e4b5cfSWhisperity   ArgType = ArgType.getCanonicalType();
39673e4b5cfSWhisperity   ParamType = ParamType.getCanonicalType();
39773e4b5cfSWhisperity 
39873e4b5cfSWhisperity   if (ArgType == ParamType)
39973e4b5cfSWhisperity     return true;
40073e4b5cfSWhisperity 
40173e4b5cfSWhisperity   // Check for constness and reference compatibility.
402*b9d678d2SJoseph Huber   if (!areRefAndQualCompatible(ArgType, ParamType, Ctx))
40373e4b5cfSWhisperity     return false;
40473e4b5cfSWhisperity 
40573e4b5cfSWhisperity   bool IsParamReference = ParamType->isReferenceType();
40673e4b5cfSWhisperity 
40773e4b5cfSWhisperity   // Reference-ness has already been checked and should be removed
40873e4b5cfSWhisperity   // before further checking.
40973e4b5cfSWhisperity   ArgType = ArgType.getNonReferenceType();
41073e4b5cfSWhisperity   ParamType = ParamType.getNonReferenceType();
41173e4b5cfSWhisperity 
41273e4b5cfSWhisperity   if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
41373e4b5cfSWhisperity     return true;
41473e4b5cfSWhisperity 
41573e4b5cfSWhisperity   // Arithmetic types are interconvertible, except scoped enums.
41673e4b5cfSWhisperity   if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) {
41773e4b5cfSWhisperity     if ((ParamType->isEnumeralType() &&
41886497026SSimon Pilgrim          ParamType->castAs<EnumType>()->getDecl()->isScoped()) ||
41973e4b5cfSWhisperity         (ArgType->isEnumeralType() &&
42086497026SSimon Pilgrim          ArgType->castAs<EnumType>()->getDecl()->isScoped()))
42173e4b5cfSWhisperity       return false;
42273e4b5cfSWhisperity 
42373e4b5cfSWhisperity     return true;
42473e4b5cfSWhisperity   }
42573e4b5cfSWhisperity 
42673e4b5cfSWhisperity   // Check if the argument and the param are both function types (the parameter
42773e4b5cfSWhisperity   // decayed to a function pointer).
42873e4b5cfSWhisperity   if (ArgType->isFunctionType() && ParamType->isFunctionPointerType()) {
42973e4b5cfSWhisperity     ParamType = ParamType->getPointeeType();
43073e4b5cfSWhisperity     return ArgType == ParamType;
43173e4b5cfSWhisperity   }
43273e4b5cfSWhisperity 
43373e4b5cfSWhisperity   // Arrays or pointer arguments convert to array or pointer parameters.
43473e4b5cfSWhisperity   if (!(isPointerOrArray(ArgType) && isPointerOrArray(ParamType)))
43573e4b5cfSWhisperity     return false;
43673e4b5cfSWhisperity 
43773e4b5cfSWhisperity   // When ParamType is an array reference, ArgType has to be of the same-sized
43873e4b5cfSWhisperity   // array-type with cv-compatible element type.
43973e4b5cfSWhisperity   if (IsParamReference && ParamType->isArrayType())
440*b9d678d2SJoseph Huber     return isCompatibleWithArrayReference(ArgType, ParamType, Ctx);
44173e4b5cfSWhisperity 
44273e4b5cfSWhisperity   bool IsParamContinuouslyConst =
44373e4b5cfSWhisperity       !IsParamReference || ParamType.getNonReferenceType().isConstQualified();
44473e4b5cfSWhisperity 
44573e4b5cfSWhisperity   // Remove the first level of indirection.
44673e4b5cfSWhisperity   ArgType = convertToPointeeOrArrayElementQualType(ArgType);
44773e4b5cfSWhisperity   ParamType = convertToPointeeOrArrayElementQualType(ParamType);
44873e4b5cfSWhisperity 
44973e4b5cfSWhisperity   // Check qualifier compatibility on the next level.
450*b9d678d2SJoseph Huber   if (!ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx))
45173e4b5cfSWhisperity     return false;
45273e4b5cfSWhisperity 
45373e4b5cfSWhisperity   if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
45473e4b5cfSWhisperity     return true;
45573e4b5cfSWhisperity 
45673e4b5cfSWhisperity   // At this point, all possible C language implicit conversion were checked.
45773e4b5cfSWhisperity   if (!Ctx.getLangOpts().CPlusPlus)
45873e4b5cfSWhisperity     return false;
45973e4b5cfSWhisperity 
46073e4b5cfSWhisperity   // Check whether ParamType and ArgType were both pointers to a class or a
46173e4b5cfSWhisperity   // struct, and check for inheritance.
46273e4b5cfSWhisperity   if (ParamType->isStructureOrClassType() &&
46373e4b5cfSWhisperity       ArgType->isStructureOrClassType()) {
46473e4b5cfSWhisperity     const auto *ArgDecl = ArgType->getAsCXXRecordDecl();
46573e4b5cfSWhisperity     const auto *ParamDecl = ParamType->getAsCXXRecordDecl();
46673e4b5cfSWhisperity     if (!ArgDecl || !ArgDecl->hasDefinition() || !ParamDecl ||
46773e4b5cfSWhisperity         !ParamDecl->hasDefinition())
46873e4b5cfSWhisperity       return false;
46973e4b5cfSWhisperity 
47073e4b5cfSWhisperity     return ArgDecl->isDerivedFrom(ParamDecl);
47173e4b5cfSWhisperity   }
47273e4b5cfSWhisperity 
47373e4b5cfSWhisperity   // Unless argument and param are both multilevel pointers, the types are not
47473e4b5cfSWhisperity   // convertible.
47573e4b5cfSWhisperity   if (!(ParamType->isAnyPointerType() && ArgType->isAnyPointerType()))
47673e4b5cfSWhisperity     return false;
47773e4b5cfSWhisperity 
478*b9d678d2SJoseph Huber   return arePointerTypesCompatible(ArgType, ParamType, IsParamContinuouslyConst,
479*b9d678d2SJoseph Huber                                    Ctx);
48073e4b5cfSWhisperity }
48173e4b5cfSWhisperity 
48273e4b5cfSWhisperity static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD) {
48373e4b5cfSWhisperity   switch (FD->getOverloadedOperator()) {
48473e4b5cfSWhisperity   case OO_None:
48573e4b5cfSWhisperity   case OO_Call:
48673e4b5cfSWhisperity   case OO_Subscript:
48773e4b5cfSWhisperity   case OO_New:
48873e4b5cfSWhisperity   case OO_Delete:
48973e4b5cfSWhisperity   case OO_Array_New:
49073e4b5cfSWhisperity   case OO_Array_Delete:
49173e4b5cfSWhisperity   case OO_Conditional:
49273e4b5cfSWhisperity   case OO_Coawait:
49373e4b5cfSWhisperity     return false;
49473e4b5cfSWhisperity 
49573e4b5cfSWhisperity   default:
49673e4b5cfSWhisperity     return FD->getNumParams() <= 2;
49773e4b5cfSWhisperity   }
49873e4b5cfSWhisperity }
49973e4b5cfSWhisperity 
50073e4b5cfSWhisperity SuspiciousCallArgumentCheck::SuspiciousCallArgumentCheck(
50173e4b5cfSWhisperity     StringRef Name, ClangTidyContext *Context)
50273e4b5cfSWhisperity     : ClangTidyCheck(Name, Context),
50373e4b5cfSWhisperity       MinimumIdentifierNameLength(Options.get(
50473e4b5cfSWhisperity           "MinimumIdentifierNameLength", DefaultMinimumIdentifierNameLength)) {
50573e4b5cfSWhisperity   auto GetToggleOpt = [this](Heuristic H) -> bool {
50673e4b5cfSWhisperity     auto Idx = static_cast<std::size_t>(H);
50773e4b5cfSWhisperity     assert(Idx < HeuristicCount);
50873e4b5cfSWhisperity     return Options.get(HeuristicToString[Idx], Defaults[Idx].Enabled);
50973e4b5cfSWhisperity   };
51073e4b5cfSWhisperity   auto GetBoundOpt = [this](Heuristic H, BoundKind BK) -> int8_t {
51173e4b5cfSWhisperity     auto Idx = static_cast<std::size_t>(H);
51273e4b5cfSWhisperity     assert(Idx < HeuristicCount);
51373e4b5cfSWhisperity 
51473e4b5cfSWhisperity     SmallString<32> Key = HeuristicToString[Idx];
51573e4b5cfSWhisperity     Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
51673e4b5cfSWhisperity                                                 : "SimilarAbove");
51773e4b5cfSWhisperity     int8_t Default = BK == BoundKind::DissimilarBelow
51873e4b5cfSWhisperity                          ? Defaults[Idx].DissimilarBelow
51973e4b5cfSWhisperity                          : Defaults[Idx].SimilarAbove;
52073e4b5cfSWhisperity     return Options.get(Key, Default);
52173e4b5cfSWhisperity   };
52273e4b5cfSWhisperity   for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
52373e4b5cfSWhisperity     auto H = static_cast<Heuristic>(Idx);
52473e4b5cfSWhisperity     if (GetToggleOpt(H))
52573e4b5cfSWhisperity       AppliedHeuristics.emplace_back(H);
52673e4b5cfSWhisperity     ConfiguredBounds.emplace_back(
52773e4b5cfSWhisperity         std::make_pair(GetBoundOpt(H, BoundKind::DissimilarBelow),
52873e4b5cfSWhisperity                        GetBoundOpt(H, BoundKind::SimilarAbove)));
52973e4b5cfSWhisperity   }
53073e4b5cfSWhisperity 
53112cb5405SNathan James   for (StringRef Abbreviation : optutils::parseStringList(
53273e4b5cfSWhisperity            Options.get("Abbreviations", DefaultAbbreviations))) {
53312cb5405SNathan James     auto KeyAndValue = Abbreviation.split("=");
53473e4b5cfSWhisperity     assert(!KeyAndValue.first.empty() && !KeyAndValue.second.empty());
53573e4b5cfSWhisperity     AbbreviationDictionary.insert(
53612cb5405SNathan James         std::make_pair(KeyAndValue.first, KeyAndValue.second.str()));
53773e4b5cfSWhisperity   }
53873e4b5cfSWhisperity }
53973e4b5cfSWhisperity 
54073e4b5cfSWhisperity void SuspiciousCallArgumentCheck::storeOptions(
54173e4b5cfSWhisperity     ClangTidyOptions::OptionMap &Opts) {
54273e4b5cfSWhisperity   Options.store(Opts, "MinimumIdentifierNameLength",
54373e4b5cfSWhisperity                 MinimumIdentifierNameLength);
54473e4b5cfSWhisperity   const auto &SetToggleOpt = [this, &Opts](Heuristic H) -> void {
54573e4b5cfSWhisperity     auto Idx = static_cast<std::size_t>(H);
54673e4b5cfSWhisperity     Options.store(Opts, HeuristicToString[Idx], isHeuristicEnabled(H));
54773e4b5cfSWhisperity   };
54873e4b5cfSWhisperity   const auto &SetBoundOpt = [this, &Opts](Heuristic H, BoundKind BK) -> void {
54973e4b5cfSWhisperity     auto Idx = static_cast<std::size_t>(H);
55073e4b5cfSWhisperity     assert(Idx < HeuristicCount);
55173e4b5cfSWhisperity     if (!Defaults[Idx].hasBounds())
55273e4b5cfSWhisperity       return;
55373e4b5cfSWhisperity 
55473e4b5cfSWhisperity     SmallString<32> Key = HeuristicToString[Idx];
55573e4b5cfSWhisperity     Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
55673e4b5cfSWhisperity                                                 : "SimilarAbove");
557ed8fceaaSKazu Hirata     Options.store(Opts, Key, *getBound(H, BK));
55873e4b5cfSWhisperity   };
55973e4b5cfSWhisperity 
56073e4b5cfSWhisperity   for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
56173e4b5cfSWhisperity     auto H = static_cast<Heuristic>(Idx);
56273e4b5cfSWhisperity     SetToggleOpt(H);
56373e4b5cfSWhisperity     SetBoundOpt(H, BoundKind::DissimilarBelow);
56473e4b5cfSWhisperity     SetBoundOpt(H, BoundKind::SimilarAbove);
56573e4b5cfSWhisperity   }
56673e4b5cfSWhisperity 
56773e4b5cfSWhisperity   SmallVector<std::string, 32> Abbreviations;
56873e4b5cfSWhisperity   for (const auto &Abbreviation : AbbreviationDictionary) {
56973e4b5cfSWhisperity     SmallString<32> EqualSignJoined;
57073e4b5cfSWhisperity     EqualSignJoined.append(Abbreviation.first());
57173e4b5cfSWhisperity     EqualSignJoined.append("=");
57273e4b5cfSWhisperity     EqualSignJoined.append(Abbreviation.second);
57373e4b5cfSWhisperity 
57473e4b5cfSWhisperity     if (!Abbreviation.second.empty())
57573e4b5cfSWhisperity       Abbreviations.emplace_back(EqualSignJoined.str());
57673e4b5cfSWhisperity   }
57773e4b5cfSWhisperity   Options.store(Opts, "Abbreviations",
57812cb5405SNathan James                 optutils::serializeStringList(std::vector<StringRef>(
57912cb5405SNathan James                     Abbreviations.begin(), Abbreviations.end())));
58073e4b5cfSWhisperity }
58173e4b5cfSWhisperity 
58273e4b5cfSWhisperity bool SuspiciousCallArgumentCheck::isHeuristicEnabled(Heuristic H) const {
58373e4b5cfSWhisperity   return llvm::is_contained(AppliedHeuristics, H);
58473e4b5cfSWhisperity }
58573e4b5cfSWhisperity 
586f71ffd3bSKazu Hirata std::optional<int8_t>
587f71ffd3bSKazu Hirata SuspiciousCallArgumentCheck::getBound(Heuristic H, BoundKind BK) const {
58873e4b5cfSWhisperity   auto Idx = static_cast<std::size_t>(H);
58973e4b5cfSWhisperity   assert(Idx < HeuristicCount);
59073e4b5cfSWhisperity 
59173e4b5cfSWhisperity   if (!Defaults[Idx].hasBounds())
592cd8702efSKazu Hirata     return std::nullopt;
59373e4b5cfSWhisperity 
59473e4b5cfSWhisperity   switch (BK) {
59573e4b5cfSWhisperity   case BoundKind::DissimilarBelow:
59673e4b5cfSWhisperity     return ConfiguredBounds[Idx].first;
59773e4b5cfSWhisperity   case BoundKind::SimilarAbove:
59873e4b5cfSWhisperity     return ConfiguredBounds[Idx].second;
59973e4b5cfSWhisperity   }
60073e4b5cfSWhisperity   llvm_unreachable("Unhandled Bound kind.");
60173e4b5cfSWhisperity }
60273e4b5cfSWhisperity 
60373e4b5cfSWhisperity void SuspiciousCallArgumentCheck::registerMatchers(MatchFinder *Finder) {
60473e4b5cfSWhisperity   // Only match calls with at least 2 arguments.
60573e4b5cfSWhisperity   Finder->addMatcher(
60673e4b5cfSWhisperity       functionDecl(forEachDescendant(callExpr(unless(anyOf(argumentCountIs(0),
60773e4b5cfSWhisperity                                                            argumentCountIs(1))))
60873e4b5cfSWhisperity                                          .bind("functionCall")))
60973e4b5cfSWhisperity           .bind("callingFunc"),
61073e4b5cfSWhisperity       this);
61173e4b5cfSWhisperity }
61273e4b5cfSWhisperity 
61373e4b5cfSWhisperity void SuspiciousCallArgumentCheck::check(
61473e4b5cfSWhisperity     const MatchFinder::MatchResult &Result) {
61573e4b5cfSWhisperity   const auto *MatchedCallExpr =
61673e4b5cfSWhisperity       Result.Nodes.getNodeAs<CallExpr>("functionCall");
61773e4b5cfSWhisperity   const auto *Caller = Result.Nodes.getNodeAs<FunctionDecl>("callingFunc");
61873e4b5cfSWhisperity   assert(MatchedCallExpr && Caller);
61973e4b5cfSWhisperity 
62073e4b5cfSWhisperity   const Decl *CalleeDecl = MatchedCallExpr->getCalleeDecl();
62173e4b5cfSWhisperity   if (!CalleeDecl)
62273e4b5cfSWhisperity     return;
62373e4b5cfSWhisperity 
62473e4b5cfSWhisperity   const FunctionDecl *CalleeFuncDecl = CalleeDecl->getAsFunction();
62573e4b5cfSWhisperity   if (!CalleeFuncDecl)
62673e4b5cfSWhisperity     return;
62773e4b5cfSWhisperity   if (CalleeFuncDecl == Caller)
62873e4b5cfSWhisperity     // Ignore recursive calls.
62973e4b5cfSWhisperity     return;
63073e4b5cfSWhisperity   if (isOverloadedUnaryOrBinarySymbolOperator(CalleeFuncDecl))
63173e4b5cfSWhisperity     return;
63273e4b5cfSWhisperity 
63373e4b5cfSWhisperity   // Get param attributes.
63473e4b5cfSWhisperity   setParamNamesAndTypes(CalleeFuncDecl);
63573e4b5cfSWhisperity 
63673e4b5cfSWhisperity   if (ParamNames.empty())
63773e4b5cfSWhisperity     return;
63873e4b5cfSWhisperity 
63973e4b5cfSWhisperity   // Get Arg attributes.
64073e4b5cfSWhisperity   std::size_t InitialArgIndex = 0;
64173e4b5cfSWhisperity 
64273e4b5cfSWhisperity   if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(CalleeFuncDecl)) {
64373e4b5cfSWhisperity     if (MethodDecl->getParent()->isLambda())
64473e4b5cfSWhisperity       // Lambda functions' first Arg are the lambda object.
64573e4b5cfSWhisperity       InitialArgIndex = 1;
64673e4b5cfSWhisperity     else if (MethodDecl->getOverloadedOperator() == OO_Call)
64773e4b5cfSWhisperity       // For custom operator()s, the first Arg is the called object.
64873e4b5cfSWhisperity       InitialArgIndex = 1;
64973e4b5cfSWhisperity   }
65073e4b5cfSWhisperity 
65173e4b5cfSWhisperity   setArgNamesAndTypes(MatchedCallExpr, InitialArgIndex);
65273e4b5cfSWhisperity 
65373e4b5cfSWhisperity   if (ArgNames.empty())
65473e4b5cfSWhisperity     return;
65573e4b5cfSWhisperity 
65673e4b5cfSWhisperity   std::size_t ParamCount = ParamNames.size();
65773e4b5cfSWhisperity 
65873e4b5cfSWhisperity   // Check similarity.
65973e4b5cfSWhisperity   for (std::size_t I = 0; I < ParamCount; ++I) {
66073e4b5cfSWhisperity     for (std::size_t J = I + 1; J < ParamCount; ++J) {
66173e4b5cfSWhisperity       // Do not check if param or arg names are short, or not convertible.
66273e4b5cfSWhisperity       if (!areParamAndArgComparable(I, J, *Result.Context))
66373e4b5cfSWhisperity         continue;
66473e4b5cfSWhisperity       if (!areArgsSwapped(I, J))
66573e4b5cfSWhisperity         continue;
66673e4b5cfSWhisperity 
66773e4b5cfSWhisperity       // Warning at the call itself.
66873e4b5cfSWhisperity       diag(MatchedCallExpr->getExprLoc(),
66973e4b5cfSWhisperity            "%ordinal0 argument '%1' (passed to '%2') looks like it might be "
67073e4b5cfSWhisperity            "swapped with the %ordinal3, '%4' (passed to '%5')")
67173e4b5cfSWhisperity           << static_cast<unsigned>(I + 1) << ArgNames[I] << ParamNames[I]
67273e4b5cfSWhisperity           << static_cast<unsigned>(J + 1) << ArgNames[J] << ParamNames[J]
67373e4b5cfSWhisperity           << MatchedCallExpr->getArg(I)->getSourceRange()
67473e4b5cfSWhisperity           << MatchedCallExpr->getArg(J)->getSourceRange();
67573e4b5cfSWhisperity 
67673e4b5cfSWhisperity       // Note at the functions declaration.
67773e4b5cfSWhisperity       SourceLocation IParNameLoc =
67873e4b5cfSWhisperity           CalleeFuncDecl->getParamDecl(I)->getLocation();
67973e4b5cfSWhisperity       SourceLocation JParNameLoc =
68073e4b5cfSWhisperity           CalleeFuncDecl->getParamDecl(J)->getLocation();
68173e4b5cfSWhisperity 
68273e4b5cfSWhisperity       diag(CalleeFuncDecl->getLocation(), "in the call to %0, declared here",
68373e4b5cfSWhisperity            DiagnosticIDs::Note)
68473e4b5cfSWhisperity           << CalleeFuncDecl
68573e4b5cfSWhisperity           << CharSourceRange::getTokenRange(IParNameLoc, IParNameLoc)
68673e4b5cfSWhisperity           << CharSourceRange::getTokenRange(JParNameLoc, JParNameLoc);
68773e4b5cfSWhisperity     }
68873e4b5cfSWhisperity   }
68973e4b5cfSWhisperity }
69073e4b5cfSWhisperity 
69173e4b5cfSWhisperity void SuspiciousCallArgumentCheck::setParamNamesAndTypes(
69273e4b5cfSWhisperity     const FunctionDecl *CalleeFuncDecl) {
69373e4b5cfSWhisperity   // Reset vectors, and fill them with the currently checked function's
69473e4b5cfSWhisperity   // parameters' data.
69573e4b5cfSWhisperity   ParamNames.clear();
69673e4b5cfSWhisperity   ParamTypes.clear();
69773e4b5cfSWhisperity 
69873e4b5cfSWhisperity   for (const ParmVarDecl *Param : CalleeFuncDecl->parameters()) {
69973e4b5cfSWhisperity     ParamTypes.push_back(Param->getType());
70073e4b5cfSWhisperity 
70173e4b5cfSWhisperity     if (IdentifierInfo *II = Param->getIdentifier())
70273e4b5cfSWhisperity       ParamNames.push_back(II->getName());
70373e4b5cfSWhisperity     else
70473e4b5cfSWhisperity       ParamNames.push_back(StringRef());
70573e4b5cfSWhisperity   }
70673e4b5cfSWhisperity }
70773e4b5cfSWhisperity 
70873e4b5cfSWhisperity void SuspiciousCallArgumentCheck::setArgNamesAndTypes(
70973e4b5cfSWhisperity     const CallExpr *MatchedCallExpr, std::size_t InitialArgIndex) {
71073e4b5cfSWhisperity   // Reset vectors, and fill them with the currently checked function's
71173e4b5cfSWhisperity   // arguments' data.
71273e4b5cfSWhisperity   ArgNames.clear();
71373e4b5cfSWhisperity   ArgTypes.clear();
71473e4b5cfSWhisperity 
71573e4b5cfSWhisperity   for (std::size_t I = InitialArgIndex, J = MatchedCallExpr->getNumArgs();
71673e4b5cfSWhisperity        I < J; ++I) {
717416e689eSWhisperity     assert(ArgTypes.size() == I - InitialArgIndex &&
718416e689eSWhisperity            ArgNames.size() == ArgTypes.size() &&
719416e689eSWhisperity            "Every iteration must put an element into the vectors!");
720416e689eSWhisperity 
72173e4b5cfSWhisperity     if (const auto *ArgExpr = dyn_cast<DeclRefExpr>(
72273e4b5cfSWhisperity             MatchedCallExpr->getArg(I)->IgnoreUnlessSpelledInSource())) {
72373e4b5cfSWhisperity       if (const auto *Var = dyn_cast<VarDecl>(ArgExpr->getDecl())) {
72473e4b5cfSWhisperity         ArgTypes.push_back(Var->getType());
72573e4b5cfSWhisperity         ArgNames.push_back(Var->getName());
726416e689eSWhisperity         continue;
727416e689eSWhisperity       }
728416e689eSWhisperity       if (const auto *FCall = dyn_cast<FunctionDecl>(ArgExpr->getDecl())) {
729416e689eSWhisperity         if (FCall->getNameInfo().getName().isIdentifier()) {
73073e4b5cfSWhisperity           ArgTypes.push_back(FCall->getType());
73173e4b5cfSWhisperity           ArgNames.push_back(FCall->getName());
732416e689eSWhisperity           continue;
733416e689eSWhisperity         }
734416e689eSWhisperity       }
735416e689eSWhisperity     }
736416e689eSWhisperity 
73773e4b5cfSWhisperity     ArgTypes.push_back(QualType());
73873e4b5cfSWhisperity     ArgNames.push_back(StringRef());
73973e4b5cfSWhisperity   }
74073e4b5cfSWhisperity }
74173e4b5cfSWhisperity 
74273e4b5cfSWhisperity bool SuspiciousCallArgumentCheck::areParamAndArgComparable(
74373e4b5cfSWhisperity     std::size_t Position1, std::size_t Position2, const ASTContext &Ctx) const {
74473e4b5cfSWhisperity   if (Position1 >= ArgNames.size() || Position2 >= ArgNames.size())
74573e4b5cfSWhisperity     return false;
74673e4b5cfSWhisperity 
74773e4b5cfSWhisperity   // Do not report for too short strings.
74873e4b5cfSWhisperity   if (ArgNames[Position1].size() < MinimumIdentifierNameLength ||
74973e4b5cfSWhisperity       ArgNames[Position2].size() < MinimumIdentifierNameLength ||
75073e4b5cfSWhisperity       ParamNames[Position1].size() < MinimumIdentifierNameLength ||
75173e4b5cfSWhisperity       ParamNames[Position2].size() < MinimumIdentifierNameLength)
75273e4b5cfSWhisperity     return false;
75373e4b5cfSWhisperity 
75473e4b5cfSWhisperity   if (!areTypesCompatible(ArgTypes[Position1], ParamTypes[Position2], Ctx) ||
75573e4b5cfSWhisperity       !areTypesCompatible(ArgTypes[Position2], ParamTypes[Position1], Ctx))
75673e4b5cfSWhisperity     return false;
75773e4b5cfSWhisperity 
75873e4b5cfSWhisperity   return true;
75973e4b5cfSWhisperity }
76073e4b5cfSWhisperity 
76173e4b5cfSWhisperity bool SuspiciousCallArgumentCheck::areArgsSwapped(std::size_t Position1,
76273e4b5cfSWhisperity                                                  std::size_t Position2) const {
76373e4b5cfSWhisperity   for (Heuristic H : AppliedHeuristics) {
76473e4b5cfSWhisperity     bool A1ToP2Similar = areNamesSimilar(
76573e4b5cfSWhisperity         ArgNames[Position2], ParamNames[Position1], H, BoundKind::SimilarAbove);
76673e4b5cfSWhisperity     bool A2ToP1Similar = areNamesSimilar(
76773e4b5cfSWhisperity         ArgNames[Position1], ParamNames[Position2], H, BoundKind::SimilarAbove);
76873e4b5cfSWhisperity 
76973e4b5cfSWhisperity     bool A1ToP1Dissimilar =
77073e4b5cfSWhisperity         !areNamesSimilar(ArgNames[Position1], ParamNames[Position1], H,
77173e4b5cfSWhisperity                          BoundKind::DissimilarBelow);
77273e4b5cfSWhisperity     bool A2ToP2Dissimilar =
77373e4b5cfSWhisperity         !areNamesSimilar(ArgNames[Position2], ParamNames[Position2], H,
77473e4b5cfSWhisperity                          BoundKind::DissimilarBelow);
77573e4b5cfSWhisperity 
77673e4b5cfSWhisperity     if ((A1ToP2Similar || A2ToP1Similar) && A1ToP1Dissimilar &&
77773e4b5cfSWhisperity         A2ToP2Dissimilar)
77873e4b5cfSWhisperity       return true;
77973e4b5cfSWhisperity   }
78073e4b5cfSWhisperity   return false;
78173e4b5cfSWhisperity }
78273e4b5cfSWhisperity 
78373e4b5cfSWhisperity bool SuspiciousCallArgumentCheck::areNamesSimilar(StringRef Arg,
78473e4b5cfSWhisperity                                                   StringRef Param, Heuristic H,
78573e4b5cfSWhisperity                                                   BoundKind BK) const {
78673e4b5cfSWhisperity   int8_t Threshold = -1;
787f71ffd3bSKazu Hirata   if (std::optional<int8_t> GotBound = getBound(H, BK))
788ed8fceaaSKazu Hirata     Threshold = *GotBound;
78973e4b5cfSWhisperity 
79073e4b5cfSWhisperity   switch (H) {
79173e4b5cfSWhisperity   case Heuristic::Equality:
79273e4b5cfSWhisperity     return applyEqualityHeuristic(Arg, Param);
79373e4b5cfSWhisperity   case Heuristic::Abbreviation:
79473e4b5cfSWhisperity     return applyAbbreviationHeuristic(AbbreviationDictionary, Arg, Param);
79573e4b5cfSWhisperity   case Heuristic::Prefix:
79673e4b5cfSWhisperity     return applyPrefixHeuristic(Arg, Param, Threshold);
79773e4b5cfSWhisperity   case Heuristic::Suffix:
79873e4b5cfSWhisperity     return applySuffixHeuristic(Arg, Param, Threshold);
79973e4b5cfSWhisperity   case Heuristic::Substring:
80073e4b5cfSWhisperity     return applySubstringHeuristic(Arg, Param, Threshold);
80173e4b5cfSWhisperity   case Heuristic::Levenshtein:
80273e4b5cfSWhisperity     return applyLevenshteinHeuristic(Arg, Param, Threshold);
80373e4b5cfSWhisperity   case Heuristic::JaroWinkler:
80473e4b5cfSWhisperity     return applyJaroWinklerHeuristic(Arg, Param, Threshold);
80573e4b5cfSWhisperity   case Heuristic::Dice:
80673e4b5cfSWhisperity     return applyDiceHeuristic(Arg, Param, Threshold);
80773e4b5cfSWhisperity   }
80873e4b5cfSWhisperity   llvm_unreachable("Unhandled heuristic kind");
80973e4b5cfSWhisperity }
81073e4b5cfSWhisperity 
8117d2ea6c4SCarlos Galvez } // namespace clang::tidy::readability
812