Hi klimek, krememek, jordan_rose,

In order to implement the static analyzer check in D5042 using ASTMatchers, I 
need the concept of a hasNextSibling() constraint.

The attached patch does not compile, but I'm wondering if I'm in the ballpark 
as far as implementation goes, or whether there is a better way to do this.  
(See the FIXME comments in the patch for what's not implemented.)

http://reviews.llvm.org/D5105

Files:
  include/clang/ASTMatchers/ASTMatchers.h
  include/clang/ASTMatchers/ASTMatchersInternal.h
  lib/ASTMatchers/ASTMatchFinder.cpp
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -1664,6 +1664,38 @@
 const internal::ArgumentAdaptingMatcherFunc<internal::HasDescendantMatcher>
 LLVM_ATTRIBUTE_UNUSED hasDescendant = {};
 
+/// \brief Matches AST nodes that are next sibling AST nodes that match the
+/// provided matcher.
+///
+/// Example matches X
+///     (matcher = recordDecl(hasName("Z"),
+///                           hasNextSibling(recordDecl(hasName("X")))))
+/// \code
+///   class Z {}; class Y {}; class X {};
+/// \endcode
+///
+/// NextSiblingT must be an AST base type.
+///
+/// Usable as: Any Matcher
+const internal::ArgumentAdaptingMatcherFunc<internal::HasNextSiblingMatcher>
+LLVM_ATTRIBUTE_UNUSED hasNextSibling = {};
+
+/// \brief Matches AST nodes that are previous sibling AST nodes that match the
+/// provided matcher.
+///
+/// Example matches Z
+///     (matcher = recordDecl(hasName("X"),
+///                           hasPreviousSibling(recordDecl(hasName("Z")))))
+/// \code
+///   class Z {}; class Y {}; class X {};
+/// \endcode
+///
+/// NextSiblingT must be an AST base type.
+///
+/// Usable as: Any Matcher
+const internal::ArgumentAdaptingMatcherFunc<internal::HasPreviousSiblingMatcher>
+LLVM_ATTRIBUTE_UNUSED hasPreviousSibling = {};
+
 /// \brief Matches AST nodes that have child AST nodes that match the
 /// provided matcher.
 ///
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -760,6 +760,22 @@
                                Matcher, Builder, Bind);
   }
 
+  template <typename T>
+  bool matchesNextSiblingOf(const T &Node,
+                            const DynTypedMatcher &Matcher,
+                            BoundNodesTreeBuilder *Builder,
+                            BindKind Bind) {
+    static_assert(std::is_base_of<Decl, T>::value ||
+            std::is_base_of<Stmt, T>::value ||
+            std::is_base_of<NestedNameSpecifier, T>::value ||
+            std::is_base_of<NestedNameSpecifierLoc, T>::value ||
+            std::is_base_of<TypeLoc, T>::value ||
+            std::is_base_of<QualType, T>::value,
+        "unsupported type for next sibling matching");
+    return matchesNextSiblingOf(ast_type_traits::DynTypedNode::create(Node),
+        Matcher, Builder, Bind);
+  }
+
   // FIXME: Implement support for BindKind.
   template <typename T>
   bool matchesAncestorOf(const T &Node,
@@ -787,6 +803,11 @@
                                    BoundNodesTreeBuilder *Builder,
                                    BindKind Bind) = 0;
 
+  virtual bool matchesNextSiblingOf(const ast_type_traits::DynTypedNode &Node,
+                                    const DynTypedMatcher &Matcher,
+                                    BoundNodesTreeBuilder *Builder,
+                                    BindKind Bind) = 0;
+
   virtual bool matchesAncestorOf(const ast_type_traits::DynTypedNode &Node,
                                  const DynTypedMatcher &Matcher,
                                  BoundNodesTreeBuilder *Builder,
@@ -1370,6 +1391,29 @@
   const Matcher<DescendantT> DescendantMatcher;
 };
 
