[ASTMatchers] Add traversal-kind support to DynTypedMatcher

Summary:
This patch exposes `TraversalKind` support in the `DynTypedMatcher` API. While
previously, the `match` method supported traversal logic, it was not possible to
set or get the traversal kind.

Reviewers: gribozavr, steveire

Subscribers: hokein, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D80685
This commit is contained in:
Yitzhak Mandelbaum 2020-05-27 18:04:50 -04:00
parent 8e325cfc14
commit 04a96aa3e4
4 changed files with 74 additions and 0 deletions

View file

@ -395,6 +395,12 @@ public:
/// 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 @@ public:
/// If it is not compatible, then this matcher will never match anything.
template <typename T> Matcher<T> 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<clang::TraversalKind> getTraversalKind() const {
return Implementation->TraversalKind();
}
private:
DynTypedMatcher(ASTNodeKind SupportedKind, ASTNodeKind RestrictKind,
IntrusiveRefCntPtr<DynMatcherInterface> Implementation)

View file

@ -136,6 +136,31 @@ public:
}
};
/// 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<DynMatcherInterface> 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<clang::TraversalKind> TraversalKind() const override {
return TK;
}
private:
clang::TraversalKind TK;
IntrusiveRefCntPtr<DynMatcherInterface> InnerMatcher;
};
} // namespace
static llvm::ManagedStatic<TrueMatcherImpl> TrueMatcherInstance;
@ -204,6 +229,14 @@ DynTypedMatcher::constructRestrictedWrapper(const DynTypedMatcher &InnerMatcher,
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);
}

View file

@ -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 @@ TEST(Matcher, matchOverEntireASTContext) {
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"))));

View file

@ -30,4 +30,9 @@ clang_target_link_libraries(ASTMatchersTests
clangTooling
)
target_link_libraries(ASTMatchersTests
PRIVATE
LLVMTestingSupport
)
add_subdirectory(Dynamic)