//===- CallDescription.cpp - function/method call matching --*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // /// \file This file defines a generic mechanism for matching for function and /// method calls of C, C++, and Objective-C languages. Instances of these /// classes are frequently used together with the CallEvent classes. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include using namespace llvm; using namespace clang; using MaybeCount = Optional; // A constructor helper. static MaybeCount readRequiredParams(MaybeCount RequiredArgs, MaybeCount RequiredParams) { if (RequiredParams) return RequiredParams; if (RequiredArgs) return RequiredArgs; return None; } ento::CallDescription::CallDescription(CallDescriptionFlags Flags, ArrayRef QualifiedName, MaybeCount RequiredArgs /*= None*/, MaybeCount RequiredParams /*= None*/) : RequiredArgs(RequiredArgs), RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)), Flags(Flags) { assert(!QualifiedName.empty()); this->QualifiedName.reserve(QualifiedName.size()); llvm::copy(QualifiedName, std::back_inserter(this->QualifiedName)); } /// Construct a CallDescription with default flags. ento::CallDescription::CallDescription(ArrayRef QualifiedName, MaybeCount RequiredArgs /*= None*/, MaybeCount RequiredParams /*= None*/) : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {} bool ento::CallDescription::matches(const CallEvent &Call) const { // FIXME: Add ObjC Message support. if (Call.getKind() == CE_ObjCMessage) return false; const auto *FD = dyn_cast_or_null(Call.getDecl()); if (!FD) return false; if (Flags & CDF_MaybeBuiltin) { return CheckerContext::isCLibraryFunction(FD, getFunctionName()) && (!RequiredArgs || *RequiredArgs <= Call.getNumArgs()) && (!RequiredParams || *RequiredParams <= Call.parameters().size()); } if (!II.hasValue()) { II = &Call.getState()->getStateManager().getContext().Idents.get( getFunctionName()); } const auto MatchNameOnly = [](const CallDescription &CD, const NamedDecl *ND) -> bool { DeclarationName Name = ND->getDeclName(); if (const auto *II = Name.getAsIdentifierInfo()) return II == CD.II.getValue(); // Fast case. // Fallback to the slow stringification and comparison for: // C++ overloaded operators, constructors, destructors, etc. // FIXME This comparison is way SLOWER than comparing pointers. // At some point in the future, we should compare FunctionDecl pointers. return Name.getAsString() == CD.getFunctionName(); }; const auto ExactMatchArgAndParamCounts = [](const CallEvent &Call, const CallDescription &CD) -> bool { const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == Call.getNumArgs(); const bool ParamsMatch = !CD.RequiredParams || *CD.RequiredParams == Call.parameters().size(); return ArgsMatch && ParamsMatch; }; const auto MatchQualifiedNameParts = [](const CallDescription &CD, const Decl *D) -> bool { const auto FindNextNamespaceOrRecord = [](const DeclContext *Ctx) -> const DeclContext * { while (Ctx && !isa(Ctx)) Ctx = Ctx->getParent(); return Ctx; }; auto QualifierPartsIt = CD.begin_qualified_name_parts(); const auto QualifierPartsEndIt = CD.end_qualified_name_parts(); // Match namespace and record names. Skip unrelated names if they don't // match. const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext()); for (; Ctx && QualifierPartsIt != QualifierPartsEndIt; Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) { // If not matched just continue and try matching for the next one. if (cast(Ctx)->getName() != *QualifierPartsIt) continue; ++QualifierPartsIt; } // We matched if we consumed all expected qualifier segments. return QualifierPartsIt == QualifierPartsEndIt; }; // Let's start matching... if (!ExactMatchArgAndParamCounts(Call, *this)) return false; if (!MatchNameOnly(*this, FD)) return false; if (!hasQualifiedNameParts()) return true; return MatchQualifiedNameParts(*this, FD); } ento::CallDescriptionSet::CallDescriptionSet( std::initializer_list &&List) { Impl.LinearMap.reserve(List.size()); for (const CallDescription &CD : List) Impl.LinearMap.push_back({CD, /*unused*/ true}); } bool ento::CallDescriptionSet::contains(const CallEvent &Call) const { return static_cast(Impl.lookup(Call)); }