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@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); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits