[clang] Add 'forNone' AST matcher (PR #86230)
llvmbot wrote: @llvm/pr-subscribers-clang Author: June Rhodes (hach-que) Changes 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()), 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. --- Full diff: https://github.com/llvm/llvm-project/pull/86230.diff 4 Files Affected: - (modified) clang/include/clang/ASTMatchers/ASTMatchers.h (+7) - (modified) clang/include/clang/ASTMatchers/ASTMatchersInternal.h (+36) - (modified) clang/lib/ASTMatchers/ASTMatchersInternal.cpp (+2) - (modified) clang/lib/ASTMatchers/Dynamic/Registry.cpp (+1) ``diff 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 +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 { } }; +/// 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) {} + + bool matches(const T , ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { +BoundNodesTreeBuilder MatchingBuilder(*Builder); +bool AnyMatched = Finder->matchesChildOf(Node, this->InnerMatcher, , +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( +[](const internal::BoundNodesMap ) { + return MatchingBuilder.contains(Nodes); +}); + } +}; + /// @} template 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 hasDescendant = {}; const internal::ArgumentAdaptingMatcherFunc forEach = {}; +const internal::ArgumentAdaptingMatcherFunc forNone = +{}; const internal::ArgumentAdaptingMatcherFunc 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); `` https://github.com/llvm/llvm-project/pull/86230 ___ cfe-commits mailing list cfe-commits@lists.llvm.org
[clang] Add 'forNone' AST matcher (PR #86230)
https://github.com/hach-que updated https://github.com/llvm/llvm-project/pull/86230 >From 9b04a03646992a3366022dd7022858c98f7560e7 Mon Sep 17 00:00:00 2001 From: June Rhodes Date: Fri, 22 Mar 2024 13:07:57 +1100 Subject: [PATCH] Add 'forNone' and 'forNoDescendant' AST matchers --- clang/include/clang/ASTMatchers/ASTMatchers.h | 15 +++ .../clang/ASTMatchers/ASTMatchersInternal.h | 97 +++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 13 +++ clang/lib/ASTMatchers/Dynamic/Registry.cpp| 2 + 4 files changed, 127 insertions(+) diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 2f71053d030f68..fa6afe2f02f6b0 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 47d912c73dd7eb..5612730462465a 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 = this->NodeMap.end(); +if (Subset.NodeMap.size() == 1) { + // Avoid iteration if the subset only has a single value. + const auto = Subset.NodeMap.begin(); + const auto = this->NodeMap.find(F->first); + return T != N && T->second == F->second; +} else { + for (const auto : Subset.NodeMap) { +const auto = 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 ); + template bool removeBindings(const ExcludePredicate ) { 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) {} + + bool matches(const T , ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { +BoundNodesTreeBuilder MatchingBuilder(*Builder); +bool AnyMatched = Finder->matchesChildOf(Node, this->InnerMatcher, , +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( +[](const internal::BoundNodesMap ) { + return MatchingBuilder.contains(Nodes); +}); + } +}; + /// @} template @@ -1724,6 +1784,43 @@ 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
[clang] Add 'forNone' AST matcher (PR #86230)
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 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 +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 = this->NodeMap.end(); +if (Subset.NodeMap.size() == 1) { + // Avoid iteration if the subset only has a single value. + const auto = Subset.NodeMap.begin(); + const auto = this->NodeMap.find(F->first); + return T != N && T->second == F->second; +} else { + for (const auto : Subset.NodeMap) { +const auto = 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 ); + template bool removeBindings(const ExcludePredicate ) { 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) {} + + bool matches(const T , ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { +BoundNodesTreeBuilder MatchingBuilder(*Builder); +bool AnyMatched = Finder->matchesChildOf(Node, this->InnerMatcher, , +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( +[](const internal::BoundNodesMap ) { + return MatchingBuilder.contains(Nodes); +}); + } +}; + /// @} template 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 ) { + for (BoundNodesMap : Bindings) { +if (Binding.contains(Subset)) { + return true; +} + } + return false; +} + namespace { using VariadicOperatorFunction = bool (*)( @@ -1022,6 +1031,8 @@ const internal::ArgumentAdaptingMatcherFunc hasDescendant = {}; const internal::ArgumentAdaptingMatcherFunc forEach = {}; +const internal::ArgumentAdaptingMatcherFunc forNone = +{}; const
[clang] Add 'forNone' AST matcher (PR #86230)
github-actions[bot] wrote: Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using `@` followed by their GitHub username. If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the [LLVM GitHub User Guide](https://llvm.org/docs/GitHub.html). You can also ask questions in a comment on this PR, on the [LLVM Discord](https://discord.com/invite/xS7Z362) or on the [forums](https://discourse.llvm.org/). https://github.com/llvm/llvm-project/pull/86230 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add 'forNone' AST matcher (PR #86230)
github-actions[bot] wrote: :warning: C/C++ code formatter, clang-format found issues in your code. :warning: You can test this locally with the following command: ``bash git-clang-format --diff 718fbbef5f18a2b7e7fc4f842b1452ae9bee581a b0ef223dfab9c8ebc67601ccfbbe0ce3abe15f12 -- clang/include/clang/ASTMatchers/ASTMatchers.h clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/lib/ASTMatchers/ASTMatchersInternal.cpp clang/lib/ASTMatchers/Dynamic/Registry.cpp `` View the diff from clang-format here. ``diff diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index bf5aaf74c0..31c0ec2c29 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1645,8 +1645,8 @@ public: bool matches(const T , ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { BoundNodesTreeBuilder MatchingBuilder(*Builder); -bool AnyMatched = Finder->matchesChildOf(Node, this->InnerMatcher, , -ASTMatchFinder::BK_All); +bool AnyMatched = Finder->matchesChildOf( +Node, this->InnerMatcher, , ASTMatchFinder::BK_All); if (!AnyMatched) { // We didn't iterate over any nodes that matched, so // Builder would be empty. This is a success case. `` https://github.com/llvm/llvm-project/pull/86230 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add 'forNone' AST matcher (PR #86230)
https://github.com/hach-que edited https://github.com/llvm/llvm-project/pull/86230 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add 'forNone' AST matcher (PR #86230)
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 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 +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 { } }; +/// 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) {} + + bool matches(const T , ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { +BoundNodesTreeBuilder MatchingBuilder(*Builder); +bool AnyMatched = Finder->matchesChildOf(Node, this->InnerMatcher, , +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( +[](const internal::BoundNodesMap ) { + return MatchingBuilder.contains(Nodes); +}); + } +}; + /// @} template 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 hasDescendant = {}; const internal::ArgumentAdaptingMatcherFunc forEach = {}; +const internal::ArgumentAdaptingMatcherFunc forNone = +{}; const internal::ArgumentAdaptingMatcherFunc 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);