https://github.com/hach-que created https://github.com/llvm/llvm-project/pull/86230
This adds a `forNone` AST matcher, which matches only if there are no immediate children of the current node that match the inner matcher. For example, given: ```cpp class F { public: int A; F() {}; }; ``` the matcher: ``` cxxConstructorDecl( unless(isImplicit()), unless(isDelegatingConstructor()), unless(isDeleted()), unless(isDefaulted()), hasBody(stmt()), unless(ofClass(cxxRecordDecl(isUClass()))), unless(ofClass(cxxRecordDecl(isUInterface()))), ofClass(cxxRecordDecl(forEach(fieldDecl().bind("declared_field")))), forNone(cxxCtorInitializer(forField(fieldDecl(equalsBoundNode("declared_field")).bind("referenced_field")))) ).bind("bad_constructor") ``` would match `F()`, because it does not have an initializer for `A`. We use this in our modified version of Clang to detect constructors that do not fully initialize all fields. >From b0ef223dfab9c8ebc67601ccfbbe0ce3abe15f12 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 | 36 +++++++++++++++++++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 2 ++ clang/lib/ASTMatchers/Dynamic/Registry.cpp | 1 + 4 files changed, 46 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..bf5aaf74c0ef9a 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1626,6 +1626,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..4a8f383011b336 100644 --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -1022,6 +1022,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