123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- //===- 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 <iterator>
- using namespace llvm;
- using namespace clang;
- using MaybeCount = Optional<unsigned>;
- // 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<const char *> 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<const char *> 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<FunctionDecl>(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<NamespaceDecl, RecordDecl>(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<NamedDecl>(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<CallDescription> &&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<bool>(Impl.lookup(Call));
- }
|