aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/ASTMatchers/ASTMatchersInternal.h28
-rw-r--r--lib/ASTMatchers/ASTMatchFinder.cpp23
-rw-r--r--unittests/ASTMatchers/ASTMatchersTest.cpp88
3 files changed, 128 insertions, 11 deletions
diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h
index 59972ccfa8..7bcf90fce7 100644
--- a/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -495,12 +495,15 @@ public:
BoundNodesTreeBuilder *Builder,
TraversalKind Traverse,
BindKind Bind) {
- TOOLING_COMPILE_ASSERT((llvm::is_base_of<Decl, T>::value ||
- 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),
+ TOOLING_COMPILE_ASSERT(
+ (llvm::is_base_of<Decl, T>::value ||
+ llvm::is_base_of<Stmt, T>::value ||
+ llvm::is_base_of<NestedNameSpecifier, T>::value ||
+ llvm::is_base_of<NestedNameSpecifierLoc, 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);
}
@@ -509,11 +512,14 @@ public:
const DynTypedMatcher &Matcher,
BoundNodesTreeBuilder *Builder,
BindKind Bind) {
- TOOLING_COMPILE_ASSERT((llvm::is_base_of<Decl, T>::value ||
- 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);
+ TOOLING_COMPILE_ASSERT(
+ (llvm::is_base_of<Decl, T>::value ||
+ llvm::is_base_of<Stmt, T>::value ||
+ llvm::is_base_of<NestedNameSpecifier, T>::value ||
+ llvm::is_base_of<NestedNameSpecifierLoc, 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);
}
diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp
index 38df2a199b..b081f5426e 100644
--- a/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -147,6 +147,12 @@ public:
traverse(*D);
else if (const Stmt *S = DynNode.get<Stmt>())
traverse(*S);
+ else if (const NestedNameSpecifier *NNS =
+ DynNode.get<NestedNameSpecifier>())
+ traverse(*NNS);
+ else if (const NestedNameSpecifierLoc *NNSLoc =
+ DynNode.get<NestedNameSpecifierLoc>())
+ traverse(*NNSLoc);
else if (const QualType *Q = DynNode.get<QualType>())
traverse(*Q);
else if (const TypeLoc *T = DynNode.get<TypeLoc>())
@@ -197,6 +203,16 @@ public:
// The TypeLoc is matched inside traverse.
return traverse(TypeLocNode);
}
+ bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) {
+ ScopedIncrement ScopedDepth(&CurrentDepth);
+ return (NNS == NULL) || traverse(*NNS);
+ }
+ bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
+ ScopedIncrement ScopedDepth(&CurrentDepth);
+ if (!match(*NNS.getNestedNameSpecifier()))
+ return false;
+ return !NNS || traverse(NNS);
+ }
bool shouldVisitTemplateInstantiations() const { return true; }
bool shouldVisitImplicitCode() const { return true; }
@@ -231,6 +247,13 @@ private:
bool baseTraverse(TypeLoc TypeLocNode) {
return VisitorBase::TraverseTypeLoc(TypeLocNode);
}
+ bool baseTraverse(const NestedNameSpecifier &NNS) {
+ return VisitorBase::TraverseNestedNameSpecifier(
+ const_cast<NestedNameSpecifier*>(&NNS));
+ }
+ bool baseTraverse(NestedNameSpecifierLoc NNS) {
+ return VisitorBase::TraverseNestedNameSpecifierLoc(NNS);
+ }
// Sets 'Matched' to true if 'Matcher' matches 'Node' and:
// 0 < CurrentDepth <= MaxDepth.
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp
index ec4312e52e..8861881efa 100644
--- a/unittests/ASTMatchers/ASTMatchersTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -3236,6 +3236,94 @@ TEST(NNS, MatchesNestedNameSpecifierPrefixes) {
specifiesTypeLoc(loc(qualType(asString("struct A"))))))));
}
+TEST(NNS, DescendantsOfNestedNameSpecifiers) {
+ std::string Fragment =
+ "namespace a { struct A { struct B { struct C {}; }; }; };"
+ "void f() { a::A::B::C c; }";
+ EXPECT_TRUE(matches(
+ Fragment,
+ nestedNameSpecifier(specifiesType(asString("struct a::A::B")),
+ hasDescendant(nestedNameSpecifier(
+ specifiesNamespace(hasName("a")))))));
+ EXPECT_TRUE(notMatches(
+ Fragment,
+ nestedNameSpecifier(specifiesType(asString("struct a::A::B")),
+ has(nestedNameSpecifier(
+ specifiesNamespace(hasName("a")))))));
+ EXPECT_TRUE(matches(
+ Fragment,
+ nestedNameSpecifier(specifiesType(asString("struct a::A")),
+ has(nestedNameSpecifier(
+ specifiesNamespace(hasName("a")))))));
+
+ // Not really useful because a NestedNameSpecifier can af at most one child,
+ // but to complete the interface.
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ Fragment,
+ nestedNameSpecifier(specifiesType(asString("struct a::A::B")),
+ forEach(nestedNameSpecifier().bind("x"))),
+ new VerifyIdIsBoundTo<NestedNameSpecifier>("x", 1)));
+}
+
+TEST(NNS, NestedNameSpecifiersAsDescendants) {
+ std::string Fragment =
+ "namespace a { struct A { struct B { struct C {}; }; }; };"
+ "void f() { a::A::B::C c; }";
+ EXPECT_TRUE(matches(
+ Fragment,
+ decl(hasDescendant(nestedNameSpecifier(specifiesType(
+ asString("struct a::A")))))));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ Fragment,
+ functionDecl(hasName("f"),
+ forEachDescendant(nestedNameSpecifier().bind("x"))),
+ // Nested names: a, a::A and a::A::B.
+ new VerifyIdIsBoundTo<NestedNameSpecifier>("x", 3)));
+}
+
+TEST(NNSLoc, DescendantsOfNestedNameSpecifierLocs) {
+ std::string Fragment =
+ "namespace a { struct A { struct B { struct C {}; }; }; };"
+ "void f() { a::A::B::C c; }";
+ EXPECT_TRUE(matches(
+ Fragment,
+ nestedNameSpecifierLoc(loc(specifiesType(asString("struct a::A::B"))),
+ hasDescendant(loc(nestedNameSpecifier(
+ specifiesNamespace(hasName("a"))))))));
+ EXPECT_TRUE(notMatches(
+ Fragment,
+ nestedNameSpecifierLoc(loc(specifiesType(asString("struct a::A::B"))),
+ has(loc(nestedNameSpecifier(
+ specifiesNamespace(hasName("a"))))))));
+ EXPECT_TRUE(matches(
+ Fragment,
+ nestedNameSpecifierLoc(loc(specifiesType(asString("struct a::A"))),
+ has(loc(nestedNameSpecifier(
+ specifiesNamespace(hasName("a"))))))));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ Fragment,
+ nestedNameSpecifierLoc(loc(specifiesType(asString("struct a::A::B"))),
+ forEach(nestedNameSpecifierLoc().bind("x"))),
+ new VerifyIdIsBoundTo<NestedNameSpecifierLoc>("x", 1)));
+}
+
+TEST(NNSLoc, NestedNameSpecifierLocsAsDescendants) {
+ std::string Fragment =
+ "namespace a { struct A { struct B { struct C {}; }; }; };"
+ "void f() { a::A::B::C c; }";
+ EXPECT_TRUE(matches(
+ Fragment,
+ decl(hasDescendant(loc(nestedNameSpecifier(specifiesType(
+ asString("struct a::A"))))))));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ Fragment,
+ functionDecl(hasName("f"),
+ forEachDescendant(nestedNameSpecifierLoc().bind("x"))),
+ // Nested names: a, a::A and a::A::B.
+ new VerifyIdIsBoundTo<NestedNameSpecifierLoc>("x", 3)));
+}
+
template <typename T>
class VerifyRecursiveMatch : public BoundNodesCallback {
public: