From 6fc3433b155f127c43f089f22b44461f91cb444a Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 30 Oct 2012 15:42:00 +0000 Subject: [PATCH] Implement descendant matchers for NestedNamespecifiers This implements has(), hasDescendant(), forEach() and forEachDescendant() for NestedNameSpecifier and NestedNameSpecifierLoc matchers. Review: http://llvm-reviews.chandlerc.com/D86 llvm-svn: 167017 --- .../clang/ASTMatchers/ASTMatchersInternal.h | 28 ++++--- clang/lib/ASTMatchers/ASTMatchFinder.cpp | 23 ++++++ clang/unittests/ASTMatchers/ASTMatchersTest.cpp | 88 ++++++++++++++++++++++ 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index 59972cc..7bcf90f 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -495,12 +495,15 @@ public: BoundNodesTreeBuilder *Builder, TraversalKind Traverse, BindKind Bind) { - TOOLING_COMPILE_ASSERT((llvm::is_base_of::value || - llvm::is_base_of::value || - llvm::is_base_of::value || - llvm::is_base_of::value), - unsupported_type_for_recursive_matching); - return matchesChildOf(ast_type_traits::DynTypedNode::create(Node), + TOOLING_COMPILE_ASSERT( + (llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::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::value || - llvm::is_base_of::value || - llvm::is_base_of::value || - llvm::is_base_of::value), - unsupported_type_for_recursive_matching); + TOOLING_COMPILE_ASSERT( + (llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value || + llvm::is_base_of::value), + unsupported_type_for_recursive_matching); return matchesDescendantOf(ast_type_traits::DynTypedNode::create(Node), Matcher, Builder, Bind); } diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp index 38df2a1..b081f54 100644 --- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -147,6 +147,12 @@ public: traverse(*D); else if (const Stmt *S = DynNode.get()) traverse(*S); + else if (const NestedNameSpecifier *NNS = + DynNode.get()) + traverse(*NNS); + else if (const NestedNameSpecifierLoc *NNSLoc = + DynNode.get()) + traverse(*NNSLoc); else if (const QualType *Q = DynNode.get()) traverse(*Q); else if (const TypeLoc *T = DynNode.get()) @@ -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(&NNS)); + } + bool baseTraverse(NestedNameSpecifierLoc NNS) { + return VisitorBase::TraverseNestedNameSpecifierLoc(NNS); + } // Sets 'Matched' to true if 'Matcher' matches 'Node' and: // 0 < CurrentDepth <= MaxDepth. diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTest.cpp index ec4312e..8861881 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/clang/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("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("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("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("x", 3))); +} + template class VerifyRecursiveMatch : public BoundNodesCallback { public: -- 2.7.4