clang-tools  15.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 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 =
130 
131 /// Returns how many % X is of Y.
132 static inline double percentage(double X, double Y) { return X / Y * 100.0; }
133 
134 static bool applyEqualityHeuristic(StringRef Arg, StringRef Param) {
135  return Arg.equals_insensitive(Param);
136 }
137 
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.
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.
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 
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 
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.
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
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.
303 static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) {
304  return !ParamType->isReferenceType() ||
305  ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
306  ArgType.getNonReferenceType());
307 }
308 
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.
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 
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.
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.
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.
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 
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 
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 
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 
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  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 
740 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 
759 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 
781 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
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:138
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:326
clang::tidy::readability::applyDiceHeuristic
static bool applyDiceHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Definition: SuspiciousCallArgumentCheck.cpp:275
Enabled
const bool Enabled
Whether the heuristic is to be enabled by default.
Definition: SuspiciousCallArgumentCheck.cpp:26
Ctx
Context Ctx
Definition: TUScheduler.cpp:495
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:153
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:53
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:303
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:165
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:15
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::readability::applyLevenshteinHeuristic
static bool applyLevenshteinHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Definition: SuspiciousCallArgumentCheck.cpp:205
X
int X
Definition: LSPBinderTests.cpp:25
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:415
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:67
clang::tidy::readability::Defaults
static constexpr DefaultHeuristicConfiguration Defaults[]
Definition: SuspiciousCallArgumentCheck.cpp:51
SuspiciousCallArgumentCheck.h
clang::tidy::ClangTidyCheck::OptionsView::get
llvm::Optional< StringRef > get(StringRef LocalName) const
Read a named option from the Context.
Definition: ClangTidyCheck.cpp:55
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:315
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
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
clang::tidy::readability::DefaultAbbreviations
static constexpr llvm::StringLiteral DefaultAbbreviations
Definition: SuspiciousCallArgumentCheck.cpp:96
clang::tidy::readability::percentage
static double percentage(double X, double Y)
Returns how many % X is of Y.
Definition: SuspiciousCallArgumentCheck.cpp:132
clang::tidy::readability::isOverloadedUnaryOrBinarySymbolOperator
static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD)
Definition: SuspiciousCallArgumentCheck.cpp:480
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:134
clang::tidy::readability::areTypesCompatible
static bool areTypesCompatible(QualType ArgType, QualType ParamType, const ASTContext &Ctx)
Checks whether ArgType converts implicitly to ParamType.
Definition: SuspiciousCallArgumentCheck.cpp:388
clang::tidy::readability::applyJaroWinklerHeuristic
static bool applyJaroWinklerHeuristic(StringRef Arg, StringRef Param, int8_t Threshold)
Definition: SuspiciousCallArgumentCheck.cpp:214
clang::tidy::utils::options::serializeStringList
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
Definition: OptionsUtils.cpp:62
clang::tidy::readability::SmallVectorSize
static constexpr std::size_t SmallVectorSize
Definition: SuspiciousCallArgumentCheck.cpp:128
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::utils::options::parseStringList
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
Definition: OptionsUtils.cpp:19
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:129
Dist
trace::Metric Dist
Definition: TraceTests.cpp:148
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:538
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:362
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:345
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:309
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:176