+/// \brief Matches nodes of type T that have at least one next sibling node of
+/// type NextSiblingT for which the given inner matcher matches.
+///
+/// NextSiblingT must be an AST base type.
+template <typename T, typename NextSiblingT>
+class HasNextSiblingMatcher : public MatcherInterface<T> {
+  static_assert(IsBaseType<NextSiblingT>::value,
+                "hasNextSibling only accepts base type matcher");
+
+public:
+  explicit HasNextSiblingMatcher(const Matcher<NextSiblingT> &NextSiblingMatcher)
+      : NextSiblingMatcher(NextSiblingMatcher) {}
+
+  bool matches(const T &Node, ASTMatchFinder *Finder,
+               BoundNodesTreeBuilder *Builder) const override {
+    return Finder->matchesNextSiblingOf(
+        Node, NextSiblingMatcher, Builder, ASTMatchFinder::BK_First);
+  }
+
+private:
+  const Matcher<NextSiblingT> NextSiblingMatcher;
+};
+
 /// \brief Matches nodes of type \c T that have a parent node of type \c ParentT
 /// for which the given inner matcher matches.
 ///
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -407,6 +407,49 @@
     return Visitor.findMatch(Node);
   }
 
+  // Matches next siblings of 'Node' with 'BaseMatcher'.
+  bool memoizedMatchesNextSibling(const ast_type_traits::DynTypedNode &Node,
+                                  const DynTypedMatcher &Matcher,
+                                  BoundNodesTreeBuilder *Builder,
+                                  TraversalKind Traversal, BindKind Bind) {
+    // For AST-nodes that don't have an identity, we can't memoize.
+    if (!Node.getMemoizationData() || !Builder->isComparable())
+      return matchesNextSibling(Node, Matcher, Builder, Traversal, Bind);
+
+    MatchKey Key;
+    Key.MatcherID = Matcher.getID();
+    Key.Node = Node;
+    // Note that we key on the bindings *before* the match.
+    Key.BoundNodes = *Builder;
+
+    MemoizationMap::iterator I = ResultCache.find(Key);
+    if (I != ResultCache.end()) {
+      *Builder = I->second.Nodes;
+      return I->second.ResultOfMatch;
+    }
+
+    MemoizedMatchResult Result;
+    Result.Nodes = *Builder;
+    Result.ResultOfMatch = matchesNextSibling(Node, Matcher, &Result.Nodes,
+        Traversal, Bind);
+    ResultCache[Key] = Result;
+    *Builder = Result.Nodes;
+    return Result.ResultOfMatch;
+  }
+
+  // Matches next sibling of 'Node' with 'BaseMatcher'.
+  bool matchesNextSibling(const ast_type_traits::DynTypedNode &Node,
+                          const DynTypedMatcher &Matcher,
+                          BoundNodesTreeBuilder *Builder,
+                          TraversalKind Traversal, BindKind Bind) {
+    // FIXME: Implement for next sibling.
+    // FIXME: Get parent of Node, match all children (max depth 0 or 1), then
+    // iterate through children ourselves???
+    MatchChildASTVisitor Visitor(
+      &Matcher, this, Builder, 1, Traversal, BK_All);
+    return Visitor.findMatch(Node);
+  }
+
   bool classIsDerivedFrom(const CXXRecordDecl *Declaration,
                           const Matcher<NamedDecl> &Base,
                           BoundNodesTreeBuilder *Builder) override;
@@ -432,6 +475,15 @@
     return memoizedMatchesRecursively(Node, Matcher, Builder, INT_MAX,
                                       TK_AsIs, Bind);
   }
+  // Implements ASTMatchFinder::matchesNextSiblingOf.
+  bool matchesNextSiblingOf(const ast_type_traits::DynTypedNode &Node,
+                            const DynTypedMatcher &Matcher,
+                            BoundNodesTreeBuilder *Builder,
+                            BindKind Bind) override {
+    if (ResultCache.size() > MaxMemoizationEntries)
+      ResultCache.clear();
+    return memoizedMatchesNextSibling(Node, Matcher, Builder, TK_AsIs, Bind);
+  }
   // Implements ASTMatchFinder::matchesAncestorOf.
   bool matchesAncestorOf(const ast_type_traits::DynTypedNode &Node,
                          const DynTypedMatcher &Matcher,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to