diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 2f71053d030f6..fa6afe2f02f6b 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -3547,6 +3547,21 @@ 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 + forNone; + +/// Matches AST nodes that have no descendant AST nodes that match the +/// provided matcher. +/// +/// Usable as: Any Matcher +extern const internal::ArgumentAdaptingMatcherFunc< + internal::ForNoDescendantMatcher> + forNoDescendant; + /// 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 47d912c73dd7e..b40f0b77b042d 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 bool removeBindings(const ExcludePredicate &Predicate) { llvm::erase_if(Bindings, Predicate); @@ -1626,6 +1650,42 @@ class ForEachMatcher : public MatcherInterface { } }; +/// 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 +class ForNoneMatcher : public MatcherInterface { + static_assert(IsBaseType::value, + "for none only accepts base type matcher"); + + DynTypedMatcher InnerMatcher; + +public: + explicit ForNoneMatcher(const Matcher &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 @@ -1724,6 +1784,44 @@ class ForEachDescendantMatcher : public MatcherInterface { } }; +/// Matches nodes of type T that have no descendant node of +/// type DescendantT for which the given inner matcher matches. +/// +/// DescendantT must be an AST base type. +/// ForNoDescendantMatcher only matches if none of the descendant nodes +/// match. +template +class ForNoDescendantMatcher : public MatcherInterface { + static_assert(IsBaseType::value, + "for no descendant only accepts base type matcher"); + + DynTypedMatcher DescendantMatcher; + +public: + explicit ForNoDescendantMatcher(const Matcher &DescendantMatcher) + : DescendantMatcher(DescendantMatcher) {} + + bool matches(const T &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { + BoundNodesTreeBuilder MatchingBuilder(*Builder); + bool AnyMatched = + Finder->matchesDescendantOf(Node, this->DescendantMatcher, + &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); + }); + } +}; + /// Matches on nodes that have a getValue() method if getValue() equals /// the value the ValueEqualsMatcher was constructed with. template diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp index bf87b1aa0992a..b25525683906f 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,8 +1031,12 @@ const internal::ArgumentAdaptingMatcherFunc hasDescendant = {}; const internal::ArgumentAdaptingMatcherFunc forEach = {}; +const internal::ArgumentAdaptingMatcherFunc forNone = + {}; const internal::ArgumentAdaptingMatcherFunc forEachDescendant = {}; +const internal::ArgumentAdaptingMatcherFunc + forNoDescendant = {}; const internal::ArgumentAdaptingMatcherFunc< internal::HasParentMatcher, internal::TypeList, diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 2c75e6beb7430..5bbafb44af895 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -259,6 +259,8 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(forEachOverridden); REGISTER_MATCHER(forEachSwitchCase); REGISTER_MATCHER(forEachTemplateArgument); + REGISTER_MATCHER(forNone); + REGISTER_MATCHER(forNoDescendant); REGISTER_MATCHER(forField); REGISTER_MATCHER(forFunction); REGISTER_MATCHER(forStmt);