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 15 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.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 "llvm/ADT/Optional.h" 20 #include <iterator> 21 22 using namespace llvm; 23 using namespace clang; 24 25 // A constructor helper. 26 static Optional<size_t> readRequiredParams(Optional<unsigned> RequiredArgs, 27 Optional<size_t> RequiredParams) { 28 if (RequiredParams) 29 return RequiredParams; 30 if (RequiredArgs) 31 return static_cast<size_t>(*RequiredArgs); 32 return None; 33 } 34 35 ento::CallDescription::CallDescription( 36 int Flags, ArrayRef<const char *> QualifiedName, 37 Optional<unsigned> RequiredArgs /*= None*/, 38 Optional<size_t> RequiredParams /*= None*/) 39 : RequiredArgs(RequiredArgs), 40 RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)), 41 Flags(Flags) { 42 assert(!QualifiedName.empty()); 43 this->QualifiedName.reserve(QualifiedName.size()); 44 llvm::copy(QualifiedName, std::back_inserter(this->QualifiedName)); 45 } 46 47 /// Construct a CallDescription with default flags. 48 ento::CallDescription::CallDescription( 49 ArrayRef<const char *> QualifiedName, 50 Optional<unsigned> RequiredArgs /*= None*/, 51 Optional<size_t> RequiredParams /*= None*/) 52 : CallDescription(0, QualifiedName, RequiredArgs, RequiredParams) {} 53 54 bool ento::CallDescription::matches(const CallEvent &Call) const { 55 // FIXME: Add ObjC Message support. 56 if (Call.getKind() == CE_ObjCMessage) 57 return false; 58 59 const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 60 if (!FD) 61 return false; 62 63 if (Flags & CDF_MaybeBuiltin) { 64 return CheckerContext::isCLibraryFunction(FD, getFunctionName()) && 65 (!RequiredArgs || RequiredArgs <= Call.getNumArgs()) && 66 (!RequiredParams || RequiredParams <= Call.parameters().size()); 67 } 68 69 if (!II.hasValue()) { 70 II = &Call.getState()->getStateManager().getContext().Idents.get( 71 getFunctionName()); 72 } 73 74 const auto MatchNameOnly = [](const CallDescription &CD, 75 const NamedDecl *ND) -> bool { 76 DeclarationName Name = ND->getDeclName(); 77 if (const auto *II = Name.getAsIdentifierInfo()) 78 return II == CD.II.getValue(); // Fast case. 79 80 // Fallback to the slow stringification and comparison for: 81 // C++ overloaded operators, constructors, destructors, etc. 82 // FIXME This comparison is way SLOWER than comparing pointers. 83 // At some point in the future, we should compare FunctionDecl pointers. 84 return Name.getAsString() == CD.getFunctionName(); 85 }; 86 87 const auto ExactMatchArgAndParamCounts = 88 [](const CallEvent &Call, const CallDescription &CD) -> bool { 89 const bool ArgsMatch = 90 !CD.RequiredArgs || CD.RequiredArgs == Call.getNumArgs(); 91 const bool ParamsMatch = 92 !CD.RequiredParams || CD.RequiredParams == Call.parameters().size(); 93 return ArgsMatch && ParamsMatch; 94 }; 95 96 const auto MatchQualifiedNameParts = [](const CallDescription &CD, 97 const Decl *D) -> bool { 98 const auto FindNextNamespaceOrRecord = 99 [](const DeclContext *Ctx) -> const DeclContext * { 100 while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx)) 101 Ctx = Ctx->getParent(); 102 return Ctx; 103 }; 104 105 auto QualifierPartsIt = CD.begin_qualified_name_parts(); 106 const auto QualifierPartsEndIt = CD.end_qualified_name_parts(); 107 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 119 // We matched if we consumed all expected qualifier segments. 120 return QualifierPartsIt == QualifierPartsEndIt; 121 }; 122 123 // Let's start matching... 124 if (!ExactMatchArgAndParamCounts(Call, *this)) 125 return false; 126 127 if (!MatchNameOnly(*this, FD)) 128 return false; 129 130 if (!hasQualifiedNameParts()) 131 return true; 132 133 return MatchQualifiedNameParts(*this, FD); 134 } 135 136 ento::CallDescriptionSet::CallDescriptionSet( 137 std::initializer_list<CallDescription> &&List) { 138 Impl.LinearMap.reserve(List.size()); 139 for (const CallDescription &CD : List) 140 Impl.LinearMap.push_back({CD, /*unused*/ true}); 141 } 142 143 bool ento::CallDescriptionSet::contains(const CallEvent &Call) const { 144 return static_cast<bool>(Impl.lookup(Call)); 145 } 146