diff options
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchersInternal.h | 28 | ||||
-rw-r--r-- | lib/ASTMatchers/ASTMatchFinder.cpp | 23 | ||||
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersTest.cpp | 88 |
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: |