[analyzer][NFC] Refactor CallEvent::isCalled()

Refactor the code to make it more readable.

It will set up further changes, and improvements to this code in
subsequent patches.
This is a non-functional change.

Reviewed By: martong

Differential Revision: https://reviews.llvm.org/D111534
This commit is contained in:
Balazs Benics 2021-10-18 14:57:24 +02:00
parent 5644d15257
commit 3ec7b91141
2 changed files with 68 additions and 35 deletions

View File

@ -1237,9 +1237,7 @@ enum CallDescriptionFlags : int {
/// arguments and the name of the function.
class CallDescription {
friend CallEvent;
mutable IdentifierInfo *II = nullptr;
mutable bool IsLookupDone = false;
mutable Optional<const IdentifierInfo *> II;
// The list of the qualified names used to identify the specified CallEvent,
// e.g. "{a, b}" represent the qualified names, like "a::b".
std::vector<const char *> QualifiedName;
@ -1273,7 +1271,9 @@ public:
Optional<size_t> RequiredParams = None)
: QualifiedName(QualifiedName), RequiredArgs(RequiredArgs),
RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
Flags(Flags) {}
Flags(Flags) {
assert(!QualifiedName.empty());
}
/// Construct a CallDescription with default flags.
CallDescription(ArrayRef<const char *> QualifiedName,
@ -1283,6 +1283,17 @@ public:
/// Get the name of the function that this object matches.
StringRef getFunctionName() const { return QualifiedName.back(); }
/// Get the qualified name parts in reversed order.
/// E.g. { "std", "vector", "data" } -> "vector", "std"
auto begin_qualified_name_parts() const {
return std::next(QualifiedName.rbegin());
}
auto end_qualified_name_parts() const { return QualifiedName.rend(); }
/// It's false, if and only if we expect a single identifier, such as
/// `getenv`. It's true for `std::swap`, or `my::detail::container::data`.
bool hasQualifiedNameParts() const { return QualifiedName.size() > 1; }
};
/// An immutable map from CallDescriptions to arbitrary data. Provides a unified

View File

@ -307,10 +307,7 @@ bool CallEvent::isCalled(const CallDescription &CD) const {
if (getKind() == CE_ObjCMessage)
return false;
const IdentifierInfo *II = getCalleeIdentifier();
if (!II)
return false;
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
const auto *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
if (!FD)
return false;
@ -320,44 +317,69 @@ bool CallEvent::isCalled(const CallDescription &CD) const {
(!CD.RequiredParams || CD.RequiredParams <= parameters().size());
}
if (!CD.IsLookupDone) {
CD.IsLookupDone = true;
if (!CD.II.hasValue()) {
CD.II = &getState()->getStateManager().getContext().Idents.get(
CD.getFunctionName());
}
if (II != CD.II)
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.
// Simply report mismatch for:
// C++ overloaded operators, constructors, destructors, etc.
return false;
};
// If CallDescription provides prefix names, use them to improve matching
// accuracy.
if (CD.QualifiedName.size() > 1 && FD) {
const DeclContext *Ctx = FD->getDeclContext();
// See if we'll be able to match them all.
size_t NumUnmatched = CD.QualifiedName.size() - 1;
for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
if (NumUnmatched == 0)
break;
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;
};
if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
if (ND->getName() == CD.QualifiedName[NumUnmatched - 1])
--NumUnmatched;
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;
}
if (const auto *RD = dyn_cast<RecordDecl>(Ctx)) {
if (RD->getName() == CD.QualifiedName[NumUnmatched - 1])
--NumUnmatched;
continue;
}
++QualifierPartsIt;
}
if (NumUnmatched > 0)
return false;
}
// We matched if we consumed all expected qualifier segments.
return QualifierPartsIt == QualifierPartsEndIt;
};
return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()) &&
(!CD.RequiredParams || CD.RequiredParams == parameters().size());
// Let's start matching...
if (!ExactMatchArgAndParamCounts(*this, CD))
return false;
if (!MatchNameOnly(CD, FD))
return false;
if (!CD.hasQualifiedNameParts())
return true;
return MatchQualifiedNameParts(CD, FD);
}
SVal CallEvent::getArgSVal(unsigned Index) const {