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