diff options
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchFinder.h | 4 | ||||
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchers.h | 79 | ||||
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchersInternal.h | 125 | ||||
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchersMacros.h | 18 | ||||
-rw-r--r-- | include/clang/ASTMatchers/ASTTypeTraits.h | 32 | ||||
-rw-r--r-- | lib/ASTMatchers/ASTMatchFinder.cpp | 29 | ||||
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersTest.cpp | 140 |
7 files changed, 366 insertions, 61 deletions
diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h index 0fcc5dda1c..64a86d7ebe 100644 --- a/include/clang/ASTMatchers/ASTMatchFinder.h +++ b/include/clang/ASTMatchers/ASTMatchFinder.h @@ -112,6 +112,10 @@ public: MatchCallback *Action); void addMatcher(const StatementMatcher &NodeMatch, MatchCallback *Action); + void addMatcher(const NestedNameSpecifierMatcher &NodeMatch, + MatchCallback *Action); + void addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch, + MatchCallback *Action); /// @} /// \brief Creates a clang ASTConsumer that finds all matches. diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index 19fac18ef2..163ffbdb48 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -110,6 +110,8 @@ internal::Matcher<T> id(const std::string &ID, typedef internal::Matcher<Decl> DeclarationMatcher; typedef internal::Matcher<QualType> TypeMatcher; typedef internal::Matcher<Stmt> StatementMatcher; +typedef internal::Matcher<NestedNameSpecifier> NestedNameSpecifierMatcher; +typedef internal::Matcher<NestedNameSpecifierLoc> NestedNameSpecifierLocMatcher; /// @} /// \brief Matches any node. @@ -2288,6 +2290,83 @@ isExplicitTemplateSpecialization() { internal::IsExplicitTemplateSpecializationMatcher>(); } +/// \brief Matches nested name specifiers. +/// +/// Given +/// \code +/// namespace ns { +/// struct A { static void f(); }; +/// void A::f() {} +/// void g() { A::f(); } +/// } +/// ns::A a; +/// \endcode +/// nestedNameSpecifier() +/// matches "ns::" and both "A::" +const internal::VariadicAllOfMatcher<NestedNameSpecifier> nestedNameSpecifier; + +/// \brief Same as \c nestedNameSpecifier but matches \c NestedNameSpecifierLoc. +const internal::VariadicAllOfMatcher< + NestedNameSpecifierLoc> nestedNameSpecifierLoc; + +/// \brief Matches \c NestedNameSpecifierLocs for which the given inner +/// NestedNameSpecifier-matcher matches. +inline internal::BindableMatcher<NestedNameSpecifierLoc> loc( + const internal::Matcher<NestedNameSpecifier> &InnerMatcher) { + return internal::BindableMatcher<NestedNameSpecifierLoc>( + new internal::LocMatcher<NestedNameSpecifierLoc, NestedNameSpecifier>( + InnerMatcher)); +} + +/// \brief Matches nested name specifiers that specify a type matching the +/// given \c QualType matcher without qualifiers. +/// FIXME: This is a temporary solution. Switch to using Type-matchers as soon +/// as we have those. +/// +/// Given +/// \code +/// struct A { struct B { struct C {}; }; }; +/// A::B::C c; +/// \endcode +/// nestedNameSpecifier(specifiesType(hasDeclaration(recordDecl(hasName("A"))))) +/// matches "A::" +AST_MATCHER_P(NestedNameSpecifier, specifiesType, + internal::Matcher<QualType>, InnerMatcher) { + if (Node.getAsType() == NULL) + return false; + return InnerMatcher.matches(QualType(Node.getAsType(), 0), Finder, Builder); +} + +/// \brief Matches on the prefix of a \c NestedNameSpecifier or +/// \c NestedNameSpecifierLoc. +/// +/// Given +/// \code +/// struct A { struct B { struct C {}; }; }; +/// A::B::C c; +/// \endcode +/// nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))) and +/// nestedNameSpecifierLoc(hasPrefix(loc(specifiesType(asString("struct A"))))) +/// both match "A::" +LOC_TRAVERSE_MATCHER(hasPrefix, NestedNameSpecifier, getPrefix) + +/// \brief Matches nested name specifiers that specify a namespace matching the +/// given namespace matcher. +/// +/// Given +/// \code +/// namespace ns { struct A {}; } +/// ns::A a; +/// \endcode +/// nestedNameSpecifier(specifiesNamespace(hasName("ns"))) +/// matches "ns::" +AST_MATCHER_P(NestedNameSpecifier, specifiesNamespace, + internal::Matcher<NamespaceDecl>, InnerMatcher) { + if (Node.getAsNamespace() == NULL) + return false; + return InnerMatcher.matches(*Node.getAsNamespace(), Finder, Builder); +} + } // end namespace ast_matchers } // end namespace clang diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index 2a49b63c83..9e04bd7e7b 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -380,6 +380,8 @@ struct IsBaseType { (llvm::is_same<T, Decl>::value || llvm::is_same<T, Stmt>::value || llvm::is_same<T, QualType>::value || + llvm::is_same<T, NestedNameSpecifier>::value || + llvm::is_same<T, NestedNameSpecifierLoc>::value || llvm::is_same<T, CXXCtorInitializer>::value); }; template <typename T> @@ -652,9 +654,6 @@ public: /// The returned matcher is equivalent to this matcher, but will /// bind the matched node on a match. Matcher<T> bind(StringRef ID) const { - TOOLING_COMPILE_ASSERT((llvm::is_base_of<Stmt, T>::value || - llvm::is_base_of<Decl, T>::value), - trying_to_bind_unsupported_node_type__only_decl_and_stmt_supported); return Matcher<T>(new IdMatcher<T>(ID, *this)); } }; @@ -779,6 +778,20 @@ private: const Matcher<T> InnertMatcher2; }; +/// \brief Creates a Matcher<T> that matches if all inner matchers match. +template<typename T> +BindableMatcher<T> makeAllOfComposite( + ArrayRef<const Matcher<T> *> InnerMatchers) { + if (InnerMatchers.empty()) + return BindableMatcher<T>(new TrueMatcher<T>); + MatcherInterface<T> *InnerMatcher = new TrueMatcher<T>; + for (int i = InnerMatchers.size() - 1; i >= 0; --i) { + InnerMatcher = new AllOfMatcher<T, Matcher<T>, Matcher<T> >( + *InnerMatchers[i], makeMatcher(InnerMatcher)); + } + return BindableMatcher<T>(InnerMatcher); +} + /// \brief Creates a Matcher<T> that matches if /// T is dyn_cast'able into InnerT and all inner matchers match. /// @@ -788,17 +801,8 @@ private: template<typename T, typename InnerT> BindableMatcher<T> makeDynCastAllOfComposite( ArrayRef<const Matcher<InnerT> *> InnerMatchers) { - if (InnerMatchers.empty()) { - Matcher<InnerT> InnerMatcher = makeMatcher(new TrueMatcher<InnerT>); - return BindableMatcher<T>(new DynCastMatcher<T, InnerT>(InnerMatcher)); - } - Matcher<InnerT> InnerMatcher = *InnerMatchers.back(); - for (int i = InnerMatchers.size() - 2; i >= 0; --i) { - InnerMatcher = makeMatcher( - new AllOfMatcher<InnerT, Matcher<InnerT>, Matcher<InnerT> >( - *InnerMatchers[i], InnerMatcher)); - } - return BindableMatcher<T>(new DynCastMatcher<T, InnerT>(InnerMatcher)); + return BindableMatcher<T>(new DynCastMatcher<T, InnerT>( + makeAllOfComposite(InnerMatchers))); } /// \brief Matches nodes of type T that have at least one descendant node of @@ -980,6 +984,99 @@ public: VariadicDynCastAllOfMatcher() {} }; +/// \brief A \c VariadicAllOfMatcher<T> object is a variadic functor that takes +/// a number of \c Matcher<T> and returns a \c Matcher<T> that matches \c T +/// nodes that are matched by all of the given matchers. +/// +/// For example: +/// const VariadicAllOfMatcher<NestedNameSpecifier> nestedNameSpecifier; +/// Creates a functor nestedNameSpecifier(...) that creates a +/// \c Matcher<NestedNameSpecifier> given a variable number of arguments of type +/// \c Matcher<NestedNameSpecifier>. +/// The returned matcher matches if all given matchers match. +template <typename T> +class VariadicAllOfMatcher : public llvm::VariadicFunction< + BindableMatcher<T>, Matcher<T>, + makeAllOfComposite<T> > { +public: + VariadicAllOfMatcher() {} +}; + +/// \brief Matches nodes of type \c TLoc for which the inner +/// \c Matcher<T> matches. +template <typename TLoc, typename T> +class LocMatcher : public MatcherInterface<TLoc> { +public: + explicit LocMatcher(const Matcher<T> &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + virtual bool matches(const TLoc &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + if (!Node) + return false; + return InnerMatcher.matches(*extract(Node), Finder, Builder); + } + +private: + const NestedNameSpecifier *extract(const NestedNameSpecifierLoc &Loc) const { + return Loc.getNestedNameSpecifier(); + } + // FIXME: Add overload for TypeLoc when implementing TypeLoc-matchers. + + const Matcher<T> InnerMatcher; +}; + +/// \brief Matches nodes of type \c T for which the inner matcher matches on a +/// another node of type \c T that can be reached using a given traverse +/// function. +template <typename T> +class TraverseMatcher : public MatcherInterface<T> { +public: + explicit TraverseMatcher(const Matcher<T> &InnerMatcher, + T *(T::*TraverseFunction)() const) + : InnerMatcher(InnerMatcher), TraverseFunction(TraverseFunction) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + T* NextNode = (Node.*TraverseFunction)(); + if (NextNode == NULL) + return false; + return InnerMatcher.matches(*NextNode, Finder, Builder); + } + +private: + const Matcher<T> InnerMatcher; + T *(T::*TraverseFunction)() const; +}; + +/// \brief Matches nodes of type \c T in a ..Loc hierarchy, for which the inner +/// matcher matches on a another node of type \c T that can be reached using a +/// given traverse function. +template <typename T> +class LocTraverseMatcher : public MatcherInterface<T> { +public: + explicit LocTraverseMatcher(const Matcher<T> &InnerMatcher, + T (T::*TraverseFunction)() const) + : InnerMatcher(InnerMatcher), TraverseFunction(TraverseFunction) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + if (!Node) + return false; + T NextNode = (Node.*TraverseFunction)(); + if (!NextNode) + return false; + return InnerMatcher.matches(NextNode, Finder, Builder); + } + +private: + const Matcher<T> InnerMatcher; + T (T::*TraverseFunction)() const; +}; + } // end namespace internal } // end namespace ast_matchers } // end namespace clang diff --git a/include/clang/ASTMatchers/ASTMatchersMacros.h b/include/clang/ASTMatchers/ASTMatchersMacros.h index c68534acae..bf122deb7e 100644 --- a/include/clang/ASTMatchers/ASTMatchersMacros.h +++ b/include/clang/ASTMatchers/ASTMatchersMacros.h @@ -221,4 +221,22 @@ const NodeType &Node, ASTMatchFinder *Finder, \ BoundNodesTreeBuilder *Builder) const +/// \brief LOC_TRAVERSE_MATCHER(MatcherName, NodeType, FunctionName) +/// defines the matcher \c MatcherName that can be used to traverse +/// a Type or NestedNameSpecifier as well as the corresponding ..Loc. +/// +/// The traversal is done using the given \c FunctionName. +#define LOC_TRAVERSE_MATCHER( \ + MatcherName, NodeType, FunctionName) \ + inline internal::Matcher<NodeType> hasPrefix( \ + const internal::Matcher<NodeType> &InnerMatcher) { \ + return makeMatcher(new internal::TraverseMatcher<NodeType>( \ + InnerMatcher, &NodeType::getPrefix)); \ + } \ + inline internal::Matcher<NodeType##Loc> hasPrefix( \ + const internal::Matcher<NodeType##Loc> &InnerMatcher) { \ + return makeMatcher(new internal::LocTraverseMatcher<NodeType##Loc>( \ + InnerMatcher, &NodeType##Loc::getPrefix)); \ + } + #endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H diff --git a/include/clang/ASTMatchers/ASTTypeTraits.h b/include/clang/ASTMatchers/ASTTypeTraits.h index db11ce8e58..72ca2ad9da 100644 --- a/include/clang/ASTMatchers/ASTTypeTraits.h +++ b/include/clang/ASTMatchers/ASTTypeTraits.h @@ -74,6 +74,8 @@ private: enum NodeTypeTag { NT_Decl, NT_Stmt, + NT_NestedNameSpecifier, + NT_NestedNameSpecifierLoc, NT_QualType } Tag; @@ -83,7 +85,7 @@ private: /// guaranteed to be unique pointers pointing to dedicated storage in the /// AST. \c QualTypes on the other hand do not have storage or unique /// pointers and thus need to be stored by value. - llvm::AlignedCharArrayUnion<Decl*, Stmt*, QualType> Storage; + llvm::AlignedCharArrayUnion<Decl*, Stmt*, NestedNameSpecifierLoc, QualType> Storage; }; template<typename T> struct DynTypedNode::BaseConverter<T, typename llvm::enable_if<llvm::is_base_of<Decl, T> >::type> { @@ -113,6 +115,33 @@ template<typename T> struct DynTypedNode::BaseConverter<T, return Result; } }; +template<> struct DynTypedNode::BaseConverter<NestedNameSpecifier, void> { + static const NestedNameSpecifier *get(NodeTypeTag Tag, const char Storage[]) { + if (Tag == NT_NestedNameSpecifier) + return *reinterpret_cast<NestedNameSpecifier*const*>(Storage); + return NULL; + } + static DynTypedNode create(const NestedNameSpecifier &Node) { + DynTypedNode Result; + Result.Tag = NT_NestedNameSpecifier; + new (Result.Storage.buffer) const NestedNameSpecifier*(&Node); + return Result; + } +}; +template<> struct DynTypedNode::BaseConverter<NestedNameSpecifierLoc, void> { + static const NestedNameSpecifierLoc *get(NodeTypeTag Tag, + const char Storage[]) { + if (Tag == NT_NestedNameSpecifierLoc) + return reinterpret_cast<const NestedNameSpecifierLoc*>(Storage); + return NULL; + } + static DynTypedNode create(const NestedNameSpecifierLoc &Node) { + DynTypedNode Result; + Result.Tag = NT_NestedNameSpecifierLoc; + new (Result.Storage.buffer) NestedNameSpecifierLoc(Node); + return Result; + } +}; template<> struct DynTypedNode::BaseConverter<QualType, void> { static const QualType *get(NodeTypeTag Tag, const char Storage[]) { if (Tag == NT_QualType) @@ -146,4 +175,3 @@ inline const void *DynTypedNode::getMemoizationData() const { } // end namespace clang #endif // LLVM_CLANG_AST_MATCHERS_AST_TYPE_TRAITS_H - diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index afab1df4a6..cba2e50337 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -313,6 +313,8 @@ public: bool TraverseStmt(Stmt *StmtNode); bool TraverseType(QualType TypeNode); bool TraverseTypeLoc(TypeLoc TypeNode); + bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS); + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS); // Matches children or descendants of 'Node' with 'BaseMatcher'. bool memoizedMatchesRecursively(const ast_type_traits::DynTypedNode &Node, @@ -556,6 +558,21 @@ bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLoc) { TraverseTypeLoc(TypeLoc); } +bool MatchASTVisitor::TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { + match(*NNS); + return RecursiveASTVisitor<MatchASTVisitor>::TraverseNestedNameSpecifier(NNS); +} + +bool MatchASTVisitor::TraverseNestedNameSpecifierLoc( + NestedNameSpecifierLoc NNS) { + match(NNS); + // We only match the nested name specifier here (as opposed to traversing it) + // because the traversal is already done in the parallel "Loc"-hierarchy. + match(*NNS.getNestedNameSpecifier()); + return + RecursiveASTVisitor<MatchASTVisitor>::TraverseNestedNameSpecifierLoc(NNS); +} + class MatchASTConsumer : public ASTConsumer { public: MatchASTConsumer( @@ -619,6 +636,18 @@ void MatchFinder::addMatcher(const StatementMatcher &NodeMatch, new internal::Matcher<Stmt>(NodeMatch), Action)); } +void MatchFinder::addMatcher(const NestedNameSpecifierMatcher &NodeMatch, + MatchCallback *Action) { + MatcherCallbackPairs.push_back(std::make_pair( + new NestedNameSpecifierMatcher(NodeMatch), Action)); +} + +void MatchFinder::addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch, + MatchCallback *Action) { + MatcherCallbackPairs.push_back(std::make_pair( + new NestedNameSpecifierLocMatcher(NodeMatch), Action)); +} + ASTConsumer *MatchFinder::newASTConsumer() { return new internal::MatchASTConsumer(&MatcherCallbackPairs, ParsingDone); } diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index 8b41c969ba..c6e161da93 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -607,36 +607,40 @@ TEST(TypeMatcher, MatchesClassType) { // 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 VerifyIdIsBoundToDecl : public BoundNodesCallback { +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 VerifyIdIsBoundToDecl(llvm::StringRef Id) + 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. - VerifyIdIsBoundToDecl(llvm::StringRef Id, int ExpectedCount) + 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 ExpectedDeclName. + // Checks that there was exactly one match with the name \c ExpectedName. // Note that \c T must be a NamedDecl for this to work. - VerifyIdIsBoundToDecl(llvm::StringRef Id, llvm::StringRef ExpectedDeclName) - : Id(Id), ExpectedCount(1), Count(0), ExpectedDeclName(ExpectedDeclName) {} + VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName) + : Id(Id), ExpectedCount(1), Count(0), ExpectedName(ExpectedName) {} - ~VerifyIdIsBoundToDecl() { + ~VerifyIdIsBoundTo() { if (ExpectedCount != -1) EXPECT_EQ(ExpectedCount, Count); - if (!ExpectedDeclName.empty()) - EXPECT_EQ(ExpectedDeclName, DeclName); + if (!ExpectedName.empty()) + EXPECT_EQ(ExpectedName, Name); } virtual bool run(const BoundNodes *Nodes) { - if (const Decl *Node = Nodes->getDeclAs<T>(Id)) { + if (Nodes->getNodeAs<T>(Id)) { ++Count; - if (const NamedDecl *Named = llvm::dyn_cast<NamedDecl>(Node)) { - DeclName = Named->getNameAsString(); + 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; } @@ -647,43 +651,32 @@ private: const std::string Id; const int ExpectedCount; int Count; - const std::string ExpectedDeclName; - std::string DeclName; -}; -template <typename T> -class VerifyIdIsBoundToStmt : public BoundNodesCallback { -public: - explicit VerifyIdIsBoundToStmt(const std::string &Id) : Id(Id) {} - virtual bool run(const BoundNodes *Nodes) { - const T *Node = Nodes->getStmtAs<T>(Id); - return Node != NULL; - } -private: - const std::string Id; + const std::string ExpectedName; + std::string Name; }; TEST(Matcher, BindMatchedNodes) { DeclarationMatcher ClassX = has(recordDecl(hasName("::X")).bind("x")); EXPECT_TRUE(matchAndVerifyResultTrue("class X {};", - ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("x"))); + ClassX, new VerifyIdIsBoundTo<CXXRecordDecl>("x"))); EXPECT_TRUE(matchAndVerifyResultFalse("class X {};", - ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("other-id"))); + ClassX, new VerifyIdIsBoundTo<CXXRecordDecl>("other-id"))); TypeMatcher TypeAHasClassB = hasDeclaration( recordDecl(hasName("A"), has(recordDecl(hasName("B")).bind("b")))); EXPECT_TRUE(matchAndVerifyResultTrue("class A { public: A *a; class B {}; };", TypeAHasClassB, - new VerifyIdIsBoundToDecl<Decl>("b"))); + new VerifyIdIsBoundTo<Decl>("b"))); StatementMatcher MethodX = callExpr(callee(methodDecl(hasName("x")))).bind("x"); EXPECT_TRUE(matchAndVerifyResultTrue("class A { void x() { x(); } };", MethodX, - new VerifyIdIsBoundToStmt<CXXMemberCallExpr>("x"))); + new VerifyIdIsBoundTo<CXXMemberCallExpr>("x"))); } TEST(Matcher, BindTheSameNameInAlternatives) { @@ -700,7 +693,7 @@ TEST(Matcher, BindTheSameNameInAlternatives) { // The second branch binds x to f() and succeeds. "int f() { return 0 + f(); }", matcher, - new VerifyIdIsBoundToStmt<CallExpr>("x"))); + new VerifyIdIsBoundTo<CallExpr>("x"))); } TEST(Matcher, BindsIDForMemoizedResults) { @@ -712,7 +705,7 @@ TEST(Matcher, BindsIDForMemoizedResults) { DeclarationMatcher(anyOf( recordDecl(hasName("A"), hasDescendant(ClassX)), recordDecl(hasName("B"), hasDescendant(ClassX)))), - new VerifyIdIsBoundToDecl<Decl>("x", 2))); + new VerifyIdIsBoundTo<Decl>("x", 2))); } TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) { @@ -1918,13 +1911,13 @@ TEST(AstMatcherPMacro, Works) { DeclarationMatcher HasClassB = just(has(recordDecl(hasName("B")).bind("b"))); EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("b"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("b"))); EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("a"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("a"))); EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("b"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("b"))); } AST_POLYMORPHIC_MATCHER_P( @@ -1943,13 +1936,13 @@ TEST(AstPolymorphicMatcherPMacro, Works) { polymorphicHas(recordDecl(hasName("B")).bind("b")); EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("b"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("b"))); EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("a"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("a"))); EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("b"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("b"))); StatementMatcher StatementHasClassB = polymorphicHas(recordDecl(hasName("B"))); @@ -2579,13 +2572,13 @@ TEST(HasConditionVariableStatement, MatchesConditionVariables) { TEST(ForEach, BindsOneNode) { EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; };", recordDecl(hasName("C"), forEach(fieldDecl(hasName("x")).bind("x"))), - new VerifyIdIsBoundToDecl<FieldDecl>("x", 1))); + new VerifyIdIsBoundTo<FieldDecl>("x", 1))); } TEST(ForEach, BindsMultipleNodes) { EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; int y; int z; };", recordDecl(hasName("C"), forEach(fieldDecl().bind("f"))), - new VerifyIdIsBoundToDecl<FieldDecl>("f", 3))); + new VerifyIdIsBoundTo<FieldDecl>("f", 3))); } TEST(ForEach, BindsRecursiveCombinations) { @@ -2593,14 +2586,14 @@ TEST(ForEach, BindsRecursiveCombinations) { "class C { class D { int x; int y; }; class E { int y; int z; }; };", recordDecl(hasName("C"), forEach(recordDecl(forEach(fieldDecl().bind("f"))))), - new VerifyIdIsBoundToDecl<FieldDecl>("f", 4))); + new VerifyIdIsBoundTo<FieldDecl>("f", 4))); } TEST(ForEachDescendant, BindsOneNode) { EXPECT_TRUE(matchAndVerifyResultTrue("class C { class D { int x; }; };", recordDecl(hasName("C"), forEachDescendant(fieldDecl(hasName("x")).bind("x"))), - new VerifyIdIsBoundToDecl<FieldDecl>("x", 1))); + new VerifyIdIsBoundTo<FieldDecl>("x", 1))); } TEST(ForEachDescendant, BindsMultipleNodes) { @@ -2608,7 +2601,7 @@ TEST(ForEachDescendant, BindsMultipleNodes) { "class C { class D { int x; int y; }; " " class E { class F { int y; int z; }; }; };", recordDecl(hasName("C"), forEachDescendant(fieldDecl().bind("f"))), - new VerifyIdIsBoundToDecl<FieldDecl>("f", 4))); + new VerifyIdIsBoundTo<FieldDecl>("f", 4))); } TEST(ForEachDescendant, BindsRecursiveCombinations) { @@ -2617,7 +2610,7 @@ TEST(ForEachDescendant, BindsRecursiveCombinations) { " class E { class F { class G { int y; int z; }; }; }; }; };", recordDecl(hasName("C"), forEachDescendant(recordDecl( forEachDescendant(fieldDecl().bind("f"))))), - new VerifyIdIsBoundToDecl<FieldDecl>("f", 8))); + new VerifyIdIsBoundTo<FieldDecl>("f", 8))); } @@ -2775,7 +2768,7 @@ TEST(HasAncestor, BindsRecursiveCombinations) { EXPECT_TRUE(matchAndVerifyResultTrue( "class C { class D { class E { class F { int y; }; }; }; };", fieldDecl(hasAncestor(recordDecl(hasAncestor(recordDecl().bind("r"))))), - new VerifyIdIsBoundToDecl<CXXRecordDecl>("r", 1))); + new VerifyIdIsBoundTo<CXXRecordDecl>("r", 1))); } TEST(HasAncestor, BindsCombinationsWithHasDescendant) { @@ -2787,7 +2780,7 @@ TEST(HasAncestor, BindsCombinationsWithHasDescendant) { hasAncestor(recordDecl()))) ).bind("d") )), - new VerifyIdIsBoundToDecl<CXXRecordDecl>("d", "E"))); + new VerifyIdIsBoundTo<CXXRecordDecl>("d", "E"))); } TEST(HasAncestor, MatchesInTemplateInstantiations) { @@ -2806,5 +2799,62 @@ TEST(HasAncestor, MatchesInImplicitCode) { hasAncestor(recordDecl(hasName("A"))))))))); } +TEST(NNS, MatchesNestedNameSpecifiers) { + EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;", + nestedNameSpecifier())); + EXPECT_TRUE(matches("template <typename T> class A { typename T::B b; };", + nestedNameSpecifier())); + EXPECT_TRUE(matches("struct A { void f(); }; void A::f() {}", + nestedNameSpecifier())); + + EXPECT_TRUE(matches( + "struct A { static void f() {} }; void g() { A::f(); }", + nestedNameSpecifier())); + EXPECT_TRUE(notMatches( + "struct A { static void f() {} }; void g(A* a) { a->f(); }", + nestedNameSpecifier())); +} + +TEST(NNS, MatchesTypes) { + NestedNameSpecifierMatcher Matcher = nestedNameSpecifier( + specifiesType(hasDeclaration(recordDecl(hasName("A"))))); + EXPECT_TRUE(matches("struct A { struct B {}; }; A::B b;", Matcher)); + EXPECT_TRUE(matches("struct A { struct B { struct C {}; }; }; A::B::C c;", + Matcher)); + EXPECT_TRUE(notMatches("namespace A { struct B {}; } A::B b;", Matcher)); +} + +TEST(NNS, MatchesNamespaceDecls) { + NestedNameSpecifierMatcher Matcher = nestedNameSpecifier( + specifiesNamespace(hasName("ns"))); + EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;", Matcher)); + EXPECT_TRUE(notMatches("namespace xx { struct A {}; } xx::A a;", Matcher)); + EXPECT_TRUE(notMatches("struct ns { struct A {}; }; ns::A a;", Matcher)); +} + +TEST(NNS, BindsNestedNameSpecifiers) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "namespace ns { struct E { struct B {}; }; } ns::E::B b;", + nestedNameSpecifier(specifiesType(asString("struct ns::E"))).bind("nns"), + new VerifyIdIsBoundTo<NestedNameSpecifier>("nns", "ns::struct E::"))); +} + +TEST(NNS, BindsNestedNameSpecifierLocs) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "namespace ns { struct B {}; } ns::B b;", + loc(nestedNameSpecifier()).bind("loc"), + new VerifyIdIsBoundTo<NestedNameSpecifierLoc>("loc", 1))); +} + +TEST(NNS, MatchesNestedNameSpecifierPrefixes) { + EXPECT_TRUE(matches( + "struct A { struct B { struct C {}; }; }; A::B::C c;", + nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))))); + EXPECT_TRUE(matches( + "struct A { struct B { struct C {}; }; }; A::B::C c;", + nestedNameSpecifierLoc(hasPrefix(loc( + specifiesType(asString("struct A"))))))); +} + } // end namespace ast_matchers } // end namespace clang |