1349cc55cSDimitry Andric //===- CallDescription.cpp - function/method call matching --*- C++ -*-===// 2349cc55cSDimitry Andric // 3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6349cc55cSDimitry Andric // 7349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 8349cc55cSDimitry Andric // 9349cc55cSDimitry Andric /// \file This file defines a generic mechanism for matching for function and 10349cc55cSDimitry Andric /// method calls of C, C++, and Objective-C languages. Instances of these 11349cc55cSDimitry Andric /// classes are frequently used together with the CallEvent classes. 12349cc55cSDimitry Andric // 13349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 14349cc55cSDimitry Andric 15349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 1681ad6265SDimitry Andric #include "clang/AST/Decl.h" 17349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 18349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19349cc55cSDimitry Andric #include "llvm/ADT/ArrayRef.h" 20349cc55cSDimitry Andric #include <iterator> 21bdd1243dSDimitry Andric #include <optional> 22349cc55cSDimitry Andric 23349cc55cSDimitry Andric using namespace llvm; 24349cc55cSDimitry Andric using namespace clang; 25349cc55cSDimitry Andric 26bdd1243dSDimitry Andric using MaybeCount = std::optional<unsigned>; 27349cc55cSDimitry Andric 28349cc55cSDimitry Andric // A constructor helper. 29349cc55cSDimitry Andric static MaybeCount readRequiredParams(MaybeCount RequiredArgs, 30349cc55cSDimitry Andric MaybeCount RequiredParams) { 31349cc55cSDimitry Andric if (RequiredParams) 32349cc55cSDimitry Andric return RequiredParams; 33349cc55cSDimitry Andric if (RequiredArgs) 34349cc55cSDimitry Andric return RequiredArgs; 35bdd1243dSDimitry Andric return std::nullopt; 36349cc55cSDimitry Andric } 37349cc55cSDimitry Andric 38*0fca6ea1SDimitry Andric ento::CallDescription::CallDescription(Mode MatchAs, 39bdd1243dSDimitry Andric ArrayRef<StringRef> QualifiedName, 40349cc55cSDimitry Andric MaybeCount RequiredArgs /*= None*/, 41349cc55cSDimitry Andric MaybeCount RequiredParams /*= None*/) 42349cc55cSDimitry Andric : RequiredArgs(RequiredArgs), 43349cc55cSDimitry Andric RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)), 44*0fca6ea1SDimitry Andric MatchAs(MatchAs) { 45349cc55cSDimitry Andric assert(!QualifiedName.empty()); 46349cc55cSDimitry Andric this->QualifiedName.reserve(QualifiedName.size()); 47bdd1243dSDimitry Andric llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName), 48bdd1243dSDimitry Andric [](StringRef From) { return From.str(); }); 49349cc55cSDimitry Andric } 50349cc55cSDimitry Andric 51349cc55cSDimitry Andric bool ento::CallDescription::matches(const CallEvent &Call) const { 52349cc55cSDimitry Andric // FIXME: Add ObjC Message support. 53349cc55cSDimitry Andric if (Call.getKind() == CE_ObjCMessage) 54349cc55cSDimitry Andric return false; 55349cc55cSDimitry Andric 56349cc55cSDimitry Andric const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 57349cc55cSDimitry Andric if (!FD) 58349cc55cSDimitry Andric return false; 59349cc55cSDimitry Andric 6081ad6265SDimitry Andric return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size()); 61349cc55cSDimitry Andric } 62349cc55cSDimitry Andric 6381ad6265SDimitry Andric bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const { 6481ad6265SDimitry Andric const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl()); 6581ad6265SDimitry Andric if (!FD) 6681ad6265SDimitry Andric return false; 6781ad6265SDimitry Andric 6881ad6265SDimitry Andric return matchesImpl(FD, CE.getNumArgs(), FD->param_size()); 6981ad6265SDimitry Andric } 7081ad6265SDimitry Andric 71*0fca6ea1SDimitry Andric bool ento::CallDescription::matchNameOnly(const NamedDecl *ND) const { 72349cc55cSDimitry Andric DeclarationName Name = ND->getDeclName(); 73*0fca6ea1SDimitry Andric if (const auto *NameII = Name.getAsIdentifierInfo()) { 74*0fca6ea1SDimitry Andric if (!II) 75*0fca6ea1SDimitry Andric II = &ND->getASTContext().Idents.get(getFunctionName()); 76*0fca6ea1SDimitry Andric 77*0fca6ea1SDimitry Andric return NameII == *II; // Fast case. 78*0fca6ea1SDimitry Andric } 79349cc55cSDimitry Andric 80349cc55cSDimitry Andric // Fallback to the slow stringification and comparison for: 81349cc55cSDimitry Andric // C++ overloaded operators, constructors, destructors, etc. 82349cc55cSDimitry Andric // FIXME This comparison is way SLOWER than comparing pointers. 83349cc55cSDimitry Andric // At some point in the future, we should compare FunctionDecl pointers. 84*0fca6ea1SDimitry Andric return Name.getAsString() == getFunctionName(); 85*0fca6ea1SDimitry Andric } 86349cc55cSDimitry Andric 87*0fca6ea1SDimitry Andric bool ento::CallDescription::matchQualifiedNameParts(const Decl *D) const { 88349cc55cSDimitry Andric const auto FindNextNamespaceOrRecord = 89349cc55cSDimitry Andric [](const DeclContext *Ctx) -> const DeclContext * { 90349cc55cSDimitry Andric while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx)) 91349cc55cSDimitry Andric Ctx = Ctx->getParent(); 92349cc55cSDimitry Andric return Ctx; 93349cc55cSDimitry Andric }; 94349cc55cSDimitry Andric 95*0fca6ea1SDimitry Andric auto QualifierPartsIt = begin_qualified_name_parts(); 96*0fca6ea1SDimitry Andric const auto QualifierPartsEndIt = end_qualified_name_parts(); 97349cc55cSDimitry Andric 98349cc55cSDimitry Andric // Match namespace and record names. Skip unrelated names if they don't 99349cc55cSDimitry Andric // match. 100349cc55cSDimitry Andric const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext()); 101349cc55cSDimitry Andric for (; Ctx && QualifierPartsIt != QualifierPartsEndIt; 102349cc55cSDimitry Andric Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) { 103349cc55cSDimitry Andric // If not matched just continue and try matching for the next one. 104349cc55cSDimitry Andric if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt) 105349cc55cSDimitry Andric continue; 106349cc55cSDimitry Andric ++QualifierPartsIt; 107349cc55cSDimitry Andric } 108349cc55cSDimitry Andric 109349cc55cSDimitry Andric // We matched if we consumed all expected qualifier segments. 110349cc55cSDimitry Andric return QualifierPartsIt == QualifierPartsEndIt; 111*0fca6ea1SDimitry Andric } 112349cc55cSDimitry Andric 113*0fca6ea1SDimitry Andric bool ento::CallDescription::matchesImpl(const FunctionDecl *FD, size_t ArgCount, 114*0fca6ea1SDimitry Andric size_t ParamCount) const { 115*0fca6ea1SDimitry Andric if (!FD) 116349cc55cSDimitry Andric return false; 117349cc55cSDimitry Andric 118*0fca6ea1SDimitry Andric const bool isMethod = isa<CXXMethodDecl>(FD); 119*0fca6ea1SDimitry Andric 120*0fca6ea1SDimitry Andric if (MatchAs == Mode::SimpleFunc && isMethod) 121*0fca6ea1SDimitry Andric return false; 122*0fca6ea1SDimitry Andric 123*0fca6ea1SDimitry Andric if (MatchAs == Mode::CXXMethod && !isMethod) 124*0fca6ea1SDimitry Andric return false; 125*0fca6ea1SDimitry Andric 126*0fca6ea1SDimitry Andric if (MatchAs == Mode::CLibraryMaybeHardened) { 127*0fca6ea1SDimitry Andric // In addition to accepting FOO() with CLibrary rules, we also want to 128*0fca6ea1SDimitry Andric // accept calls to __FOO_chk() and __builtin___FOO_chk(). 129*0fca6ea1SDimitry Andric if (CheckerContext::isCLibraryFunction(FD) && 130*0fca6ea1SDimitry Andric CheckerContext::isHardenedVariantOf(FD, getFunctionName())) { 131*0fca6ea1SDimitry Andric // Check that the actual argument/parameter counts are greater or equal 132*0fca6ea1SDimitry Andric // to the required counts. (Setting a requirement to std::nullopt matches 133*0fca6ea1SDimitry Andric // anything, so in that case value_or ensures that the value is compared 134*0fca6ea1SDimitry Andric // with itself.) 135*0fca6ea1SDimitry Andric return (RequiredArgs.value_or(ArgCount) <= ArgCount && 136*0fca6ea1SDimitry Andric RequiredParams.value_or(ParamCount) <= ParamCount); 137*0fca6ea1SDimitry Andric } 138*0fca6ea1SDimitry Andric } 139*0fca6ea1SDimitry Andric 140*0fca6ea1SDimitry Andric if (RequiredArgs.value_or(ArgCount) != ArgCount || 141*0fca6ea1SDimitry Andric RequiredParams.value_or(ParamCount) != ParamCount) 142*0fca6ea1SDimitry Andric return false; 143*0fca6ea1SDimitry Andric 144*0fca6ea1SDimitry Andric if (MatchAs == Mode::CLibrary || MatchAs == Mode::CLibraryMaybeHardened) 145*0fca6ea1SDimitry Andric return CheckerContext::isCLibraryFunction(FD, getFunctionName()); 146*0fca6ea1SDimitry Andric 147*0fca6ea1SDimitry Andric if (!matchNameOnly(FD)) 148349cc55cSDimitry Andric return false; 149349cc55cSDimitry Andric 150349cc55cSDimitry Andric if (!hasQualifiedNameParts()) 151349cc55cSDimitry Andric return true; 152349cc55cSDimitry Andric 153*0fca6ea1SDimitry Andric return matchQualifiedNameParts(FD); 154349cc55cSDimitry Andric } 155349cc55cSDimitry Andric 156349cc55cSDimitry Andric ento::CallDescriptionSet::CallDescriptionSet( 157349cc55cSDimitry Andric std::initializer_list<CallDescription> &&List) { 158349cc55cSDimitry Andric Impl.LinearMap.reserve(List.size()); 159349cc55cSDimitry Andric for (const CallDescription &CD : List) 160349cc55cSDimitry Andric Impl.LinearMap.push_back({CD, /*unused*/ true}); 161349cc55cSDimitry Andric } 162349cc55cSDimitry Andric 163349cc55cSDimitry Andric bool ento::CallDescriptionSet::contains(const CallEvent &Call) const { 164349cc55cSDimitry Andric return static_cast<bool>(Impl.lookup(Call)); 165349cc55cSDimitry Andric } 16681ad6265SDimitry Andric 16781ad6265SDimitry Andric bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const { 16881ad6265SDimitry Andric return static_cast<bool>(Impl.lookupAsWritten(CE)); 16981ad6265SDimitry Andric } 170