[clang] Add 'forNone' AST matcher (PR #86230)

via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 21 19:14:22 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: June Rhodes (hach-que)

<details>
<summary>Changes</summary>

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<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);

``````````

</details>


https://github.com/llvm/llvm-project/pull/86230


More information about the cfe-commits mailing list