diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -395,6 +395,12 @@ /// restricts the node types for \p Kind. DynTypedMatcher dynCastTo(const ASTNodeKind Kind) const; + /// Return a matcher that that points to the same implementation, but sets the + /// traversal kind. + /// + /// If the traversal kind is already set, then \c TK overrides it. + DynTypedMatcher withTraversalKind(TraversalKind TK); + /// Returns true if the matcher matches the given \c DynNode. bool matches(const DynTypedNode &DynNode, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const; @@ -458,6 +464,14 @@ /// If it is not compatible, then this matcher will never match anything. template Matcher unconditionalConvertTo() const; + /// Returns the \c TraversalKind respected by calls to `match()`, if any. + /// + /// Most matchers will not have a traversal kind set, instead relying on the + /// surrounding context. For those, \c llvm::None is returned. + llvm::Optional getTraversalKind() const { + return Implementation->TraversalKind(); + } + private: DynTypedMatcher(ASTNodeKind SupportedKind, ASTNodeKind RestrictKind, IntrusiveRefCntPtr Implementation) diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -136,6 +136,31 @@ } }; +/// A matcher that specifies a particular \c TraversalKind. +/// +/// The kind provided to the constructor overrides any kind that may be +/// specified by the `InnerMatcher`. +class DynTraversalMatcherImpl : public DynMatcherInterface { +public: + explicit DynTraversalMatcherImpl( + clang::TraversalKind TK, + IntrusiveRefCntPtr InnerMatcher) + : TK(TK), InnerMatcher(std::move(InnerMatcher)) {} + + bool dynMatches(const DynTypedNode &DynNode, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { + return this->InnerMatcher->dynMatches(DynNode, Finder, Builder); + } + + llvm::Optional TraversalKind() const override { + return TK; + } + +private: + clang::TraversalKind TK; + IntrusiveRefCntPtr InnerMatcher; +}; + } // namespace static llvm::ManagedStatic TrueMatcherInstance; @@ -204,6 +229,14 @@ return Copy; } +DynTypedMatcher +DynTypedMatcher::withTraversalKind(ast_type_traits::TraversalKind TK) { + auto Copy = *this; + Copy.Implementation = + new DynTraversalMatcherImpl(TK, std::move(Copy.Implementation)); + return Copy; +} + DynTypedMatcher DynTypedMatcher::trueMatcher(ASTNodeKind NodeKind) { return DynTypedMatcher(NodeKind, NodeKind, &*TrueMatcherInstance); } diff --git a/clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp @@ -13,10 +13,12 @@ #include "clang/Tooling/Tooling.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/Host.h" +#include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" namespace clang { namespace ast_matchers { +using internal::DynTypedMatcher; #if GTEST_HAS_DEATH_TEST TEST(HasNameDeathTest, DiesOnEmptyName) { @@ -171,6 +173,26 @@ EXPECT_NE(nullptr, PT); } +TEST(DynTypedMatcherTest, TraversalKindForwardsToImpl) { + auto M = DynTypedMatcher(decl()); + EXPECT_FALSE(M.getTraversalKind().hasValue()); + + M = DynTypedMatcher(traverse(TK_AsIs, decl())); + EXPECT_THAT(M.getTraversalKind(), llvm::ValueIs(TK_AsIs)); +} + +TEST(DynTypedMatcherTest, ConstructWithTraversalKindSetsTK) { + auto M = DynTypedMatcher(decl()).withTraversalKind(TK_AsIs); + EXPECT_THAT(M.getTraversalKind(), llvm::ValueIs(TK_AsIs)); +} + +TEST(DynTypedMatcherTest, ConstructWithTraversalKindOverridesNestedTK) { + auto M = DynTypedMatcher(decl()).withTraversalKind(TK_AsIs).withTraversalKind( + TK_IgnoreUnlessSpelledInSource); + EXPECT_THAT(M.getTraversalKind(), + llvm::ValueIs(TK_IgnoreUnlessSpelledInSource)); +} + TEST(IsInlineMatcher, IsInline) { EXPECT_TRUE(matches("void g(); inline void f();", functionDecl(isInline(), hasName("f")))); diff --git a/clang/unittests/ASTMatchers/CMakeLists.txt b/clang/unittests/ASTMatchers/CMakeLists.txt --- a/clang/unittests/ASTMatchers/CMakeLists.txt +++ b/clang/unittests/ASTMatchers/CMakeLists.txt @@ -30,4 +30,9 @@ clangTooling ) +target_link_libraries(ASTMatchersTests + PRIVATE + LLVMTestingSupport +) + add_subdirectory(Dynamic)