[clang] 6c51270 - [analyzer][NFC] Introduce CallDescription::matches() in addition to isCalled()
Balazs Benics via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 19 09:32:50 PST 2021
Author: Balazs Benics
Date: 2021-11-19T18:32:13+01:00
New Revision: 6c512703a9e6e495afa0f44528821c27f28db795
URL: https://github.com/llvm/llvm-project/commit/6c512703a9e6e495afa0f44528821c27f28db795
DIFF: https://github.com/llvm/llvm-project/commit/6c512703a9e6e495afa0f44528821c27f28db795.diff
LOG: [analyzer][NFC] Introduce CallDescription::matches() in addition to isCalled()
This patch introduces `CallDescription::matches()` member function,
accepting a `CallEvent`.
Semantically, `Call.isCalled(CD)` is the same as `CD.matches(Call)`.
The patch also introduces the `matchesAny()` variadic free function template.
It accepts a `CallEvent` and at least one `CallDescription` to match
against.
Reviewed By: martong
Differential Revision: https://reviews.llvm.org/D113590
Added:
Modified:
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h
clang/lib/StaticAnalyzer/Core/CallDescription.cpp
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h
index b34e1c82eb7d..a35a5fb912eb 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h
@@ -83,6 +83,33 @@ class CallDescription {
/// 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; }
+
+ /// @name Matching CallDescriptions against a CallEvent
+ /// @{
+
+ /// Returns true if the CallEvent is a call to a function that matches
+ /// the CallDescription.
+ ///
+ /// \note This function is not intended to be used to match Obj-C method
+ /// calls.
+ bool matches(const CallEvent &Call) const;
+
+ /// Returns true whether the CallEvent matches on any of the CallDescriptions
+ /// supplied.
+ ///
+ /// \note This function is not intended to be used to match Obj-C method
+ /// calls.
+ friend bool matchesAny(const CallEvent &Call, const CallDescription &CD1) {
+ return CD1.matches(Call);
+ }
+
+ /// \copydoc clang::ento::matchesAny(const CallEvent &, const CallDescription &)
+ template <typename... Ts>
+ friend bool matchesAny(const CallEvent &Call, const CallDescription &CD1,
+ const Ts &...CDs) {
+ return CD1.matches(Call) || matchesAny(Call, CDs...);
+ }
+ /// @}
};
/// An immutable map from CallDescriptions to arbitrary data. Provides a unified
@@ -113,7 +140,7 @@ template <typename T> class CallDescriptionMap {
// Slow path: linear lookup.
// TODO: Implement some sort of fast path.
for (const std::pair<CallDescription, T> &I : LinearMap)
- if (Call.isCalled(I.first))
+ if (I.first.matches(Call))
return &I.second;
return nullptr;
diff --git a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
index 097cbf68f066..3ab54de96f3f 100644
--- a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
@@ -14,6 +14,7 @@
#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"
@@ -47,6 +48,88 @@ ento::CallDescription::CallDescription(
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());
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index f2a6e3853d1d..c59b4b46d058 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -304,85 +304,7 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
}
bool CallEvent::isCalled(const CallDescription &CD) const {
- // FIXME: Add ObjC Message support.
- if (getKind() == CE_ObjCMessage)
- return false;
-
- const auto *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
- if (!FD)
- return false;
-
- if (CD.Flags & CDF_MaybeBuiltin) {
- return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) &&
- (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) &&
- (!CD.RequiredParams || CD.RequiredParams <= parameters().size());
- }
-
- if (!CD.II.hasValue()) {
- CD.II = &getState()->getStateManager().getContext().Idents.get(
- CD.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(*this, CD))
- return false;
-
- if (!MatchNameOnly(CD, FD))
- return false;
-
- if (!CD.hasQualifiedNameParts())
- return true;
-
- return MatchQualifiedNameParts(CD, FD);
+ return CD.matches(*this);
}
SVal CallEvent::getArgSVal(unsigned Index) const {
More information about the cfe-commits
mailing list