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