#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/AST/Decl.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>;
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 ,
MaybeCount RequiredParams )
: RequiredArgs(RequiredArgs),
RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
Flags(Flags) {
assert(!QualifiedName.empty());
this->QualifiedName.reserve(QualifiedName.size());
llvm::copy(QualifiedName, std::back_inserter(this->QualifiedName));
}
ento::CallDescription::CallDescription(ArrayRef<const char *> QualifiedName,
MaybeCount RequiredArgs ,
MaybeCount RequiredParams )
: CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
bool ento::CallDescription::matches(const CallEvent &Call) const {
if (Call.getKind() == CE_ObjCMessage)
return false;
const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD)
return false;
return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
}
bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
if (!FD)
return false;
return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
}
bool ento::CallDescription::matchesImpl(const FunctionDecl *Callee,
size_t ArgCount,
size_t ParamCount) const {
const auto *FD = Callee;
if (!FD)
return false;
if (Flags & CDF_MaybeBuiltin) {
return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
(!RequiredArgs || *RequiredArgs <= ArgCount) &&
(!RequiredParams || *RequiredParams <= ParamCount);
}
if (!II) {
II = &FD->getASTContext().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;
return Name.getAsString() == CD.getFunctionName();
};
const auto ExactMatchArgAndParamCounts =
[](size_t ArgCount, size_t ParamCount,
const CallDescription &CD) -> bool {
const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == ArgCount;
const bool ParamsMatch =
!CD.RequiredParams || *CD.RequiredParams == ParamCount;
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();
const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
continue;
++QualifierPartsIt;
}
return QualifierPartsIt == QualifierPartsEndIt;
};
if (!ExactMatchArgAndParamCounts(ArgCount, ParamCount, *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, true});
}
bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
return static_cast<bool>(Impl.lookup(Call));
}
bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
return static_cast<bool>(Impl.lookupAsWritten(CE));
}