Hi klimek,
http://llvm-reviews.chandlerc.com/D83
Files:
include/clang/ASTMatchers/ASTMatchers.h
include/clang/ASTMatchers/ASTMatchersInternal.h
include/clang/ASTMatchers/ASTTypeTraits.h
lib/ASTMatchers/ASTMatchFinder.cpp
unittests/ASTMatchers/ASTMatchersTest.cpp
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -1086,6 +1086,15 @@
Stmt,
CXXFunctionalCastExpr> functionalCastExpr;
+/// \brief Matches \c QualTypes in the clang AST.
+const internal::VariadicAllOfMatcher<QualType> qualType;
+
+/// \brief Matches \c Types in the clang AST.
+const internal::VariadicDynCastAllOfMatcher<Type, Type> type;
+
+/// \brief Matches \c TypeLocs in the clang AST.
+const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypeLoc> typeLoc;
+
/// \brief Various overloads for the anyOf matcher.
/// @{
@@ -1335,6 +1344,11 @@
ChildT>(ChildMatcher);
}
+inline internal::ArgumentAdaptingMatcher<internal::HasMatcher, QualType>
+has(const internal::Matcher<Type> &ChildMatcher) {
+ return has(qualType(ChildMatcher));
+}
+
/// \brief Matches AST nodes that have descendant AST nodes that match the
/// provided matcher.
///
@@ -1357,6 +1371,12 @@
DescendantT>(DescendantMatcher);
}
+inline internal::ArgumentAdaptingMatcher<internal::HasDescendantMatcher,
+ QualType>
+hasDescendant(const internal::Matcher<Type> &DescendantMatcher) {
+ return hasDescendant(qualType(DescendantMatcher));
+}
+
/// \brief Matches AST nodes that have child AST nodes that match the
/// provided matcher.
///
@@ -1406,14 +1426,20 @@
///
/// Usable as: Any Matcher
template <typename DescendantT>
-internal::ArgumentAdaptingMatcher<internal::ForEachDescendantMatcher, DescendantT>
+internal::ArgumentAdaptingMatcher<internal::ForEachDescendantMatcher,
+ DescendantT>
forEachDescendant(
const internal::Matcher<DescendantT> &DescendantMatcher) {
return internal::ArgumentAdaptingMatcher<
internal::ForEachDescendantMatcher,
DescendantT>(DescendantMatcher);
}
+inline internal::ArgumentAdaptingMatcher<internal::ForEachDescendantMatcher,
+ QualType>
+forEachDescendant(const internal::Matcher<Type> &DescendantMatcher) {
+ return forEachDescendant(qualType(DescendantMatcher));
+}
/// \brief Matches AST nodes that have a parent that matches the provided
/// matcher.
///
@@ -2470,15 +2496,6 @@
internal::IsExplicitTemplateSpecializationMatcher>();
}
-/// \brief Matches \c QualTypes in the clang AST.
-const internal::VariadicAllOfMatcher<QualType> qualType;
-
-/// \brief Matches \c Types in the clang AST.
-const internal::VariadicDynCastAllOfMatcher<Type, Type> type;
-
-/// \brief Matches \c TypeLocs in the clang AST.
-const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypeLoc> typeLoc;
-
/// \brief Matches \c TypeLocs for which the given inner
/// QualType-matcher matches.
inline internal::BindableMatcher<TypeLoc> loc(
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -426,6 +426,7 @@
(llvm::is_same<T, Decl>::value ||
llvm::is_same<T, Stmt>::value ||
llvm::is_same<T, QualType>::value ||
+ llvm::is_same<T, TypeLoc>::value ||
llvm::is_same<T, NestedNameSpecifier>::value ||
llvm::is_same<T, NestedNameSpecifierLoc>::value ||
llvm::is_same<T, CXXCtorInitializer>::value);
@@ -495,8 +496,10 @@
TraversalKind Traverse,
BindKind Bind) {
TOOLING_COMPILE_ASSERT((llvm::is_base_of<Decl, T>::value ||
- llvm::is_base_of<Stmt, T>::value),
- only_Decl_or_Stmt_allowed_for_recursive_matching);
+ llvm::is_base_of<Stmt, T>::value ||
+ llvm::is_base_of<TypeLoc, T>::value ||
+ llvm::is_base_of<QualType, T>::value),
+ unsupported_type_for_recursive_matching);
return matchesChildOf(ast_type_traits::DynTypedNode::create(Node),
Matcher, Builder, Traverse, Bind);
}
@@ -507,8 +510,10 @@
BoundNodesTreeBuilder *Builder,
BindKind Bind) {
TOOLING_COMPILE_ASSERT((llvm::is_base_of<Decl, T>::value ||
- llvm::is_base_of<Stmt, T>::value),
- only_Decl_or_Stmt_allowed_for_recursive_matching);
+ llvm::is_base_of<Stmt, T>::value ||
+ llvm::is_base_of<TypeLoc, T>::value ||
+ llvm::is_base_of<QualType, T>::value),
+ unsupported_type_for_recursive_matching);
return matchesDescendantOf(ast_type_traits::DynTypedNode::create(Node),
Matcher, Builder, Bind);
}
Index: include/clang/ASTMatchers/ASTTypeTraits.h
===================================================================
--- include/clang/ASTMatchers/ASTTypeTraits.h
+++ include/clang/ASTMatchers/ASTTypeTraits.h
@@ -74,10 +74,10 @@
enum NodeTypeTag {
NT_Decl,
NT_Stmt,
- NT_Type,
NT_NestedNameSpecifier,
NT_NestedNameSpecifierLoc,
NT_QualType,
+ NT_Type,
NT_TypeLoc
} Tag;
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -124,7 +124,7 @@
: Matcher(Matcher),
Finder(Finder),
Builder(Builder),
- CurrentDepth(-1),
+ CurrentDepth(0),
MaxDepth(MaxDepth),
Traversal(Traversal),
Bind(Bind),
@@ -147,17 +147,23 @@
traverse(*D);
else if (const Stmt *S = DynNode.get<Stmt>())
traverse(*S);
+ else if (const QualType *Q = DynNode.get<QualType>())
+ traverse(*Q);
+ else if (const TypeLoc *T = DynNode.get<TypeLoc>())
+ traverse(*T);
// FIXME: Add other base types after adding tests.
return Matches;
}
// The following are overriding methods from the base visitor class.
// They are public only to allow CRTP to work. They are *not *part
// of the public API of this class.
bool TraverseDecl(Decl *DeclNode) {
+ ScopedIncrement ScopedDepth(&CurrentDepth);
return (DeclNode == NULL) || traverse(*DeclNode);
}
bool TraverseStmt(Stmt *StmtNode) {
+ ScopedIncrement ScopedDepth(&CurrentDepth);
const Stmt *StmtToTraverse = StmtNode;
if (Traversal ==
ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses) {
@@ -169,8 +175,15 @@
return (StmtToTraverse == NULL) || traverse(*StmtToTraverse);
}
bool TraverseType(QualType TypeNode) {
+ ScopedIncrement ScopedDepth(&CurrentDepth);
return traverse(TypeNode);
}
+ bool TraverseTypeLoc(TypeLoc TypeLocNode) {
+ ScopedIncrement ScopedDepth(&CurrentDepth);
+ if (!match(TypeLocNode.getType()))
+ return false;
+ return traverse(TypeLocNode);
+ }
bool shouldVisitTemplateInstantiations() const { return true; }
bool shouldVisitImplicitCode() const { return true; }
@@ -188,7 +201,7 @@
// Resets the state of this object.
void reset() {
Matches = false;
- CurrentDepth = -1;
+ CurrentDepth = 0;
}
// Forwards the call to the corresponding Traverse*() method in the
@@ -202,48 +215,47 @@
bool baseTraverse(QualType TypeNode) {
return VisitorBase::TraverseType(TypeNode);
}
+ bool baseTraverse(TypeLoc TypeLocNode) {
+ return VisitorBase::TraverseTypeLoc(TypeLocNode);
+ }
- // Traverses the subtree rooted at 'node'; returns true if the
- // traversal should continue after this function returns; also sets
- // matched_ to true if a match is found during the traversal.
+ // Sets 'Matched' to true if 'Matcher' matches 'Node' and:
+ // 0 < CurrentDepth <= MaxDepth.
+ //
+ // Returns 'true' if traversal should continue after this function
+ // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
template <typename T>
- bool traverse(const T &Node) {
- TOOLING_COMPILE_ASSERT(IsBaseType<T>::value,
- traverse_can_only_be_instantiated_with_base_type);
- ScopedIncrement ScopedDepth(&CurrentDepth);
- if (CurrentDepth == 0) {
- // We don't want to match the root node, so just recurse.
- return baseTraverse(Node);
+ bool match(const T &Node) {
+ if (CurrentDepth == 0 || CurrentDepth > MaxDepth) {
+ return true;
}
if (Bind != ASTMatchFinder::BK_All) {
if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node),
Finder, Builder)) {
Matches = true;
return false; // Abort as soon as a match is found.
}
- if (CurrentDepth < MaxDepth) {
- // The current node doesn't match, and we haven't reached the
- // maximum depth yet, so recurse.
- return baseTraverse(Node);
- }
- // The current node doesn't match, and we have reached the
- // maximum depth, so don't recurse (but continue the traversal
- // such that other nodes at the current level can be visited).
- return true;
} else {
BoundNodesTreeBuilder RecursiveBuilder;
if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node),
Finder, &RecursiveBuilder)) {
// After the first match the matcher succeeds.
Matches = true;
Builder->addMatch(RecursiveBuilder.build());
}
- if (CurrentDepth < MaxDepth) {
- baseTraverse(Node);
- }
- // In kBindAll mode we always search for more matches.
- return true;
}
+ return true;
+ }
+
+ // Traverses the subtree rooted at 'Node'; returns true if the
+ // traversal should continue after this function returns.
+ template <typename T>
+ bool traverse(const T &Node) {
+ TOOLING_COMPILE_ASSERT(IsBaseType<T>::value,
+ traverse_can_only_be_instantiated_with_base_type);
+ if (!match(Node))
+ return false;
+ return baseTraverse(Node);
}
const DynTypedMatcher *const Matcher;
@@ -322,8 +334,12 @@
BoundNodesTreeBuilder *Builder, int MaxDepth,
TraversalKind Traversal, BindKind Bind) {
const UntypedMatchInput input(Matcher.getID(), Node.getMemoizationData());
- assert(input.second &&
- "Fix getMemoizationData once more types allow recursive matching.");
+
+ // For AST-nodes that don't have an identity, we can't memoize.
+ if (!input.second)
+ return matchesRecursively(Node, Matcher, Builder, MaxDepth, Traversal,
+ Bind);
+
std::pair<MemoizationMap::iterator, bool> InsertResult
= ResultCache.insert(std::make_pair(input, MemoizedMatchResult()));
if (InsertResult.second) {
Index: unittests/ASTMatchers/ASTMatchersTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -555,6 +555,110 @@
"};", ZDescendantClassXDescendantClassY));
}
+// Implements a run method that returns whether BoundNodes contains a
+// Decl bound to Id that can be dynamically cast to T.
+// Optionally checks that the check succeeded a specific number of times.
+template <typename T>
+class VerifyIdIsBoundTo : public BoundNodesCallback {
+public:
+ // Create an object that checks that a node of type \c T was bound to \c Id.
+ // Does not check for a certain number of matches.
+ explicit VerifyIdIsBoundTo(llvm::StringRef Id)
+ : Id(Id), ExpectedCount(-1), Count(0) {}
+
+ // Create an object that checks that a node of type \c T was bound to \c Id.
+ // Checks that there were exactly \c ExpectedCount matches.
+ VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount)
+ : Id(Id), ExpectedCount(ExpectedCount), Count(0) {}
+
+ // Create an object that checks that a node of type \c T was bound to \c Id.
+ // Checks that there was exactly one match with the name \c ExpectedName.
+ // Note that \c T must be a NamedDecl for this to work.
+ VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName)
+ : Id(Id), ExpectedCount(1), Count(0), ExpectedName(ExpectedName) {}
+
+ ~VerifyIdIsBoundTo() {
+ if (ExpectedCount != -1)
+ EXPECT_EQ(ExpectedCount, Count);
+ if (!ExpectedName.empty())
+ EXPECT_EQ(ExpectedName, Name);
+ }
+
+ virtual bool run(const BoundNodes *Nodes) {
+ if (Nodes->getNodeAs<T>(Id)) {
+ ++Count;
+ if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {
+ Name = Named->getNameAsString();
+ } else if (const NestedNameSpecifier *NNS =
+ Nodes->getNodeAs<NestedNameSpecifier>(Id)) {
+ llvm::raw_string_ostream OS(Name);
+ NNS->print(OS, PrintingPolicy(LangOptions()));
+ }
+ return true;
+ }
+ return false;
+ }
+
+private:
+ const std::string Id;
+ const int ExpectedCount;
+ int Count;
+ const std::string ExpectedName;
+ std::string Name;
+};
+
+TEST(HasDescendant, MatchesDescendantTypes) {
+ EXPECT_TRUE(matches("void f() { int i = 3; }",
+ decl(hasDescendant(loc(builtinType())))));
+ EXPECT_TRUE(matches("void f() { int i = 3; }",
+ stmt(hasDescendant(builtinType()))));
+
+ EXPECT_TRUE(matches("void f() { int i = 3; }",
+ stmt(hasDescendant(loc(builtinType())))));
+ EXPECT_TRUE(matches("void f() { int i = 3; }",
+ stmt(hasDescendant(qualType(builtinType())))));
+
+ EXPECT_TRUE(notMatches("void f() { float f = 2.0f; }",
+ stmt(hasDescendant(isInteger()))));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void f() { int a; float c; int d; int e; }",
+ functionDecl(forEachDescendant(
+ varDecl(hasDescendant(isInteger())).bind("x"))),
+ new VerifyIdIsBoundTo<Decl>("x", 3)));
+}
+
+TEST(HasDescendant, MatchesDescendantsOfTypes) {
+ EXPECT_TRUE(matches("void f() { int*** i; }",
+ qualType(hasDescendant(builtinType()))));
+ EXPECT_TRUE(matches("void f() { int*** i; }",
+ qualType(hasDescendant(
+ pointerType(pointee(builtinType()))))));
+ EXPECT_TRUE(matches("void f() { int*** i; }",
+ typeLoc(hasDescendant(builtinTypeLoc()))));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void f() { int*** i; }",
+ qualType(asString("int ***"), forEachDescendant(pointerType().bind("x"))),
+ new VerifyIdIsBoundTo<Type>("x", 2)));
+}
+
+TEST(Has, MatchesChildrenOfTypes) {
+ EXPECT_TRUE(matches("int i;",
+ varDecl(hasName("i"), has(isInteger()))));
+ EXPECT_TRUE(notMatches("int** i;",
+ varDecl(hasName("i"), has(isInteger()))));
+}
+
+TEST(Has, MatchesChildTypes) {
+ EXPECT_TRUE(matches(
+ "int* i;",
+ varDecl(hasName("i"), hasType(qualType(has(builtinType()))))));
+ EXPECT_TRUE(notMatches(
+ "int* i;",
+ varDecl(hasName("i"), hasType(qualType(has(pointerType()))))));
+}
+
TEST(Enum, DoesNotMatchClasses) {
EXPECT_TRUE(notMatches("class X {};", enumDecl(hasName("X"))));
}
@@ -613,58 +717,6 @@
matches("class A { public: A *a; class B {}; };", TypeAHasClassB));
}
-// Implements a run method that returns whether BoundNodes contains a
-// Decl bound to Id that can be dynamically cast to T.
-// Optionally checks that the check succeeded a specific number of times.
-template <typename T>
-class VerifyIdIsBoundTo : public BoundNodesCallback {
-public:
- // Create an object that checks that a node of type \c T was bound to \c Id.
- // Does not check for a certain number of matches.
- explicit VerifyIdIsBoundTo(llvm::StringRef Id)
- : Id(Id), ExpectedCount(-1), Count(0) {}
-
- // Create an object that checks that a node of type \c T was bound to \c Id.
- // Checks that there were exactly \c ExpectedCount matches.
- VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount)
- : Id(Id), ExpectedCount(ExpectedCount), Count(0) {}
-
- // Create an object that checks that a node of type \c T was bound to \c Id.
- // Checks that there was exactly one match with the name \c ExpectedName.
- // Note that \c T must be a NamedDecl for this to work.
- VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName)
- : Id(Id), ExpectedCount(1), Count(0), ExpectedName(ExpectedName) {}
-
- ~VerifyIdIsBoundTo() {
- if (ExpectedCount != -1)
- EXPECT_EQ(ExpectedCount, Count);
- if (!ExpectedName.empty())
- EXPECT_EQ(ExpectedName, Name);
- }
-
- virtual bool run(const BoundNodes *Nodes) {
- if (Nodes->getNodeAs<T>(Id)) {
- ++Count;
- if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {
- Name = Named->getNameAsString();
- } else if (const NestedNameSpecifier *NNS =
- Nodes->getNodeAs<NestedNameSpecifier>(Id)) {
- llvm::raw_string_ostream OS(Name);
- NNS->print(OS, PrintingPolicy(LangOptions()));
- }
- return true;
- }
- return false;
- }
-
-private:
- const std::string Id;
- const int ExpectedCount;
- int Count;
- const std::string ExpectedName;
- std::string Name;
-};
-
TEST(Matcher, BindMatchedNodes) {
DeclarationMatcher ClassX = has(recordDecl(hasName("::X")).bind("x"));
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits