Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -1664,6 +1664,38 @@ const internal::ArgumentAdaptingMatcherFunc 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 +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 +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 + bool matchesNextSiblingOf(const T &Node, + const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) { + static_assert(std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value, + "unsupported type for next sibling matching"); + return matchesNextSiblingOf(ast_type_traits::DynTypedNode::create(Node), + Matcher, Builder, Bind); + } + // FIXME: Implement support for BindKind. template 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 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 +class HasNextSiblingMatcher : public MatcherInterface { + static_assert(IsBaseType::value, + "hasNextSibling only accepts base type matcher"); + +public: + explicit HasNextSiblingMatcher(const Matcher &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 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 &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,