[clang] Add 'forNone' AST matcher (PR #86230)
June Rhodes via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 21 19:22:50 PDT 2024
https://github.com/hach-que updated https://github.com/llvm/llvm-project/pull/86230
>From bf78fb2172048c703824698b839e20ad1b8bf0b2 Mon Sep 17 00:00:00 2001
From: June Rhodes <jrhodes at redpoint.games>
Date: Fri, 22 Mar 2024 13:07:57 +1100
Subject: [PATCH] Add 'forNone' AST matcher
---
clang/include/clang/ASTMatchers/ASTMatchers.h | 7 +++
.../clang/ASTMatchers/ASTMatchersInternal.h | 60 +++++++++++++++++++
clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 11 ++++
clang/lib/ASTMatchers/Dynamic/Registry.cpp | 1 +
4 files changed, 79 insertions(+)
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 2f71053d030f68..8cc7a0e92acbdd 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3547,6 +3547,13 @@ extern const internal::ArgumentAdaptingMatcherFunc<
internal::ForEachDescendantMatcher>
forEachDescendant;
+/// Matches AST nodes that have no child AST nodes that match the
+/// provided matcher.
+///
+/// Usable as: Any Matcher
+extern const internal::ArgumentAdaptingMatcherFunc<internal::ForNoneMatcher>
+ forNone;
+
/// Matches if the node or any descendant matches.
///
/// Generates results for each match.
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index 47d912c73dd7eb..fc6d44c3b8b933 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -268,6 +268,26 @@ class BoundNodesMap {
return true;
}
+ /// Returns \c true if the \c BoundNodesMap entirely contains the values
+ /// in \c Subset.
+ bool contains(const BoundNodesMap& Subset) {
+ const auto &N = this->NodeMap.end();
+ if (Subset.NodeMap.size() == 1) {
+ // Avoid iteration if the subset only has a single value.
+ const auto &F = Subset.NodeMap.begin();
+ const auto &T = this->NodeMap.find(F->first);
+ return T != N && T->second == F->second;
+ } else {
+ for (const auto &F : Subset.NodeMap) {
+ const auto &T = this->NodeMap.find(F.first);
+ if (T == N || T->second != F.second) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
private:
IDToNodeMap NodeMap;
};
@@ -306,6 +326,10 @@ class BoundNodesTreeBuilder {
/// The ownership of 'ResultVisitor' remains at the caller.
void visitMatches(Visitor* ResultVisitor);
+ /// Returns true if any of the entries in this tree contain the
+ /// other bound nodes map.
+ bool contains(const internal::BoundNodesMap &Subset);
+
template <typename ExcludePredicate>
bool removeBindings(const ExcludePredicate &Predicate) {
llvm::erase_if(Bindings, Predicate);
@@ -1626,6 +1650,42 @@ class ForEachMatcher : public MatcherInterface<T> {
}
};
+/// Matches nodes of type T that have no child nodes of type ChildT for
+/// which a specified child matcher matches. ChildT must be an AST base
+/// type.
+/// ForNoneMatcher will only match if none of the child nodes match
+/// the inner matcher.
+template <typename T, typename ChildT>
+class ForNoneMatcher : public MatcherInterface<T> {
+ static_assert(IsBaseType<ChildT>::value,
+ "for none only accepts base type matcher");
+
+ DynTypedMatcher InnerMatcher;
+
+public:
+ explicit ForNoneMatcher(const Matcher<ChildT> &InnerMatcher)
+ : InnerMatcher(InnerMatcher) {}
+
+ bool matches(const T &Node, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const override {
+ BoundNodesTreeBuilder MatchingBuilder(*Builder);
+ bool AnyMatched = Finder->matchesChildOf(Node, this->InnerMatcher, &MatchingBuilder,
+ ASTMatchFinder::BK_All);
+ if (!AnyMatched) {
+ // We didn't iterate over any nodes that matched, so
+ // Builder would be empty. This is a success case.
+ return true;
+ }
+ // Otherwise remove from Builder any entries that we
+ // also have in MatchingBuilder because we want to leave
+ // only the remaining entries.
+ return Builder->removeBindings(
+ [&MatchingBuilder](const internal::BoundNodesMap &Nodes) {
+ return MatchingBuilder.contains(Nodes);
+ });
+ }
+};
+
/// @}
template <typename T>
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index bf87b1aa0992a5..f36a8c3ae834c3 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -106,6 +106,15 @@ void BoundNodesTreeBuilder::visitMatches(Visitor *ResultVisitor) {
}
}
+bool BoundNodesTreeBuilder::contains(const internal::BoundNodesMap &Subset) {
+ for (BoundNodesMap &Binding : Bindings) {
+ if (Binding.contains(Subset)) {
+ return true;
+ }
+ }
+ return false;
+}
+
namespace {
using VariadicOperatorFunction = bool (*)(
@@ -1022,6 +1031,8 @@ const internal::ArgumentAdaptingMatcherFunc<internal::HasDescendantMatcher>
hasDescendant = {};
const internal::ArgumentAdaptingMatcherFunc<internal::ForEachMatcher> forEach =
{};
+const internal::ArgumentAdaptingMatcherFunc<internal::ForNoneMatcher> forNone =
+ {};
const internal::ArgumentAdaptingMatcherFunc<internal::ForEachDescendantMatcher>
forEachDescendant = {};
const internal::ArgumentAdaptingMatcherFunc<
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 2c75e6beb74301..f6b866e6a0bcbf 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -259,6 +259,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(forEachOverridden);
REGISTER_MATCHER(forEachSwitchCase);
REGISTER_MATCHER(forEachTemplateArgument);
+ REGISTER_MATCHER(forNone);
REGISTER_MATCHER(forField);
REGISTER_MATCHER(forFunction);
REGISTER_MATCHER(forStmt);
More information about the cfe-commits
mailing list