CallDescription.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. //===- CallDescription.cpp - function/method call matching --*- C++ -*-===//
  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. /// \file This file defines a generic mechanism for matching for function and
  10. /// method calls of C, C++, and Objective-C languages. Instances of these
  11. /// classes are frequently used together with the CallEvent classes.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
  15. #include "clang/AST/Decl.h"
  16. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  17. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  18. #include "llvm/ADT/ArrayRef.h"
  19. #include <iterator>
  20. #include <optional>
  21. using namespace llvm;
  22. using namespace clang;
  23. using MaybeCount = std::optional<unsigned>;
  24. // A constructor helper.
  25. static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
  26. MaybeCount RequiredParams) {
  27. if (RequiredParams)
  28. return RequiredParams;
  29. if (RequiredArgs)
  30. return RequiredArgs;
  31. return std::nullopt;
  32. }
  33. ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
  34. ArrayRef<StringRef> QualifiedName,
  35. MaybeCount RequiredArgs /*= None*/,
  36. MaybeCount RequiredParams /*= None*/)
  37. : RequiredArgs(RequiredArgs),
  38. RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
  39. Flags(Flags) {
  40. assert(!QualifiedName.empty());
  41. this->QualifiedName.reserve(QualifiedName.size());
  42. llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),
  43. [](StringRef From) { return From.str(); });
  44. }
  45. /// Construct a CallDescription with default flags.
  46. ento::CallDescription::CallDescription(ArrayRef<StringRef> QualifiedName,
  47. MaybeCount RequiredArgs /*= None*/,
  48. MaybeCount RequiredParams /*= None*/)
  49. : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
  50. bool ento::CallDescription::matches(const CallEvent &Call) const {
  51. // FIXME: Add ObjC Message support.
  52. if (Call.getKind() == CE_ObjCMessage)
  53. return false;
  54. const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
  55. if (!FD)
  56. return false;
  57. return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
  58. }
  59. bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
  60. const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
  61. if (!FD)
  62. return false;
  63. return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
  64. }
  65. bool ento::CallDescription::matchesImpl(const FunctionDecl *Callee,
  66. size_t ArgCount,
  67. size_t ParamCount) const {
  68. const auto *FD = Callee;
  69. if (!FD)
  70. return false;
  71. if (Flags & CDF_MaybeBuiltin) {
  72. return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
  73. (!RequiredArgs || *RequiredArgs <= ArgCount) &&
  74. (!RequiredParams || *RequiredParams <= ParamCount);
  75. }
  76. if (!II) {
  77. II = &FD->getASTContext().Idents.get(getFunctionName());
  78. }
  79. const auto MatchNameOnly = [](const CallDescription &CD,
  80. const NamedDecl *ND) -> bool {
  81. DeclarationName Name = ND->getDeclName();
  82. if (const auto *II = Name.getAsIdentifierInfo())
  83. return II == *CD.II; // Fast case.
  84. // Fallback to the slow stringification and comparison for:
  85. // C++ overloaded operators, constructors, destructors, etc.
  86. // FIXME This comparison is way SLOWER than comparing pointers.
  87. // At some point in the future, we should compare FunctionDecl pointers.
  88. return Name.getAsString() == CD.getFunctionName();
  89. };
  90. const auto ExactMatchArgAndParamCounts =
  91. [](size_t ArgCount, size_t ParamCount,
  92. const CallDescription &CD) -> bool {
  93. const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == ArgCount;
  94. const bool ParamsMatch =
  95. !CD.RequiredParams || *CD.RequiredParams == ParamCount;
  96. return ArgsMatch && ParamsMatch;
  97. };
  98. const auto MatchQualifiedNameParts = [](const CallDescription &CD,
  99. const Decl *D) -> bool {
  100. const auto FindNextNamespaceOrRecord =
  101. [](const DeclContext *Ctx) -> const DeclContext * {
  102. while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
  103. Ctx = Ctx->getParent();
  104. return Ctx;
  105. };
  106. auto QualifierPartsIt = CD.begin_qualified_name_parts();
  107. const auto QualifierPartsEndIt = CD.end_qualified_name_parts();
  108. // Match namespace and record names. Skip unrelated names if they don't
  109. // match.
  110. const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
  111. for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
  112. Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
  113. // If not matched just continue and try matching for the next one.
  114. if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
  115. continue;
  116. ++QualifierPartsIt;
  117. }
  118. // We matched if we consumed all expected qualifier segments.
  119. return QualifierPartsIt == QualifierPartsEndIt;
  120. };
  121. // Let's start matching...
  122. if (!ExactMatchArgAndParamCounts(ArgCount, ParamCount, *this))
  123. return false;
  124. if (!MatchNameOnly(*this, FD))
  125. return false;
  126. if (!hasQualifiedNameParts())
  127. return true;
  128. return MatchQualifiedNameParts(*this, FD);
  129. }
  130. ento::CallDescriptionSet::CallDescriptionSet(
  131. std::initializer_list<CallDescription> &&List) {
  132. Impl.LinearMap.reserve(List.size());
  133. for (const CallDescription &CD : List)
  134. Impl.LinearMap.push_back({CD, /*unused*/ true});
  135. }
  136. bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
  137. return static_cast<bool>(Impl.lookup(Call));
  138. }
  139. bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
  140. return static_cast<bool>(Impl.lookupAsWritten(CE));
  141. }