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