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