1 //===--- SuspiciousCallArgumentCheck.cpp - clang-tidy ---------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "SuspiciousCallArgumentCheck.h"
10 #include "../utils/OptionsUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Type.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include <sstream>
15 
16 using namespace clang::ast_matchers;
17 namespace optutils = clang::tidy::utils::options;
18 
19 namespace clang {
20 namespace tidy {
21 namespace readability {
22 
23 namespace {
24 struct DefaultHeuristicConfiguration {
25   /// Whether the heuristic is to be enabled by default.
26   const bool Enabled;
27 
28   /// The upper bound of % of similarity the two strings might have to be
29   /// considered dissimilar.
30   /// (For purposes of configuration, -1 if the heuristic is not configurable
31   /// with bounds.)
32   const int8_t DissimilarBelow;
33 
34   /// The lower bound of % of similarity the two string must have to be
35   /// considered similar.
36   /// (For purposes of configuration, -1 if the heuristic is not configurable
37   /// with bounds.)
38   const int8_t SimilarAbove;
39 
40   /// Can the heuristic be configured with bounds?
hasBoundsclang::tidy::readability::__anon91f1173b0111::DefaultHeuristicConfiguration41   bool hasBounds() const { return DissimilarBelow > -1 && SimilarAbove > -1; }
42 };
43 } // namespace
44 
45 static constexpr std::size_t DefaultMinimumIdentifierNameLength = 3;
46 
47 static constexpr StringRef HeuristicToString[] = {
48     "Equality",  "Abbreviation", "Prefix",      "Suffix",
49     "Substring", "Levenshtein",  "JaroWinkler", "Dice"};
50 
51 static constexpr DefaultHeuristicConfiguration Defaults[] = {
52     {true, -1, -1}, // Equality.
53     {true, -1, -1}, // Abbreviation.
54     {true, 25, 30}, // Prefix.
55     {true, 25, 30}, // Suffix.
56     {true, 40, 50}, // Substring.
57     {true, 50, 66}, // Levenshtein.
58     {true, 75, 85}, // Jaro-Winkler.
59     {true, 60, 70}, // Dice.
60 };
61 
62 static_assert(
63     sizeof(HeuristicToString) / sizeof(HeuristicToString[0]) ==
64         SuspiciousCallArgumentCheck::HeuristicCount,
65     "Ensure that every heuristic has a corresponding stringified name");
66 static_assert(sizeof(Defaults) / sizeof(Defaults[0]) ==
67                   SuspiciousCallArgumentCheck::HeuristicCount,
68               "Ensure that every heuristic has a default configuration.");
69 
70 namespace {
71 template <std::size_t I> struct HasWellConfiguredBounds {
72   static constexpr bool Value =
73       !((Defaults[I].DissimilarBelow == -1) ^ (Defaults[I].SimilarAbove == -1));
74   static_assert(Value, "A heuristic must either have a dissimilarity and "
75                        "similarity bound, or neither!");
76 };
77 
78 template <std::size_t I> struct HasWellConfiguredBoundsFold {
79   static constexpr bool Value = HasWellConfiguredBounds<I>::Value &&
80                                 HasWellConfiguredBoundsFold<I - 1>::Value;
81 };
82 
83 template <> struct HasWellConfiguredBoundsFold<0> {
84   static constexpr bool Value = HasWellConfiguredBounds<0>::Value;
85 };
86 
87 struct AllHeuristicsBoundsWellConfigured {
88   static constexpr bool Value =
89       HasWellConfiguredBoundsFold<SuspiciousCallArgumentCheck::HeuristicCount -
90                                   1>::Value;
91 };
92 
93 static_assert(AllHeuristicsBoundsWellConfigured::Value, "");
94 } // namespace
95 
96 static constexpr llvm::StringLiteral DefaultAbbreviations = "addr=address;"
97                                                             "arr=array;"
98                                                             "attr=attribute;"
99                                                             "buf=buffer;"
100                                                             "cl=client;"
101                                                             "cnt=count;"
102                                                             "col=column;"
103                                                             "cpy=copy;"
104                                                             "dest=destination;"
105                                                             "dist=distance"
106                                                             "dst=distance;"
107                                                             "elem=element;"
108                                                             "hght=height;"
109                                                             "i=index;"
110                                                             "idx=index;"
111                                                             "len=length;"
112                                                             "ln=line;"
113                                                             "lst=list;"
114                                                             "nr=number;"
115                                                             "num=number;"
116                                                             "pos=position;"
117                                                             "ptr=pointer;"
118                                                             "ref=reference;"
119                                                             "src=source;"
120                                                             "srv=server;"
121                                                             "stmt=statement;"
122                                                             "str=string;"
123                                                             "val=value;"
124                                                             "var=variable;"
125                                                             "vec=vector;"
126                                                             "wdth=width";
127 
128 static constexpr std::size_t SmallVectorSize =
129     SuspiciousCallArgumentCheck::SmallVectorSize;
130 
131 /// Returns how many % X is of Y.
percentage(double X,double Y)132 static inline double percentage(double X, double Y) { return X / Y * 100.0; }
133 
applyEqualityHeuristic(StringRef Arg,StringRef Param)134 static bool applyEqualityHeuristic(StringRef Arg, StringRef Param) {
135   return Arg.equals_insensitive(Param);
136 }
137 
applyAbbreviationHeuristic(const llvm::StringMap<std::string> & AbbreviationDictionary,StringRef Arg,StringRef Param)138 static bool applyAbbreviationHeuristic(
139     const llvm::StringMap<std::string> &AbbreviationDictionary, StringRef Arg,
140     StringRef Param) {
141   if (AbbreviationDictionary.find(Arg) != AbbreviationDictionary.end() &&
142       Param.equals(AbbreviationDictionary.lookup(Arg)))
143     return true;
144 
145   if (AbbreviationDictionary.find(Param) != AbbreviationDictionary.end() &&
146       Arg.equals(AbbreviationDictionary.lookup(Param)))
147     return true;
148 
149   return false;
150 }
151 
152 /// Check whether the shorter String is a prefix of the longer String.
applyPrefixHeuristic(StringRef Arg,StringRef Param,int8_t Threshold)153 static bool applyPrefixHeuristic(StringRef Arg, StringRef Param,
154                                  int8_t Threshold) {
155   StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
156   StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
157 
158   if (Longer.startswith_insensitive(Shorter))
159     return percentage(Shorter.size(), Longer.size()) > Threshold;
160 
161   return false;
162 }
163 
164 /// Check whether the shorter String is a suffix of the longer String.
applySuffixHeuristic(StringRef Arg,StringRef Param,int8_t Threshold)165 static bool applySuffixHeuristic(StringRef Arg, StringRef Param,
166                                  int8_t Threshold) {
167   StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
168   StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
169 
170   if (Longer.endswith_insensitive(Shorter))
171     return percentage(Shorter.size(), Longer.size()) > Threshold;
172 
173   return false;
174 }
175 
applySubstringHeuristic(StringRef Arg,StringRef Param,int8_t Threshold)176 static bool applySubstringHeuristic(StringRef Arg, StringRef Param,
177                                     int8_t Threshold) {
178 
179   std::size_t MaxLength = 0;
180   SmallVector<std::size_t, SmallVectorSize> Current(Param.size());
181   SmallVector<std::size_t, SmallVectorSize> Previous(Param.size());
182   std::string ArgLower = Arg.lower();
183   std::string ParamLower = Param.lower();
184 
185   for (std::size_t I = 0; I < Arg.size(); ++I) {
186     for (std::size_t J = 0; J < Param.size(); ++J) {
187       if (ArgLower[I] == ParamLower[J]) {
188         if (I == 0 || J == 0)
189           Current[J] = 1;
190         else
191           Current[J] = 1 + Previous[J - 1];
192 
193         MaxLength = std::max(MaxLength, Current[J]);
194       } else
195         Current[J] = 0;
196     }
197 
198     Current.swap(Previous);
199   }
200 
201   size_t LongerLength = std::max(Arg.size(), Param.size());
202   return percentage(MaxLength, LongerLength) > Threshold;
203 }
204 
applyLevenshteinHeuristic(StringRef Arg,StringRef Param,int8_t Threshold)205 static bool applyLevenshteinHeuristic(StringRef Arg, StringRef Param,
206                                       int8_t Threshold) {
207   std::size_t LongerLength = std::max(Arg.size(), Param.size());
208   double Dist = Arg.edit_distance(Param);
209   Dist = (1.0 - Dist / LongerLength) * 100.0;
210   return Dist > Threshold;
211 }
212 
213 // Based on http://en.wikipedia.org/wiki/Jaro–Winkler_distance.
applyJaroWinklerHeuristic(StringRef Arg,StringRef Param,int8_t Threshold)214 static bool applyJaroWinklerHeuristic(StringRef Arg, StringRef Param,
215                                       int8_t Threshold) {
216   std::size_t Match = 0, Transpos = 0;
217   std::ptrdiff_t ArgLen = Arg.size();
218   std::ptrdiff_t ParamLen = Param.size();
219   SmallVector<int, SmallVectorSize> ArgFlags(ArgLen);
220   SmallVector<int, SmallVectorSize> ParamFlags(ParamLen);
221   std::ptrdiff_t Range =
222       std::max(std::ptrdiff_t{0}, std::max(ArgLen, ParamLen) / 2 - 1);
223 
224   // Calculate matching characters.
225   for (std::ptrdiff_t I = 0; I < ParamLen; ++I)
226     for (std::ptrdiff_t J = std::max(I - Range, std::ptrdiff_t{0}),
227                         L = std::min(I + Range + 1, ArgLen);
228          J < L; ++J)
229       if (tolower(Param[I]) == tolower(Arg[J]) && !ArgFlags[J]) {
230         ArgFlags[J] = 1;
231         ParamFlags[I] = 1;
232         ++Match;
233         break;
234       }
235 
236   if (!Match)
237     return false;
238 
239   // Calculate character transpositions.
240   std::ptrdiff_t L = 0;
241   for (std::ptrdiff_t I = 0; I < ParamLen; ++I) {
242     if (ParamFlags[I] == 1) {
243       std::ptrdiff_t J;
244       for (J = L; J < ArgLen; ++J)
245         if (ArgFlags[J] == 1) {
246           L = J + 1;
247           break;
248         }
249 
250       if (tolower(Param[I]) != tolower(Arg[J]))
251         ++Transpos;
252     }
253   }
254   Transpos /= 2;
255 
256   // Jaro distance.
257   double MatchD = Match;
258   double Dist = ((MatchD / ArgLen) + (MatchD / ParamLen) +
259                  ((MatchD - Transpos) / Match)) /
260                 3.0;
261 
262   // Calculate common string prefix up to 4 chars.
263   L = 0;
264   for (std::ptrdiff_t I = 0;
265        I < std::min(std::min(ArgLen, ParamLen), std::ptrdiff_t{4}); ++I)
266     if (tolower(Arg[I]) == tolower(Param[I]))
267       ++L;
268 
269   // Jaro-Winkler distance.
270   Dist = (Dist + (L * 0.1 * (1.0 - Dist))) * 100.0;
271   return Dist > Threshold;
272 }
273 
274 // Based on http://en.wikipedia.org/wiki/Sørensen–Dice_coefficient
applyDiceHeuristic(StringRef Arg,StringRef Param,int8_t Threshold)275 static bool applyDiceHeuristic(StringRef Arg, StringRef Param,
276                                int8_t Threshold) {
277   llvm::StringSet<> ArgBigrams;
278   llvm::StringSet<> ParamBigrams;
279 
280   // Extract character bigrams from Arg.
281   for (std::ptrdiff_t I = 0; I < static_cast<std::ptrdiff_t>(Arg.size()) - 1;
282        ++I)
283     ArgBigrams.insert(Arg.substr(I, 2).lower());
284 
285   // Extract character bigrams from Param.
286   for (std::ptrdiff_t I = 0; I < static_cast<std::ptrdiff_t>(Param.size()) - 1;
287        ++I)
288     ParamBigrams.insert(Param.substr(I, 2).lower());
289 
290   std::size_t Intersection = 0;
291 
292   // Find the intersection between the two sets.
293   for (auto IT = ParamBigrams.begin(); IT != ParamBigrams.end(); ++IT)
294     Intersection += ArgBigrams.count((IT->getKey()));
295 
296   // Calculate Dice coefficient.
297   return percentage(Intersection * 2.0,
298                     ArgBigrams.size() + ParamBigrams.size()) > Threshold;
299 }
300 
301 /// Checks if ArgType binds to ParamType regarding reference-ness and
302 /// cv-qualifiers.
areRefAndQualCompatible(QualType ArgType,QualType ParamType)303 static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) {
304   return !ParamType->isReferenceType() ||
305          ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
306              ArgType.getNonReferenceType());
307 }
308 
isPointerOrArray(QualType TypeToCheck)309 static bool isPointerOrArray(QualType TypeToCheck) {
310   return TypeToCheck->isPointerType() || TypeToCheck->isArrayType();
311 }
312 
313 /// Checks whether ArgType is an array type identical to ParamType's array type.
314 /// Enforces array elements' qualifier compatibility as well.
isCompatibleWithArrayReference(QualType ArgType,QualType ParamType)315 static bool isCompatibleWithArrayReference(QualType ArgType,
316                                            QualType ParamType) {
317   if (!ArgType->isArrayType())
318     return false;
319   // Here, qualifiers belong to the elements of the arrays.
320   if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
321     return false;
322 
323   return ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType();
324 }
325 
convertToPointeeOrArrayElementQualType(QualType TypeToConvert)326 static QualType convertToPointeeOrArrayElementQualType(QualType TypeToConvert) {
327   unsigned CVRqualifiers = 0;
328   // Save array element qualifiers, since getElementType() removes qualifiers
329   // from array elements.
330   if (TypeToConvert->isArrayType())
331     CVRqualifiers = TypeToConvert.getLocalQualifiers().getCVRQualifiers();
332   TypeToConvert = TypeToConvert->isPointerType()
333                       ? TypeToConvert->getPointeeType()
334                       : TypeToConvert->getAsArrayTypeUnsafe()->getElementType();
335   TypeToConvert = TypeToConvert.withCVRQualifiers(CVRqualifiers);
336   return TypeToConvert;
337 }
338 
339 /// Checks if multilevel pointers' qualifiers compatibility continues on the
340 /// current pointer level. For multilevel pointers, C++ permits conversion, if
341 /// every cv-qualifier in ArgType also appears in the corresponding position in
342 /// ParamType, and if PramType has a cv-qualifier that's not in ArgType, then
343 /// every * in ParamType to the right of that cv-qualifier, except the last
344 /// one, must also be const-qualified.
arePointersStillQualCompatible(QualType ArgType,QualType ParamType,bool & IsParamContinuouslyConst)345 static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
346                                            bool &IsParamContinuouslyConst) {
347   // The types are compatible, if the parameter is at least as qualified as the
348   // argument, and if it is more qualified, it has to be const on upper pointer
349   // levels.
350   bool AreTypesQualCompatible =
351       ParamType.isAtLeastAsQualifiedAs(ArgType) &&
352       (!ParamType.hasQualifiers() || IsParamContinuouslyConst);
353   // Check whether the parameter's constness continues at the current pointer
354   // level.
355   IsParamContinuouslyConst &= ParamType.isConstQualified();
356 
357   return AreTypesQualCompatible;
358 }
359 
360 /// Checks whether multilevel pointers are compatible in terms of levels,
361 /// qualifiers and pointee type.
arePointerTypesCompatible(QualType ArgType,QualType ParamType,bool IsParamContinuouslyConst)362 static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
363                                       bool IsParamContinuouslyConst) {
364   if (!arePointersStillQualCompatible(ArgType, ParamType,
365                                       IsParamContinuouslyConst))
366     return false;
367 
368   do {
369     // Step down one pointer level.
370     ArgType = convertToPointeeOrArrayElementQualType(ArgType);
371     ParamType = convertToPointeeOrArrayElementQualType(ParamType);
372 
373     // Check whether cv-qualifiers permit compatibility on
374     // current level.
375     if (!arePointersStillQualCompatible(ArgType, ParamType,
376                                         IsParamContinuouslyConst))
377       return false;
378 
379     if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
380       return true;
381 
382   } while (ParamType->isPointerType() && ArgType->isPointerType());
383   // The final type does not match, or pointer levels differ.
384   return false;
385 }
386 
387 /// Checks whether ArgType converts implicitly to ParamType.
areTypesCompatible(QualType ArgType,QualType ParamType,const ASTContext & Ctx)388 static bool areTypesCompatible(QualType ArgType, QualType ParamType,
389                                const ASTContext &Ctx) {
390   if (ArgType.isNull() || ParamType.isNull())
391     return false;
392 
393   ArgType = ArgType.getCanonicalType();
394   ParamType = ParamType.getCanonicalType();
395 
396   if (ArgType == ParamType)
397     return true;
398 
399   // Check for constness and reference compatibility.
400   if (!areRefAndQualCompatible(ArgType, ParamType))
401     return false;
402 
403   bool IsParamReference = ParamType->isReferenceType();
404 
405   // Reference-ness has already been checked and should be removed
406   // before further checking.
407   ArgType = ArgType.getNonReferenceType();
408   ParamType = ParamType.getNonReferenceType();
409 
410   if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
411     return true;
412 
413   // Arithmetic types are interconvertible, except scoped enums.
414   if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) {
415     if ((ParamType->isEnumeralType() &&
416          ParamType->castAs<EnumType>()->getDecl()->isScoped()) ||
417         (ArgType->isEnumeralType() &&
418          ArgType->castAs<EnumType>()->getDecl()->isScoped()))
419       return false;
420 
421     return true;
422   }
423 
424   // Check if the argument and the param are both function types (the parameter
425   // decayed to a function pointer).
426   if (ArgType->isFunctionType() && ParamType->isFunctionPointerType()) {
427     ParamType = ParamType->getPointeeType();
428     return ArgType == ParamType;
429   }
430 
431   // Arrays or pointer arguments convert to array or pointer parameters.
432   if (!(isPointerOrArray(ArgType) && isPointerOrArray(ParamType)))
433     return false;
434 
435   // When ParamType is an array reference, ArgType has to be of the same-sized
436   // array-type with cv-compatible element type.
437   if (IsParamReference && ParamType->isArrayType())
438     return isCompatibleWithArrayReference(ArgType, ParamType);
439 
440   bool IsParamContinuouslyConst =
441       !IsParamReference || ParamType.getNonReferenceType().isConstQualified();
442 
443   // Remove the first level of indirection.
444   ArgType = convertToPointeeOrArrayElementQualType(ArgType);
445   ParamType = convertToPointeeOrArrayElementQualType(ParamType);
446 
447   // Check qualifier compatibility on the next level.
448   if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
449     return false;
450 
451   if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
452     return true;
453 
454   // At this point, all possible C language implicit conversion were checked.
455   if (!Ctx.getLangOpts().CPlusPlus)
456     return false;
457 
458   // Check whether ParamType and ArgType were both pointers to a class or a
459   // struct, and check for inheritance.
460   if (ParamType->isStructureOrClassType() &&
461       ArgType->isStructureOrClassType()) {
462     const auto *ArgDecl = ArgType->getAsCXXRecordDecl();
463     const auto *ParamDecl = ParamType->getAsCXXRecordDecl();
464     if (!ArgDecl || !ArgDecl->hasDefinition() || !ParamDecl ||
465         !ParamDecl->hasDefinition())
466       return false;
467 
468     return ArgDecl->isDerivedFrom(ParamDecl);
469   }
470 
471   // Unless argument and param are both multilevel pointers, the types are not
472   // convertible.
473   if (!(ParamType->isAnyPointerType() && ArgType->isAnyPointerType()))
474     return false;
475 
476   return arePointerTypesCompatible(ArgType, ParamType,
477                                    IsParamContinuouslyConst);
478 }
479 
isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl * FD)480 static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD) {
481   switch (FD->getOverloadedOperator()) {
482   case OO_None:
483   case OO_Call:
484   case OO_Subscript:
485   case OO_New:
486   case OO_Delete:
487   case OO_Array_New:
488   case OO_Array_Delete:
489   case OO_Conditional:
490   case OO_Coawait:
491     return false;
492 
493   default:
494     return FD->getNumParams() <= 2;
495   }
496 }
497 
SuspiciousCallArgumentCheck(StringRef Name,ClangTidyContext * Context)498 SuspiciousCallArgumentCheck::SuspiciousCallArgumentCheck(
499     StringRef Name, ClangTidyContext *Context)
500     : ClangTidyCheck(Name, Context),
501       MinimumIdentifierNameLength(Options.get(
502           "MinimumIdentifierNameLength", DefaultMinimumIdentifierNameLength)) {
503   auto GetToggleOpt = [this](Heuristic H) -> bool {
504     auto Idx = static_cast<std::size_t>(H);
505     assert(Idx < HeuristicCount);
506     return Options.get(HeuristicToString[Idx], Defaults[Idx].Enabled);
507   };
508   auto GetBoundOpt = [this](Heuristic H, BoundKind BK) -> int8_t {
509     auto Idx = static_cast<std::size_t>(H);
510     assert(Idx < HeuristicCount);
511 
512     SmallString<32> Key = HeuristicToString[Idx];
513     Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
514                                                 : "SimilarAbove");
515     int8_t Default = BK == BoundKind::DissimilarBelow
516                          ? Defaults[Idx].DissimilarBelow
517                          : Defaults[Idx].SimilarAbove;
518     return Options.get(Key, Default);
519   };
520   for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
521     auto H = static_cast<Heuristic>(Idx);
522     if (GetToggleOpt(H))
523       AppliedHeuristics.emplace_back(H);
524     ConfiguredBounds.emplace_back(
525         std::make_pair(GetBoundOpt(H, BoundKind::DissimilarBelow),
526                        GetBoundOpt(H, BoundKind::SimilarAbove)));
527   }
528 
529   for (StringRef Abbreviation : optutils::parseStringList(
530            Options.get("Abbreviations", DefaultAbbreviations))) {
531     auto KeyAndValue = Abbreviation.split("=");
532     assert(!KeyAndValue.first.empty() && !KeyAndValue.second.empty());
533     AbbreviationDictionary.insert(
534         std::make_pair(KeyAndValue.first, KeyAndValue.second.str()));
535   }
536 }
537 
storeOptions(ClangTidyOptions::OptionMap & Opts)538 void SuspiciousCallArgumentCheck::storeOptions(
539     ClangTidyOptions::OptionMap &Opts) {
540   Options.store(Opts, "MinimumIdentifierNameLength",
541                 MinimumIdentifierNameLength);
542   const auto &SetToggleOpt = [this, &Opts](Heuristic H) -> void {
543     auto Idx = static_cast<std::size_t>(H);
544     Options.store(Opts, HeuristicToString[Idx], isHeuristicEnabled(H));
545   };
546   const auto &SetBoundOpt = [this, &Opts](Heuristic H, BoundKind BK) -> void {
547     auto Idx = static_cast<std::size_t>(H);
548     assert(Idx < HeuristicCount);
549     if (!Defaults[Idx].hasBounds())
550       return;
551 
552     SmallString<32> Key = HeuristicToString[Idx];
553     Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
554                                                 : "SimilarAbove");
555     Options.store(Opts, Key, *getBound(H, BK));
556   };
557 
558   for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
559     auto H = static_cast<Heuristic>(Idx);
560     SetToggleOpt(H);
561     SetBoundOpt(H, BoundKind::DissimilarBelow);
562     SetBoundOpt(H, BoundKind::SimilarAbove);
563   }
564 
565   SmallVector<std::string, 32> Abbreviations;
566   for (const auto &Abbreviation : AbbreviationDictionary) {
567     SmallString<32> EqualSignJoined;
568     EqualSignJoined.append(Abbreviation.first());
569     EqualSignJoined.append("=");
570     EqualSignJoined.append(Abbreviation.second);
571 
572     if (!Abbreviation.second.empty())
573       Abbreviations.emplace_back(EqualSignJoined.str());
574   }
575   Options.store(Opts, "Abbreviations",
576                 optutils::serializeStringList(std::vector<StringRef>(
577                     Abbreviations.begin(), Abbreviations.end())));
578 }
579 
isHeuristicEnabled(Heuristic H) const580 bool SuspiciousCallArgumentCheck::isHeuristicEnabled(Heuristic H) const {
581   return llvm::is_contained(AppliedHeuristics, H);
582 }
583 
getBound(Heuristic H,BoundKind BK) const584 Optional<int8_t> SuspiciousCallArgumentCheck::getBound(Heuristic H,
585                                                        BoundKind BK) const {
586   auto Idx = static_cast<std::size_t>(H);
587   assert(Idx < HeuristicCount);
588 
589   if (!Defaults[Idx].hasBounds())
590     return None;
591 
592   switch (BK) {
593   case BoundKind::DissimilarBelow:
594     return ConfiguredBounds[Idx].first;
595   case BoundKind::SimilarAbove:
596     return ConfiguredBounds[Idx].second;
597   }
598   llvm_unreachable("Unhandled Bound kind.");
599 }
600 
registerMatchers(MatchFinder * Finder)601 void SuspiciousCallArgumentCheck::registerMatchers(MatchFinder *Finder) {
602   // Only match calls with at least 2 arguments.
603   Finder->addMatcher(
604       functionDecl(forEachDescendant(callExpr(unless(anyOf(argumentCountIs(0),
605                                                            argumentCountIs(1))))
606                                          .bind("functionCall")))
607           .bind("callingFunc"),
608       this);
609 }
610 
check(const MatchFinder::MatchResult & Result)611 void SuspiciousCallArgumentCheck::check(
612     const MatchFinder::MatchResult &Result) {
613   const auto *MatchedCallExpr =
614       Result.Nodes.getNodeAs<CallExpr>("functionCall");
615   const auto *Caller = Result.Nodes.getNodeAs<FunctionDecl>("callingFunc");
616   assert(MatchedCallExpr && Caller);
617 
618   const Decl *CalleeDecl = MatchedCallExpr->getCalleeDecl();
619   if (!CalleeDecl)
620     return;
621 
622   const FunctionDecl *CalleeFuncDecl = CalleeDecl->getAsFunction();
623   if (!CalleeFuncDecl)
624     return;
625   if (CalleeFuncDecl == Caller)
626     // Ignore recursive calls.
627     return;
628   if (isOverloadedUnaryOrBinarySymbolOperator(CalleeFuncDecl))
629     return;
630 
631   // Get param attributes.
632   setParamNamesAndTypes(CalleeFuncDecl);
633 
634   if (ParamNames.empty())
635     return;
636 
637   // Get Arg attributes.
638   std::size_t InitialArgIndex = 0;
639 
640   if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(CalleeFuncDecl)) {
641     if (MethodDecl->getParent()->isLambda())
642       // Lambda functions' first Arg are the lambda object.
643       InitialArgIndex = 1;
644     else if (MethodDecl->getOverloadedOperator() == OO_Call)
645       // For custom operator()s, the first Arg is the called object.
646       InitialArgIndex = 1;
647   }
648 
649   setArgNamesAndTypes(MatchedCallExpr, InitialArgIndex);
650 
651   if (ArgNames.empty())
652     return;
653 
654   std::size_t ParamCount = ParamNames.size();
655 
656   // Check similarity.
657   for (std::size_t I = 0; I < ParamCount; ++I) {
658     for (std::size_t J = I + 1; J < ParamCount; ++J) {
659       // Do not check if param or arg names are short, or not convertible.
660       if (!areParamAndArgComparable(I, J, *Result.Context))
661         continue;
662       if (!areArgsSwapped(I, J))
663         continue;
664 
665       // Warning at the call itself.
666       diag(MatchedCallExpr->getExprLoc(),
667            "%ordinal0 argument '%1' (passed to '%2') looks like it might be "
668            "swapped with the %ordinal3, '%4' (passed to '%5')")
669           << static_cast<unsigned>(I + 1) << ArgNames[I] << ParamNames[I]
670           << static_cast<unsigned>(J + 1) << ArgNames[J] << ParamNames[J]
671           << MatchedCallExpr->getArg(I)->getSourceRange()
672           << MatchedCallExpr->getArg(J)->getSourceRange();
673 
674       // Note at the functions declaration.
675       SourceLocation IParNameLoc =
676           CalleeFuncDecl->getParamDecl(I)->getLocation();
677       SourceLocation JParNameLoc =
678           CalleeFuncDecl->getParamDecl(J)->getLocation();
679 
680       diag(CalleeFuncDecl->getLocation(), "in the call to %0, declared here",
681            DiagnosticIDs::Note)
682           << CalleeFuncDecl
683           << CharSourceRange::getTokenRange(IParNameLoc, IParNameLoc)
684           << CharSourceRange::getTokenRange(JParNameLoc, JParNameLoc);
685     }
686   }
687 }
688 
setParamNamesAndTypes(const FunctionDecl * CalleeFuncDecl)689 void SuspiciousCallArgumentCheck::setParamNamesAndTypes(
690     const FunctionDecl *CalleeFuncDecl) {
691   // Reset vectors, and fill them with the currently checked function's
692   // parameters' data.
693   ParamNames.clear();
694   ParamTypes.clear();
695 
696   for (const ParmVarDecl *Param : CalleeFuncDecl->parameters()) {
697     ParamTypes.push_back(Param->getType());
698 
699     if (IdentifierInfo *II = Param->getIdentifier())
700       ParamNames.push_back(II->getName());
701     else
702       ParamNames.push_back(StringRef());
703   }
704 }
705 
setArgNamesAndTypes(const CallExpr * MatchedCallExpr,std::size_t InitialArgIndex)706 void SuspiciousCallArgumentCheck::setArgNamesAndTypes(
707     const CallExpr *MatchedCallExpr, std::size_t InitialArgIndex) {
708   // Reset vectors, and fill them with the currently checked function's
709   // arguments' data.
710   ArgNames.clear();
711   ArgTypes.clear();
712 
713   for (std::size_t I = InitialArgIndex, J = MatchedCallExpr->getNumArgs();
714        I < J; ++I) {
715     assert(ArgTypes.size() == I - InitialArgIndex &&
716            ArgNames.size() == ArgTypes.size() &&
717            "Every iteration must put an element into the vectors!");
718 
719     if (const auto *ArgExpr = dyn_cast<DeclRefExpr>(
720             MatchedCallExpr->getArg(I)->IgnoreUnlessSpelledInSource())) {
721       if (const auto *Var = dyn_cast<VarDecl>(ArgExpr->getDecl())) {
722         ArgTypes.push_back(Var->getType());
723         ArgNames.push_back(Var->getName());
724         continue;
725       }
726       if (const auto *FCall = dyn_cast<FunctionDecl>(ArgExpr->getDecl())) {
727         if (FCall->getNameInfo().getName().isIdentifier()) {
728           ArgTypes.push_back(FCall->getType());
729           ArgNames.push_back(FCall->getName());
730           continue;
731         }
732       }
733     }
734 
735     ArgTypes.push_back(QualType());
736     ArgNames.push_back(StringRef());
737   }
738 }
739 
areParamAndArgComparable(std::size_t Position1,std::size_t Position2,const ASTContext & Ctx) const740 bool SuspiciousCallArgumentCheck::areParamAndArgComparable(
741     std::size_t Position1, std::size_t Position2, const ASTContext &Ctx) const {
742   if (Position1 >= ArgNames.size() || Position2 >= ArgNames.size())
743     return false;
744 
745   // Do not report for too short strings.
746   if (ArgNames[Position1].size() < MinimumIdentifierNameLength ||
747       ArgNames[Position2].size() < MinimumIdentifierNameLength ||
748       ParamNames[Position1].size() < MinimumIdentifierNameLength ||
749       ParamNames[Position2].size() < MinimumIdentifierNameLength)
750     return false;
751 
752   if (!areTypesCompatible(ArgTypes[Position1], ParamTypes[Position2], Ctx) ||
753       !areTypesCompatible(ArgTypes[Position2], ParamTypes[Position1], Ctx))
754     return false;
755 
756   return true;
757 }
758 
areArgsSwapped(std::size_t Position1,std::size_t Position2) const759 bool SuspiciousCallArgumentCheck::areArgsSwapped(std::size_t Position1,
760                                                  std::size_t Position2) const {
761   for (Heuristic H : AppliedHeuristics) {
762     bool A1ToP2Similar = areNamesSimilar(
763         ArgNames[Position2], ParamNames[Position1], H, BoundKind::SimilarAbove);
764     bool A2ToP1Similar = areNamesSimilar(
765         ArgNames[Position1], ParamNames[Position2], H, BoundKind::SimilarAbove);
766 
767     bool A1ToP1Dissimilar =
768         !areNamesSimilar(ArgNames[Position1], ParamNames[Position1], H,
769                          BoundKind::DissimilarBelow);
770     bool A2ToP2Dissimilar =
771         !areNamesSimilar(ArgNames[Position2], ParamNames[Position2], H,
772                          BoundKind::DissimilarBelow);
773 
774     if ((A1ToP2Similar || A2ToP1Similar) && A1ToP1Dissimilar &&
775         A2ToP2Dissimilar)
776       return true;
777   }
778   return false;
779 }
780 
areNamesSimilar(StringRef Arg,StringRef Param,Heuristic H,BoundKind BK) const781 bool SuspiciousCallArgumentCheck::areNamesSimilar(StringRef Arg,
782                                                   StringRef Param, Heuristic H,
783                                                   BoundKind BK) const {
784   int8_t Threshold = -1;
785   if (Optional<int8_t> GotBound = getBound(H, BK))
786     Threshold = *GotBound;
787 
788   switch (H) {
789   case Heuristic::Equality:
790     return applyEqualityHeuristic(Arg, Param);
791   case Heuristic::Abbreviation:
792     return applyAbbreviationHeuristic(AbbreviationDictionary, Arg, Param);
793   case Heuristic::Prefix:
794     return applyPrefixHeuristic(Arg, Param, Threshold);
795   case Heuristic::Suffix:
796     return applySuffixHeuristic(Arg, Param, Threshold);
797   case Heuristic::Substring:
798     return applySubstringHeuristic(Arg, Param, Threshold);
799   case Heuristic::Levenshtein:
800     return applyLevenshteinHeuristic(Arg, Param, Threshold);
801   case Heuristic::JaroWinkler:
802     return applyJaroWinklerHeuristic(Arg, Param, Threshold);
803   case Heuristic::Dice:
804     return applyDiceHeuristic(Arg, Param, Threshold);
805   }
806   llvm_unreachable("Unhandled heuristic kind");
807 }
808 
809 } // namespace readability
810 } // namespace tidy
811 } // namespace clang
812