SuspiciousCallArgumentCheck.cpp 29 KB

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