143 lines
5.2 KiB
C++
143 lines
5.2 KiB
C++
//===- 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"
|
|
|
|
using namespace llvm;
|
|
using namespace clang;
|
|
|
|
// A constructor helper.
|
|
static Optional<size_t> readRequiredParams(Optional<unsigned> RequiredArgs,
|
|
Optional<size_t> RequiredParams) {
|
|
if (RequiredParams)
|
|
return RequiredParams;
|
|
if (RequiredArgs)
|
|
return static_cast<size_t>(*RequiredArgs);
|
|
return None;
|
|
}
|
|
|
|
ento::CallDescription::CallDescription(
|
|
int Flags, ArrayRef<const char *> QualifiedName,
|
|
Optional<unsigned> RequiredArgs /*= None*/,
|
|
Optional<size_t> RequiredParams /*= None*/)
|
|
: QualifiedName(QualifiedName), RequiredArgs(RequiredArgs),
|
|
RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
|
|
Flags(Flags) {
|
|
assert(!QualifiedName.empty());
|
|
}
|
|
|
|
/// Construct a CallDescription with default flags.
|
|
ento::CallDescription::CallDescription(
|
|
ArrayRef<const char *> QualifiedName,
|
|
Optional<unsigned> RequiredArgs /*= None*/,
|
|
Optional<size_t> RequiredParams /*= None*/)
|
|
: CallDescription(0, 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));
|
|
}
|