diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core/CallDescription.cpp')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/CallDescription.cpp | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp new file mode 100644 index 000000000000..810fe365d021 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp @@ -0,0 +1,146 @@ +//===- 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)); +} |
