RecursiveASTVisitor: When in 'shouldVisitTemplateInstantiations' mode, visit
all instantiations of a template when we visit the canonical declaration of the
primary template, rather than trying to match them up to the partial
specialization from which they are instantiated. This fixes a bug where we
failed to visit instantiations of partial specializations of member templates of
class templates, and naturally extends to allow us to visit instantiations where
we have instantiated only a declaration.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@155597 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp
index 6ef2786..247a5fe 100644
--- a/unittests/Tooling/RecursiveASTVisitorTest.cpp
+++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp
@@ -152,6 +152,19 @@
   }
 };
 
+class NamedDeclVisitor
+  : public ExpectedLocationVisitor<NamedDeclVisitor> {
+public:
+  bool VisitNamedDecl(NamedDecl *Decl) {
+    std::string NameWithTemplateArgs;
+    Decl->getNameForDiagnostic(NameWithTemplateArgs,
+                               Decl->getASTContext().getPrintingPolicy(),
+                               true);
+    Match(NameWithTemplateArgs, Decl->getLocation());
+    return true;
+  }
+};
+
 TEST(RecursiveASTVisitor, VisitsBaseClassDeclarations) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("class X", 1, 30);
@@ -251,4 +264,85 @@
 }
 */
 
+TEST(RecursiveASTVisitor, VisitsCallInPartialTemplateSpecialization) {
+  CXXMemberCallVisitor Visitor;
+  Visitor.ExpectMatch("A::x", 6, 20);
+  EXPECT_TRUE(Visitor.runOver(
+    "template <typename T1> struct X {\n"
+    "  template <typename T2, bool B> struct Y { void g(); };\n"
+    "};\n"
+    "template <typename T1> template <typename T2>\n"
+    "struct X<T1>::Y<T2, true> {\n"
+    "  void f() { T2 y; y.x(); }\n"
+    "};\n"
+    "struct A { void x(); };\n"
+    "int main() {\n"
+    "  (new X<A>::Y<A, true>())->f();\n"
+    "}\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsPartialTemplateSpecialization) {
+  // From cfe-commits/Week-of-Mon-20100830/033998.html
+  // Contrary to the approach sugggested in that email, we visit all
+  // specializations when we visit the primary template.  Visiting them when we
+  // visit the associated specialization is problematic for specializations of
+  // template members of class templates.
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<bool>", 1, 26);
+  Visitor.ExpectMatch("A<char *>", 2, 26);
+  EXPECT_TRUE(Visitor.runOver(
+    "template <class T> class A {};\n"
+    "template <class T> class A<T*> {};\n"
+    "A<bool> ab;\n"
+    "A<char*> acp;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsUndefinedClassTemplateSpecialization) {
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<int>", 1, 29);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> struct A;\n"
+    "A<int> *p;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsNestedUndefinedClassTemplateSpecialization) {
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<int>::B<char>", 2, 31);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> struct A {\n"
+    "  template<typename U> struct B;\n"
+    "};\n"
+    "A<int>::B<char> *p;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsUndefinedFunctionTemplateSpecialization) {
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<int>", 1, 26);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> int A();\n"
+    "int k = A<int>();\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsNestedUndefinedFunctionTemplateSpecialization) {
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<int>::B<char>", 2, 35);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> struct A {\n"
+    "  template<typename U> static int B();\n"
+    "};\n"
+    "int k = A<int>::B<char>();\n"));
+}
+
+TEST(RecursiveASTVisitor, NoRecursionInSelfFriend) {
+  // From cfe-commits/Week-of-Mon-20100830/033977.html
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("vector_iterator<int>", 2, 7);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename Container>\n"
+    "class vector_iterator {\n"
+    "    template <typename C> friend class vector_iterator;\n"
+    "};\n"
+    "vector_iterator<int> it_int;\n"));
+}
+
 } // end namespace clang