clang-tools  14.0.0git
SuspiciousCallArgumentCheck.cpp
Go to the documentation of this file.
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 
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;
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?
41  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 
94 } // namespace
95 
96 static const std::string DefaultAbbreviations =
97  optutils::serializeStringList({"addr=address",
98  "arr=array",
99  "attr=attribute",
100  "buf=buffer",
101  "cl=client",
102  "cnt=count",
103  "col=column",
104  "cpy=copy",
105  "dest=destination",
106  "dist=distance"
107  "dst=distance",
108  "elem=element",
109  "hght=height",
110  "i=index",
111  "idx=index",
112  "len=length",
113  "ln=line",
114  "lst=list",
115  "nr=number",
116  "num=number",
117  "pos=position",
118  "ptr=pointer",
119  "ref=reference",
120  "src=source",
121  "srv=server",
122  "stmt=statement",
123  "str=string",
124  "val=value",
125  "var=variable",
126  "vec=vector",
127  "wdth=width"});
128 
129 static constexpr std::size_t SmallVectorSize =
131 
132 /// Returns how many % X is of Y.
133 static inline double percentage(double X, double Y) { return X / Y * 100.0; }
134 
135 static bool applyEqualityHeuristic(StringRef Arg, StringRef Param) {
136  return Arg.equals_insensitive(Param);
137 }
138 
140  const llvm::StringMap<std::string> &AbbreviationDictionary, StringRef Arg,
141  StringRef Param) {
142  if (AbbreviationDictionary.find(Arg) != AbbreviationDictionary.end() &&
143  Param.equals(AbbreviationDictionary.lookup(Arg)))
144  return true;
145 
146  if (AbbreviationDictionary.find(Param) != AbbreviationDictionary.end() &&
147  Arg.equals(AbbreviationDictionary.lookup(Param)))
148  return true;
149 
150  return false;
151 }
152 
153 /// Check whether the shorter String is a prefix of the longer String.
154 static bool applyPrefixHeuristic(StringRef Arg, StringRef Param,
155  int8_t Threshold) {
156  StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
157  StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
158 
159  if (Longer.startswith_insensitive(Shorter))
160  return percentage(Shorter.size(), Longer.size()) > Threshold;
161 
162  return false;
163 }
164 
165 /// Check whether the shorter String is a suffix of the longer String.
166 static bool applySuffixHeuristic(StringRef Arg, StringRef Param,
167  int8_t Threshold) {
168  StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
169  StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
170 
171  if (Longer.endswith_insensitive(Shorter))
172  return percentage(Shorter.size(), Longer.size()) > Threshold;
173 
174  return false;
175 }
176 
177 static bool applySubstringHeuristic(StringRef Arg, StringRef Param,
178  int8_t Threshold) {
179 
180  std::size_t MaxLength = 0;
181  SmallVector<std::size_t, SmallVectorSize> Current(Param.size());
182  SmallVector<std::size_t, SmallVectorSize> Previous(Param.size());
183  std::string ArgLower = Arg.lower();
184  std::string ParamLower = Param.lower();
185 
186  for (std::size_t I = 0; I < Arg.size(); ++I) {
187  for (std::size_t J = 0; J < Param.size(); ++J) {
188  if (ArgLower[I] == ParamLower[J]) {
189  if (I == 0 || J == 0)
190  Current[J] = 1;
191  else
192  Current[J] = 1 + Previous[J - 1];
193 
194  MaxLength = std::max(MaxLength, Current[J]);
195  } else
196  Current[J] = 0;
197  }
198 
199  Current.swap(Previous);
200  }
201 
202  size_t LongerLength = std::max(Arg.size(), Param.size());
203  return percentage(MaxLength, LongerLength) > Threshold;
204 }
205 
206 static bool applyLevenshteinHeuristic(StringRef Arg, StringRef Param,
207  int8_t Threshold) {
208  std::size_t LongerLength = std::max(Arg.size(), Param.size());
209  double Dist = Arg.edit_distance(Param);
210  Dist = (1.0 - Dist / LongerLength) * 100.0;
211  return Dist > Threshold;
212 }
213 
214 // Based on http://en.wikipedia.org/wiki/Jaro–Winkler_distance.
215 static bool applyJaroWinklerHeuristic(StringRef Arg, StringRef Param,
216  int8_t Threshold) {
217  std::size_t Match = 0, Transpos = 0;
218  std::ptrdiff_t ArgLen = Arg.size();
219  std::ptrdiff_t ParamLen = Param.size();
220  SmallVector<int, SmallVectorSize> ArgFlags(ArgLen);
221  SmallVector<int, SmallVectorSize> ParamFlags(ParamLen);
222  std::ptrdiff_t Range =
223  std::max(std::ptrdiff_t{0}, std::max(ArgLen, ParamLen) / 2 - 1);
224 
225  // Calculate matching characters.
226  for (std::ptrdiff_t I = 0; I < ParamLen; ++I)
227  for (std::ptrdiff_t J = std::max(I - Range, std::ptrdiff_t{0}),
228  L = std::min(I + Range + 1, ArgLen);
229  J < L; ++J)
230  if (tolower(Param[I]) == tolower(Arg[J]) && !ArgFlags[J]) {
231  ArgFlags[J] = 1;
232  ParamFlags[I] = 1;
233  ++Match;
234  break;
235  }
236 
237  if (!Match)
238  return false;
239 
240  // Calculate character transpositions.
241  std::ptrdiff_t L = 0;
242  for (std::ptrdiff_t I = 0; I < ParamLen; ++I) {
243  if (ParamFlags[I] == 1) {
244  std::ptrdiff_t J;
245  for (J = L; J < ArgLen; ++J)
246  if (ArgFlags[J] == 1) {
247  L = J + 1;
248  break;
249  }
250 
251  if (tolower(Param[I]) != tolower(Arg[J]))
252  ++Transpos;
253  }
254  }
255  Transpos /= 2;
256 
257  // Jaro distance.
258  double MatchD = Match;
259  double Dist = ((MatchD / ArgLen) + (MatchD / ParamLen) +
260  ((MatchD - Transpos) / Match)) /
261  3.0;
262 
263  // Calculate common string prefix up to 4 chars.
264  L = 0;
265  for (std::ptrdiff_t I = 0;
266  I < std::min(std::min(ArgLen, ParamLen), std::ptrdiff_t{4}); ++I)
267  if (tolower(Arg[I]) == tolower(Param[I]))
268  ++L;
269 
270  // Jaro-Winkler distance.
271  Dist = (Dist + (L * 0.1 * (1.0 - Dist))) * 100.0;
272  return Dist > Threshold;
273 }
274 
275 // Based on http://en.wikipedia.org/wiki/Sørensen–Dice_coefficient
276 static bool applyDiceHeuristic(StringRef Arg, StringRef Param,
277  int8_t Threshold) {
278  llvm::StringSet<> ArgBigrams;
279  llvm::StringSet<> ParamBigrams;
280 
281  // Extract character bigrams from Arg.
282  for (std::ptrdiff_t I = 0; I < static_cast<std::ptrdiff_t>(Arg.size()) - 1;
283  ++I)
284  ArgBigrams.insert(Arg.substr(I, 2).lower());
285 
286  // Extract character bigrams from Param.
287  for (std::ptrdiff_t I = 0; I < static_cast<std::ptrdiff_t>(Param.size()) - 1;
288  ++I)
289  ParamBigrams.insert(Param.substr(I, 2).lower());
290 
291  std::size_t Intersection = 0;
292 
293  // Find the intersection between the two sets.
294  for (auto IT = ParamBigrams.begin(); IT != ParamBigrams.end(); ++IT)
295  Intersection += ArgBigrams.count((IT->getKey()));
296 
297  // Calculate Dice coefficient.
298  return percentage(Intersection * 2.0,
299  ArgBigrams.size() + ParamBigrams.size()) > Threshold;
300 }
301 
302 /// Checks if ArgType binds to ParamType regarding reference-ness and
303 /// cv-qualifiers.
304 static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) {
305  return !ParamType->isReferenceType() ||
306  ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
307  ArgType.getNonReferenceType());
308 }
309 
310 static bool isPointerOrArray(QualType TypeToCheck) {
311  return TypeToCheck->isPointerType() || TypeToCheck->isArrayType();
312 }
313 
314 /// Checks whether ArgType is an array type identical to ParamType's array type.
315 /// Enforces array elements' qualifier compatibility as well.
316 static bool isCompatibleWithArrayReference(QualType ArgType,
317  QualType ParamType) {
318  if (!ArgType->isArrayType())
319  return false;
320  // Here, qualifiers belong to the elements of the arrays.
321  if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
322  return false;
323 
324  return ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType();
325 }
326 
327 static QualType convertToPointeeOrArrayElementQualType(QualType TypeToConvert) {
328  unsigned CVRqualifiers = 0;
329  // Save array element qualifiers, since getElementType() removes qualifiers
330  // from array elements.
331  if (TypeToConvert->isArrayType())
332  CVRqualifiers = TypeToConvert.getLocalQualifiers().getCVRQualifiers();
333  TypeToConvert = TypeToConvert->isPointerType()
334  ? TypeToConvert->getPointeeType()
335  : TypeToConvert->getAsArrayTypeUnsafe()->getElementType();
336  TypeToConvert = TypeToConvert.withCVRQualifiers(CVRqualifiers);
337  return TypeToConvert;
338 }
339 
340 /// Checks if multilevel pointers' qualifiers compatibility continues on the
341 /// current pointer level. For multilevel pointers, C++ permits conversion, if
342 /// every cv-qualifier in ArgType also appears in the corresponding position in
343 /// ParamType, and if PramType has a cv-qualifier that's not in ArgType, then
344 /// every * in ParamType to the right of that cv-qualifier, except the last
345 /// one, must also be const-qualified.
346 static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
347  bool &IsParamContinuouslyConst) {
348  // The types are compatible, if the parameter is at least as qualified as the
349  // argument, and if it is more qualified, it has to be const on upper pointer
350  // levels.
351  bool AreTypesQualCompatible =
352  ParamType.isAtLeastAsQualifiedAs(ArgType) &&
353  (!ParamType.hasQualifiers() || IsParamContinuouslyConst);
354  // Check whether the parameter's constness continues at the current pointer
355  // level.
356  IsParamContinuouslyConst &= ParamType.isConstQualified();
357 
358  return AreTypesQualCompatible;
359 }
360 
361 /// Checks whether multilevel pointers are compatible in terms of levels,
362 /// qualifiers and pointee type.
363 static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
364  bool IsParamContinuouslyConst) {
365  if (!arePointersStillQualCompatible(ArgType, ParamType,
366  IsParamContinuouslyConst))
367  return false;
368 
369  do {
370  // Step down one pointer level.
371  ArgType = convertToPointeeOrArrayElementQualType(ArgType);
372  ParamType = convertToPointeeOrArrayElementQualType(ParamType);
373 
374  // Check whether cv-qualifiers permit compatibility on
375  // current level.
376  if (!arePointersStillQualCompatible(ArgType, ParamType,
377  IsParamContinuouslyConst))
378  return false;
379 
380  if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
381  return true;
382 
383  } while (ParamType->isPointerType() && ArgType->isPointerType());
384  // The final type does not match, or pointer levels differ.
385  return false;
386 }
387 
388 /// Checks whether ArgType converts implicitly to ParamType.
389 static bool areTypesCompatible(QualType ArgType, QualType ParamType,
390  const ASTContext &Ctx) {
391  if (ArgType.isNull() || ParamType.isNull())
392  return false;
393 
394  ArgType = ArgType.getCanonicalType();
395  ParamType = ParamType.getCanonicalType();
396 
397  if (ArgType == ParamType)
398  return true;
399 
400  // Check for constness and reference compatibility.
401  if (!areRefAndQualCompatible(ArgType, ParamType))
402  return false;
403 
404  bool IsParamReference = ParamType->isReferenceType();
405 
406  // Reference-ness has already been checked and should be removed
407  // before further checking.
408  ArgType = ArgType.getNonReferenceType();
409  ParamType = ParamType.getNonReferenceType();
410 
411  if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
412  return true;
413 
414  // Arithmetic types are interconvertible, except scoped enums.
415  if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) {
416  if ((ParamType->isEnumeralType() &&
417  ParamType->getAs<EnumType>()->getDecl()->isScoped()) ||
418  (ArgType->isEnumeralType() &&
419  ArgType->getAs<EnumType>()->getDecl()->isScoped()))
420  return false;
421 
422  return true;
423  }
424 
425  // Check if the argument and the param are both function types (the parameter
426  // decayed to a function pointer).
427  if (ArgType->isFunctionType() && ParamType->isFunctionPointerType()) {
428  ParamType = ParamType->getPointeeType();
429  return ArgType == ParamType;
430  }
431 
432  // Arrays or pointer arguments convert to array or pointer parameters.
433  if (!(isPointerOrArray(ArgType) && isPointerOrArray(ParamType)))
434  return false;
435 
436  // When ParamType is an array reference, ArgType has to be of the same-sized
437  // array-type with cv-compatible element type.
438  if (IsParamReference && ParamType->isArrayType())
439  return isCompatibleWithArrayReference(ArgType, ParamType);
440 
441  bool IsParamContinuouslyConst =
442  !IsParamReference || ParamType.getNonReferenceType().isConstQualified();
443 
444  // Remove the first level of indirection.
445  ArgType = convertToPointeeOrArrayElementQualType(ArgType);
446  ParamType = convertToPointeeOrArrayElementQualType(ParamType);
447 
448  // Check qualifier compatibility on the next level.
449  if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
450  return false;
451 
452  if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
453  return true;
454 
455  // At this point, all possible C language implicit conversion were checked.
456  if (!Ctx.getLangOpts().CPlusPlus)
457  return false;
458 
459  // Check whether ParamType and ArgType were both pointers to a class or a
460  // struct, and check for inheritance.
461  if (ParamType->isStructureOrClassType() &&
462  ArgType->isStructureOrClassType()) {
463  const auto *ArgDecl = ArgType->getAsCXXRecordDecl();
464  const auto *ParamDecl = ParamType->getAsCXXRecordDecl();
465  if (!ArgDecl || !ArgDecl->hasDefinition() || !ParamDecl ||
466  !ParamDecl->hasDefinition())
467  return false;
468 
469  return ArgDecl->isDerivedFrom(ParamDecl);
470  }
471 
472  // Unless argument and param are both multilevel pointers, the types are not
473  // convertible.
474  if (!(ParamType->isAnyPointerType() && ArgType->isAnyPointerType()))
475  return false;
476 
477  return arePointerTypesCompatible(ArgType, ParamType,
478  IsParamContinuouslyConst);
479 }
480 
481 static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD) {
482  switch (FD->getOverloadedOperator()) {
483  case OO_None:
484  case OO_Call:
485  case OO_Subscript:
486  case OO_New:
487  case OO_Delete:
488  case OO_Array_New:
489  case OO_Array_Delete:
490  case OO_Conditional:
491  case OO_Coawait:
492  return false;
493 
494  default:
495  return FD->getNumParams() <= 2;
496  }
497 }
498 
499 SuspiciousCallArgumentCheck::SuspiciousCallArgumentCheck(
500  StringRef Name, ClangTidyContext *Context)
501  : ClangTidyCheck(Name, Context),
502  MinimumIdentifierNameLength(Options.get(
503  "MinimumIdentifierNameLength", DefaultMinimumIdentifierNameLength)) {
504  auto GetToggleOpt = [this](Heuristic H) -> bool {
505  auto Idx = static_cast<std::size_t>(H);
506  assert(Idx < HeuristicCount);
507  return Options.get(HeuristicToString[Idx], Defaults[Idx].Enabled);
508  };
509  auto GetBoundOpt = [this](Heuristic H, BoundKind BK) -> int8_t {
510  auto Idx = static_cast<std::size_t>(H);
511  assert(Idx < HeuristicCount);
512 
513  SmallString<32> Key = HeuristicToString[Idx];
514  Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
515  : "SimilarAbove");
516  int8_t Default = BK == BoundKind::DissimilarBelow
517  ? Defaults[Idx].DissimilarBelow
518  : Defaults[Idx].SimilarAbove;
519  return Options.get(Key, Default);
520  };
521  for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
522  auto H = static_cast<Heuristic>(Idx);
523  if (GetToggleOpt(H))
524  AppliedHeuristics.emplace_back(H);
525  ConfiguredBounds.emplace_back(
526  std::make_pair(GetBoundOpt(H, BoundKind::DissimilarBelow),
527  GetBoundOpt(H, BoundKind::SimilarAbove)));
528  }
529 
530  for (const std::string &Abbreviation : optutils::parseStringList(
531  Options.get("Abbreviations", DefaultAbbreviations))) {
532  auto KeyAndValue = StringRef{Abbreviation}.split("=");
533  assert(!KeyAndValue.first.empty() && !KeyAndValue.second.empty());
534  AbbreviationDictionary.insert(
535  std::make_pair(KeyAndValue.first.str(), KeyAndValue.second.str()));
536  }
537 }
538 
541  Options.store(Opts, "MinimumIdentifierNameLength",
542  MinimumIdentifierNameLength);
543  const auto &SetToggleOpt = [this, &Opts](Heuristic H) -> void {
544  auto Idx = static_cast<std::size_t>(H);
545  Options.store(Opts, HeuristicToString[Idx], isHeuristicEnabled(H));
546  };
547  const auto &SetBoundOpt = [this, &Opts](Heuristic H, BoundKind BK) -> void {
548  auto Idx = static_cast<std::size_t>(H);
549  assert(Idx < HeuristicCount);
550  if (!Defaults[Idx].hasBounds())
551  return;
552 
553  SmallString<32> Key = HeuristicToString[Idx];
554  Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
555  : "SimilarAbove");
556  Options.store(Opts, Key, getBound(H, BK).getValue());
557  };
558 
559  for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
560  auto H = static_cast<Heuristic>(Idx);
561  SetToggleOpt(H);
562  SetBoundOpt(H, BoundKind::DissimilarBelow);
563  SetBoundOpt(H, BoundKind::SimilarAbove);
564  }
565 
566  SmallVector<std::string, 32> Abbreviations;
567  for (const auto &Abbreviation : AbbreviationDictionary) {
568  SmallString<32> EqualSignJoined;
569  EqualSignJoined.append(Abbreviation.first());
570  EqualSignJoined.append("=");
571  EqualSignJoined.append(Abbreviation.second);
572 
573  if (!Abbreviation.second.empty())
574  Abbreviations.emplace_back(EqualSignJoined.str());
575  }
576  Options.store(Opts, "Abbreviations",
577  optutils::serializeStringList(Abbreviations));
578 }
579 
580 bool SuspiciousCallArgumentCheck::isHeuristicEnabled(Heuristic H) const {
581  return llvm::is_contained(AppliedHeuristics, H);
582 }
583 
584 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 
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 
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 
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 
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  if (const auto *ArgExpr = dyn_cast<DeclRefExpr>(
716  MatchedCallExpr->getArg(I)->IgnoreUnlessSpelledInSource())) {
717  if (const auto *Var = dyn_cast<VarDecl>(ArgExpr->getDecl())) {
718  ArgTypes.push_back(Var->getType());
719  ArgNames.push_back(Var->getName());
720  } else if (const auto *FCall =
721  dyn_cast<FunctionDecl>(ArgExpr->getDecl())) {
722  ArgTypes.push_back(FCall->getType());
723  ArgNames.push_back(FCall->getName());
724  } else {
725  ArgTypes.push_back(QualType());
726  ArgNames.push_back(StringRef());
727  }
728  } else {
729  ArgTypes.push_back(QualType());
730  ArgNames.push_back(StringRef());
731  }
732  }
733 }
734 
735 bool SuspiciousCallArgumentCheck::areParamAndArgComparable(
736  std::size_t Position1, std::size_t Position2, const ASTContext &Ctx) const {
737  if (Position1 >= ArgNames.size() || Position2 >= ArgNames.size())
738  return false;
739 
740  // Do not report for too short strings.
741  if (ArgNames[Position1].size() < MinimumIdentifierNameLength ||
742  ArgNames[Position2].size() < MinimumIdentifierNameLength ||
743  ParamNames[Position1].size() < MinimumIdentifierNameLength ||
744  ParamNames[Position2].size() < MinimumIdentifierNameLength)
745  return false;
746 
747  if (!areTypesCompatible(ArgTypes[Position1], ParamTypes[Position2], Ctx) ||
748  !areTypesCompatible(ArgTypes[Position2], ParamTypes[Position1], Ctx))
749  return false;
750 
751  return true;
752 }
753 
754 bool SuspiciousCallArgumentCheck::areArgsSwapped(std::size_t Position1,
755  std::size_t Position2) const {
756  for (Heuristic H : AppliedHeuristics) {
757  bool A1ToP2Similar = areNamesSimilar(
758  ArgNames[Position2], ParamNames[Position1], H, BoundKind::SimilarAbove);
759  bool A2ToP1Similar = areNamesSimilar(
760  ArgNames[Position1], ParamNames[Position2], H, BoundKind::SimilarAbove);
761 
762  bool A1ToP1Dissimilar =
763  !areNamesSimilar(ArgNames[Position1], ParamNames[Position1], H,
764  BoundKind::DissimilarBelow);
765  bool A2ToP2Dissimilar =
766  !areNamesSimilar(ArgNames[Position2], ParamNames[Position2], H,
767  BoundKind::DissimilarBelow);
768 
769  if ((A1ToP2Similar || A2ToP1Similar) && A1ToP1Dissimilar &&
770  A2ToP2Dissimilar)
771  return true;
772  }
773  return false;
774 }
775 
776 bool SuspiciousCallArgumentCheck::areNamesSimilar(StringRef Arg,
777  StringRef Param, Heuristic H,
778  BoundKind BK) const {
779  int8_t Threshold = -1;
780  if (Optional<int8_t> GotBound = getBound(H, BK))
781  Threshold = GotBound.getValue();
782 
783  switch (H) {
784  case Heuristic::Equality:
785  return applyEqualityHeuristic(Arg, Param);
786  case Heuristic::Abbreviation:
787  return applyAbbreviationHeuristic(AbbreviationDictionary, Arg, Param);
788  case Heuristic::Prefix:
789  return applyPrefixHeuristic(Arg, Param, Threshold);
790  case Heuristic::Suffix:
791  return applySuffixHeuristic(Arg, Param, Threshold);
792  case Heuristic::Substring:
793  return applySubstringHeuristic(Arg, Param, Threshold);
794  case Heuristic::Levenshtein:
795  return applyLevenshteinHeuristic(Arg, Param, Threshold);
796  case Heuristic::JaroWinkler:
797  return applyJaroWinklerHeuristic(Arg, Param, Threshold);
798  case Heuristic::Dice:
799  return applyDiceHeuristic(Arg, Param, Threshold);
800  }
801  llvm_unreachable("Unhandled heuristic kind");
802 }
803 
804 } // namespace readability
805 } // namespace tidy
806 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::tidy::readability::SuspiciousCallArgumentCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: SuspiciousCallArgumentCheck.cpp:611
clang::tidy::readability::applyAbbreviationHeuristic
static bool applyAbbreviationHeuristic(const llvm::StringMap< std::string > &AbbreviationDictionary, StringRef Arg, StringRef Param)
Definition: SuspiciousCallArgumentCheck.cpp:139
DissimilarBelow
const int8_t DissimilarBelow
The upper bound of % of similarity the two strings might have to be considered dissimilar.
Definition: SuspiciousCallArgumentCheck.cpp:32
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
clang::tidy::readability::convertToPointeeOrArrayElementQualType
static QualType convertToPointeeOrArrayElementQualType(QualType TypeToConvert)
Definition: SuspiciousCallArgumentCheck.cpp:327
clang::tidy::readability::applyDiceHeuristic
static bool applyDiceHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Definition: SuspiciousCallArgumentCheck.cpp:276
Enabled
const bool Enabled
Whether the heuristic is to be enabled by default.
Definition: SuspiciousCallArgumentCheck.cpp:26
Ctx
Context Ctx
Definition: TUScheduler.cpp:454
clang::tidy::readability::applyPrefixHeuristic
static bool applyPrefixHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Check whether the shorter String is a prefix of the longer String.
Definition: SuspiciousCallArgumentCheck.cpp:154
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:54
clang::tidy::readability::areRefAndQualCompatible
static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType)
Checks if ArgType binds to ParamType regarding reference-ness and cv-qualifiers.
Definition: SuspiciousCallArgumentCheck.cpp:304
clang::tidy::readability::applySuffixHeuristic
static bool applySuffixHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Check whether the shorter String is a suffix of the longer String.
Definition: SuspiciousCallArgumentCheck.cpp:166
clang::tidy::ClangTidyCheck::OptionsView::get
llvm::Optional< std::string > get(StringRef LocalName) const
Read a named option from the Context.
Definition: ClangTidyCheck.cpp:55
clang::tidy::readability::SuspiciousCallArgumentCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SuspiciousCallArgumentCheck.cpp:601
clang::tidy::utils::options
Definition: OptionsUtils.cpp:14
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::abseil::getValue
static double getValue(const IntegerLiteral *IntLit, const FloatingLiteral *FloatLit)
Definition: DurationFactoryScaleCheck.cpp:38
clang::tidy::readability::applyLevenshteinHeuristic
static bool applyLevenshteinHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Definition: SuspiciousCallArgumentCheck.cpp:206
X
int X
Definition: LSPBinderTests.cpp:25
clang::tidy::utils::options::serializeStringList
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
Definition: OptionsUtils.cpp:30
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
clang::tidy::utils::options::parseStringList
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
Definition: OptionsUtils.cpp:18
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:416
clang::tidy::readability::SuspiciousCallArgumentCheck::HeuristicCount
static constexpr std::size_t HeuristicCount
Definition: SuspiciousCallArgumentCheck.h:53
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:76
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::readability::Defaults
static constexpr DefaultHeuristicConfiguration Defaults[]
Definition: SuspiciousCallArgumentCheck.cpp:51
SuspiciousCallArgumentCheck.h
clang::tidy::readability::isCompatibleWithArrayReference
static bool isCompatibleWithArrayReference(QualType ArgType, QualType ParamType)
Checks whether ArgType is an array type identical to ParamType's array type.
Definition: SuspiciousCallArgumentCheck.cpp:316
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
clang::tidy::readability::percentage
static double percentage(double X, double Y)
Returns how many % X is of Y.
Definition: SuspiciousCallArgumentCheck.cpp:133
clang::tidy::readability::isOverloadedUnaryOrBinarySymbolOperator
static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD)
Definition: SuspiciousCallArgumentCheck.cpp:481
clang::tidy::readability::DefaultMinimumIdentifierNameLength
static constexpr std::size_t DefaultMinimumIdentifierNameLength
Definition: SuspiciousCallArgumentCheck.cpp:45
clang::tidy::readability::applyEqualityHeuristic
static bool applyEqualityHeuristic(StringRef Arg, StringRef Param)
Definition: SuspiciousCallArgumentCheck.cpp:135
clang::tidy::readability::areTypesCompatible
static bool areTypesCompatible(QualType ArgType, QualType ParamType, const ASTContext &Ctx)
Checks whether ArgType converts implicitly to ParamType.
Definition: SuspiciousCallArgumentCheck.cpp:389
clang::tidy::readability::applyJaroWinklerHeuristic
static bool applyJaroWinklerHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Definition: SuspiciousCallArgumentCheck.cpp:215
clang::tidy::readability::SmallVectorSize
static constexpr std::size_t SmallVectorSize
Definition: SuspiciousCallArgumentCheck.cpp:129
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::readability::HeuristicToString
static constexpr StringRef HeuristicToString[]
Definition: SuspiciousCallArgumentCheck.cpp:47
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:120
Dist
trace::Metric Dist
Definition: TraceTests.cpp:151
clang::tidy::readability::SuspiciousCallArgumentCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: SuspiciousCallArgumentCheck.cpp:539
clang::tidy::readability::arePointerTypesCompatible
static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType, bool IsParamContinuouslyConst)
Checks whether multilevel pointers are compatible in terms of levels, qualifiers and pointee type.
Definition: SuspiciousCallArgumentCheck.cpp:363
clang::tidy::readability::arePointersStillQualCompatible
static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType, bool &IsParamContinuouslyConst)
Checks if multilevel pointers' qualifiers compatibility continues on the current pointer level.
Definition: SuspiciousCallArgumentCheck.cpp:346
clang::tidy::readability::DefaultAbbreviations
static const std::string DefaultAbbreviations
Definition: SuspiciousCallArgumentCheck.cpp:96
SimilarAbove
const int8_t SimilarAbove
The lower bound of % of similarity the two string must have to be considered similar.
Definition: SuspiciousCallArgumentCheck.cpp:38
clang::tidy::readability::isPointerOrArray
static bool isPointerOrArray(QualType TypeToCheck)
Definition: SuspiciousCallArgumentCheck.cpp:310
Value
static constexpr bool Value
Definition: SuspiciousCallArgumentCheck.cpp:72
clang::tidy::readability::applySubstringHeuristic
static bool applySubstringHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Definition: SuspiciousCallArgumentCheck.cpp:177