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