]> granicus.if.org Git - clang/commitdiff
Add Tooling functionality to get a name for a QualType that can be used to name
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 9 Feb 2016 21:04:04 +0000 (21:04 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 9 Feb 2016 21:04:04 +0000 (21:04 +0000)
that type from the global scope.

Patch by Sterling Augustine, derived (with permission) from code from Cling by
Vassil Vassilev and Philippe Canal.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260278 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Tooling/Core/QualTypeNames.h [new file with mode: 0644]
lib/Tooling/Core/CMakeLists.txt
lib/Tooling/Core/QualTypeNames.cpp [new file with mode: 0644]
unittests/Tooling/CMakeLists.txt
unittests/Tooling/QualTypeNamesTest.cpp [new file with mode: 0644]

diff --git a/include/clang/Tooling/Core/QualTypeNames.h b/include/clang/Tooling/Core/QualTypeNames.h
new file mode 100644 (file)
index 0000000..49624a3
--- /dev/null
@@ -0,0 +1,76 @@
+//===--- QualTypeNames.h - Generate Complete QualType Names ----*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+// ===----------------------------------------------------------------------===//
+//
+// \file
+// Functionality to generate the fully-qualified names of QualTypes,
+// including recursively expanding any subtypes and template
+// parameters.
+//
+// More precisely: Generates a name that can be used to name the same
+// type if used at the end of the current translation unit--with
+// certain limitations. See below.
+//
+// This code desugars names only very minimally, so in this code:
+//
+// namespace A {
+//   struct X {};
+// }
+// using A::X;
+// namespace B {
+//   using std::tuple;
+//   typedef tuple<X> TX;
+//   TX t;
+// }
+//
+// B::t's type is reported as "B::TX", rather than std::tuple<A::X>.
+//
+// Also, this code replaces types found via using declarations with
+// their more qualified name, so for the code:
+//
+// using std::tuple;
+// tuple<int> TInt;
+//
+// TInt's type will be named, "std::tuple<int>".
+//
+// Limitations:
+//
+// Some types have ambiguous names at the end of a translation unit,
+// are not namable at all there, or are special cases in other ways.
+//
+// 1) Types with only local scope will have their local names:
+//
+// void foo() {
+//   struct LocalType {} LocalVar;
+// }
+//
+// LocalVar's type will be named, "struct LocalType", without any
+// qualification.
+//
+// 2) Types that have been shadowed are reported normally, but a
+// client using that name at the end of the translation unit will be
+// referring to a different type.
+//
+// ===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_CORE_QUALTYPENAMES_H
+#define LLVM_CLANG_TOOLING_CORE_QUALTYPENAMES_H
+
+#include "clang/AST/ASTContext.h"
+
+namespace clang {
+namespace TypeName {
+/// \brief Get the fully qualified name for a type. This includes full
+/// qualification of all template parameters etc.
+///
+/// \param[in] QT - the type for which the fully qualified name will be
+/// returned.
+/// \param[in] Ctx - the ASTContext to be used.
+std::string getFullyQualifiedName(QualType QT,
+                                  const ASTContext &Ctx);
+}  // end namespace TypeName
+}  // end namespace clang
+#endif  // LLVM_CLANG_TOOLING_CORE_QUALTYPENAMES_H
index b88e1f8333a260b71fcecef0c8549e01d3483f45..f6348cbf80ed6d00c80c6f35845de636af2227e3 100644 (file)
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support)
 add_clang_library(clangToolingCore
   Lookup.cpp
   Replacement.cpp
+  QualTypeNames.cpp
 
   LINK_LIBS
   clangAST
diff --git a/lib/Tooling/Core/QualTypeNames.cpp b/lib/Tooling/Core/QualTypeNames.cpp
new file mode 100644 (file)
index 0000000..6c3ff14
--- /dev/null
@@ -0,0 +1,432 @@
+//===------- QualTypeNames.cpp - Generate Complete QualType Names ---------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Core/QualTypeNames.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/GlobalDecl.h"
+#include "clang/AST/Mangle.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <stdio.h>
+#include <memory>
+
+namespace clang {
+
+namespace TypeName {
+/// \brief Generates a QualType that can be used to name the same type
+/// if used at the end of the current translation unit. This ignores
+/// issues such as type shadowing.
+///
+/// \param[in] QT - the type for which the fully qualified type will be
+/// returned.
+/// \param[in] Ctx - the ASTContext to be used.
+static QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx);
+
+/// \brief Create a NestedNameSpecifier for Namesp and its enclosing
+/// scopes.
+///
+/// \param[in] Ctx - the AST Context to be used.
+/// \param[in] Namesp - the NamespaceDecl for which a NestedNameSpecifier
+/// is requested.
+static NestedNameSpecifier *createNestedNameSpecifier(
+    const ASTContext &Ctx, const NamespaceDecl *Namesp);
+
+/// \brief Create a NestedNameSpecifier for TagDecl and its enclosing
+/// scopes.
+///
+/// \param[in] Ctx - the AST Context to be used.
+/// \param[in] TD - the TagDecl for which a NestedNameSpecifier is
+/// requested.
+/// \param[in] FullyQualify - Convert all template arguments into fully
+/// qualified names.
+static NestedNameSpecifier *createNestedNameSpecifier(
+    const ASTContext &Ctx, const TypeDecl *TD, bool FullyQualify);
+
+static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
+    const ASTContext &Ctx, const Decl *decl, bool FullyQualified);
+
+static NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
+    const ASTContext &Ctx, NestedNameSpecifier *scope);
+
+static bool getFullyQualifiedTemplateName(const ASTContext &Ctx,
+                                          TemplateName &TName) {
+  bool Changed = false;
+  NestedNameSpecifier *NNS = nullptr;
+
+  TemplateDecl *ArgTDecl = TName.getAsTemplateDecl();
+  // ArgTDecl won't be NULL because we asserted that this isn't a
+  // dependent context very early in the call chain.
+  assert(ArgTDecl != nullptr);
+  QualifiedTemplateName *QTName = TName.getAsQualifiedTemplateName();
+
+  if (QTName && !QTName->hasTemplateKeyword()) {
+    NNS = QTName->getQualifier();
+    NestedNameSpecifier *QNNS = getFullyQualifiedNestedNameSpecifier(Ctx, NNS);
+    if (QNNS != NNS) {
+      Changed = true;
+      NNS = QNNS;
+    } else {
+      NNS = nullptr;
+    }
+  } else {
+    NNS = createNestedNameSpecifierForScopeOf(Ctx, ArgTDecl, true);
+  }
+  if (NNS) {
+    TName = Ctx.getQualifiedTemplateName(NNS,
+                                         /*TemplateKeyword=*/false, ArgTDecl);
+    Changed = true;
+  }
+  return Changed;
+}
+
+static bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx,
+                                              TemplateArgument &Arg) {
+  bool Changed = false;
+
+  // Note: we do not handle TemplateArgument::Expression, to replace it
+  // we need the information for the template instance decl.
+
+  if (Arg.getKind() == TemplateArgument::Template) {
+    TemplateName TName = Arg.getAsTemplate();
+    Changed = getFullyQualifiedTemplateName(Ctx, TName);
+    if (Changed) {
+      Arg = TemplateArgument(TName);
+    }
+  } else if (Arg.getKind() == TemplateArgument::Type) {
+    QualType SubTy = Arg.getAsType();
+    // Check if the type needs more desugaring and recurse.
+    QualType QTFQ = getFullyQualifiedType(SubTy, Ctx);
+    if (QTFQ != SubTy) {
+      Arg = TemplateArgument(QTFQ);
+      Changed = true;
+    }
+  }
+  return Changed;
+}
+
+static const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx,
+                                                 const Type *TypePtr) {
+  // DependentTemplateTypes exist within template declarations and
+  // definitions. Therefore we shouldn't encounter them at the end of
+  // a translation unit. If we do, the caller has made an error.
+  assert(!isa<DependentTemplateSpecializationType>(TypePtr));
+  // In case of template specializations, iterate over the arguments
+  // and fully qualify them as well.
+  if (const auto *TST = dyn_cast<const TemplateSpecializationType>(TypePtr)) {
+    bool MightHaveChanged = false;
+    SmallVector<TemplateArgument, 4> FQArgs;
+    for (TemplateSpecializationType::iterator I = TST->begin(), E = TST->end();
+         I != E; ++I) {
+      // Cheap to copy and potentially modified by
+      // getFullyQualifedTemplateArgument.
+      TemplateArgument Arg(*I);
+      MightHaveChanged |= getFullyQualifiedTemplateArgument(Ctx, Arg);
+      FQArgs.push_back(Arg);
+    }
+
+    // If a fully qualified arg is different from the unqualified arg,
+    // allocate new type in the AST.
+    if (MightHaveChanged) {
+      QualType QT = Ctx.getTemplateSpecializationType(
+          TST->getTemplateName(), FQArgs.data(), FQArgs.size(),
+          TST->getCanonicalTypeInternal());
+      // getTemplateSpecializationType returns a fully qualified
+      // version of the specialization itself, so no need to qualify
+      // it.
+      return QT.getTypePtr();
+    }
+  } else if (const auto *TSTRecord = dyn_cast<const RecordType>(TypePtr)) {
+    // We are asked to fully qualify and we have a Record Type,
+    // which can point to a template instantiation with no sugar in any of
+    // its template argument, however we still need to fully qualify them.
+
+    if (const auto *TSTDecl =
+        dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl())) {
+      const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs();
+
+      bool MightHaveChanged = false;
+      SmallVector<TemplateArgument, 4> FQArgs;
+      for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) {
+        // cheap to copy and potentially modified by
+        // getFullyQualifedTemplateArgument
+        TemplateArgument Arg(TemplateArgs[I]);
+        MightHaveChanged |= getFullyQualifiedTemplateArgument(Ctx, Arg);
+        FQArgs.push_back(Arg);
+      }
+
+      // If a fully qualified arg is different from the unqualified arg,
+      // allocate new type in the AST.
+      if (MightHaveChanged) {
+        TemplateName TN(TSTDecl->getSpecializedTemplate());
+        QualType QT = Ctx.getTemplateSpecializationType(
+            TN, FQArgs.data(), FQArgs.size(),
+            TSTRecord->getCanonicalTypeInternal());
+        // getTemplateSpecializationType returns a fully qualified
+        // version of the specialization itself, so no need to qualify
+        // it.
+        return QT.getTypePtr();
+      }
+    }
+  }
+  return TypePtr;
+}
+
+static NestedNameSpecifier *createOuterNNS(const ASTContext &Ctx, const Decl *D,
+                                           bool FullyQualify) {
+  const DeclContext *DC = D->getDeclContext();
+  if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
+    while (NS && NS->isInline()) {
+      // Ignore inline namespace;
+      NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
+    }
+    if (NS->getDeclName()) return createNestedNameSpecifier(Ctx, NS);
+    return nullptr;  // no starting '::', no anonymous
+  } else if (const auto *TD = dyn_cast<TagDecl>(DC)) {
+    return createNestedNameSpecifier(Ctx, TD, FullyQualify);
+  } else if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC)) {
+    return createNestedNameSpecifier(Ctx, TDD, FullyQualify);
+  }
+  return nullptr;  // no starting '::'
+}
+
+/// \brief Return a fully qualified version of this name specifier.
+static NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
+    const ASTContext &Ctx, NestedNameSpecifier *Scope) {
+  switch (Scope->getKind()) {
+    case NestedNameSpecifier::Global:
+      // Already fully qualified
+      return Scope;
+    case NestedNameSpecifier::Namespace:
+      return TypeName::createNestedNameSpecifier(Ctx, Scope->getAsNamespace());
+    case NestedNameSpecifier::NamespaceAlias:
+      // Namespace aliases are only valid for the duration of the
+      // scope where they were introduced, and therefore are often
+      // invalid at the end of the TU.  So use the namespace name more
+      // likely to be valid at the end of the TU.
+      return TypeName::createNestedNameSpecifier(
+          Ctx, Scope->getAsNamespaceAlias()->getNamespace()->getCanonicalDecl());
+    case NestedNameSpecifier::Identifier:
+      // A function or some other construct that makes it un-namable
+      // at the end of the TU. Skip the current component of the name,
+      // but use the name of it's prefix.
+      return getFullyQualifiedNestedNameSpecifier(Ctx, Scope->getPrefix());
+    case NestedNameSpecifier::Super:
+    case NestedNameSpecifier::TypeSpec:
+    case NestedNameSpecifier::TypeSpecWithTemplate: {
+      const Type *Type = Scope->getAsType();
+      // Find decl context.
+      const TagDecl *TD = nullptr;
+      if (const TagType *TagDeclType = Type->getAs<TagType>()) {
+        TD = TagDeclType->getDecl();
+      } else {
+        TD = Type->getAsCXXRecordDecl();
+      }
+      if (TD) {
+        return TypeName::createNestedNameSpecifier(Ctx, TD,
+                                                   true /*FullyQualified*/);
+      } else if (const auto *TDD = dyn_cast<TypedefType>(Type)) {
+        return TypeName::createNestedNameSpecifier(Ctx, TDD->getDecl(),
+                                                   true /*FullyQualified*/);
+      }
+      return Scope;
+    }
+  }
+}
+
+/// \brief Create a nested name specifier for the declaring context of
+/// the type.
+static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
+    const ASTContext &Ctx, const Decl *Decl, bool FullyQualified) {
+  assert(Decl);
+
+  const DeclContext *DC = Decl->getDeclContext()->getRedeclContext();
+  const auto *Outer = dyn_cast_or_null<NamedDecl>(DC);
+  const auto *OuterNS = dyn_cast_or_null<NamespaceDecl>(DC);
+  if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) {
+    if (const auto *CxxDecl = dyn_cast<CXXRecordDecl>(DC)) {
+      if (ClassTemplateDecl *ClassTempl =
+              CxxDecl->getDescribedClassTemplate()) {
+        // We are in the case of a type(def) that was declared in a
+        // class template but is *not* type dependent.  In clang, it
+        // gets attached to the class template declaration rather than
+        // any specific class template instantiation.  This result in
+        // 'odd' fully qualified typename:
+        //
+        //    vector<_Tp,_Alloc>::size_type
+        //
+        // Make the situation is 'useable' but looking a bit odd by
+        // picking a random instance as the declaring context.
+        if (ClassTempl->spec_begin() != ClassTempl->spec_end()) {
+          Decl = *(ClassTempl->spec_begin());
+          Outer = dyn_cast<NamedDecl>(Decl);
+          OuterNS = dyn_cast<NamespaceDecl>(Decl);
+        }
+      }
+    }
+
+    if (OuterNS) {
+      return createNestedNameSpecifier(Ctx, OuterNS);
+    } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
+      return createNestedNameSpecifier(Ctx, TD, FullyQualified);
+    } else if (dyn_cast<TranslationUnitDecl>(Outer)) {
+      // Context is the TU. Nothing needs to be done.
+      return nullptr;
+    } else {
+      // Decl's context was neither the TU, a namespace, nor a
+      // TagDecl, which means it is a type local to a scope, and not
+      // accessible at the end of the TU.
+      return nullptr;
+    }
+  }
+  return nullptr;
+}
+
+/// \brief Create a nested name specifier for the declaring context of
+/// the type.
+static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
+    const ASTContext &Ctx, const Type *TypePtr, bool FullyQualified) {
+  if (!TypePtr) return nullptr;
+
+  Decl *Decl = nullptr;
+  // There are probably other cases ...
+  if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) {
+    Decl = TDT->getDecl();
+  } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) {
+    Decl = TagDeclType->getDecl();
+  } else {
+    Decl = TypePtr->getAsCXXRecordDecl();
+  }
+
+  if (!Decl) return nullptr;
+
+  return createNestedNameSpecifierForScopeOf(Ctx, Decl, FullyQualified);
+}
+
+NestedNameSpecifier *createNestedNameSpecifier(
+    const ASTContext &Ctx, const NamespaceDecl *Namespace) {
+  while (Namespace && Namespace->isInline()) {
+    // Ignore inline namespace;
+    Namespace = dyn_cast<NamespaceDecl>(Namespace->getDeclContext());
+  }
+  if (!Namespace) return nullptr;
+
+  bool FullyQualified = true;  // doesn't matter, DeclContexts are namespaces
+  return NestedNameSpecifier::Create(
+      Ctx, createOuterNNS(Ctx, Namespace, FullyQualified), Namespace);
+}
+
+NestedNameSpecifier *createNestedNameSpecifier(
+    const ASTContext &Ctx, const TypeDecl *TD, bool FullyQualify) {
+  return NestedNameSpecifier::Create(Ctx, createOuterNNS(Ctx, TD, FullyQualify),
+                                     true /*Template*/, TD->getTypeForDecl());
+}
+
+/// \brief Return the fully qualified type, including fully-qualified
+/// versions of any template parameters.
+QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx) {
+  // In case of myType* we need to strip the pointer first, fully
+  // qualify and attach the pointer once again.
+  if (isa<PointerType>(QT.getTypePtr())) {
+    // Get the qualifiers.
+    Qualifiers Quals = QT.getQualifiers();
+    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx);
+    QT = Ctx.getPointerType(QT);
+    // Add back the qualifiers.
+    QT = Ctx.getQualifiedType(QT, Quals);
+    return QT;
+  }
+
+  // In case of myType& we need to strip the reference first, fully
+  // qualify and attach the reference once again.
+  if (isa<ReferenceType>(QT.getTypePtr())) {
+    // Get the qualifiers.
+    bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
+    Qualifiers Quals = QT.getQualifiers();
+    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx);
+    // Add the r- or l-value reference type back to the fully
+    // qualified one.
+    if (IsLValueRefTy)
+      QT = Ctx.getLValueReferenceType(QT);
+    else
+      QT = Ctx.getRValueReferenceType(QT);
+    // Add back the qualifiers.
+    QT = Ctx.getQualifiedType(QT, Quals);
+    return QT;
+  }
+
+  // Remove the part of the type related to the type being a template
+  // parameter (we won't report it as part of the 'type name' and it
+  // is actually make the code below to be more complex (to handle
+  // those)
+  while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) {
+    // Get the qualifiers.
+    Qualifiers Quals = QT.getQualifiers();
+
+    QT = dyn_cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar();
+
+    // Add back the qualifiers.
+    QT = Ctx.getQualifiedType(QT, Quals);
+  }
+
+  NestedNameSpecifier *Prefix = nullptr;
+  Qualifiers PrefixQualifiers;
+  ElaboratedTypeKeyword Keyword = ETK_None;
+  if (const auto *ETypeInput = dyn_cast<ElaboratedType>(QT.getTypePtr())) {
+    QT = ETypeInput->getNamedType();
+    Keyword = ETypeInput->getKeyword();
+  }
+  // Create a nested name specifier if needed (i.e. if the decl context
+  // is not the global scope.
+  Prefix = createNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(),
+                                               true /*FullyQualified*/);
+
+  // move the qualifiers on the outer type (avoid 'std::const string'!)
+  if (Prefix) {
+    PrefixQualifiers = QT.getLocalQualifiers();
+    QT = QualType(QT.getTypePtr(), 0);
+  }
+
+  // In case of template specializations iterate over the arguments and
+  // fully qualify them as well.
+  if (isa<const TemplateSpecializationType>(QT.getTypePtr()) ||
+      isa<const RecordType>(QT.getTypePtr())) {
+    // We are asked to fully qualify and we have a Record Type (which
+    // may pont to a template specialization) or Template
+    // Specialization Type. We need to fully qualify their arguments.
+
+    Qualifiers Quals = QT.getLocalQualifiers();
+    const Type *TypePtr = getFullyQualifiedTemplateType(Ctx, QT.getTypePtr());
+    QT = Ctx.getQualifiedType(TypePtr, Quals);
+  }
+  if (Prefix || Keyword != ETK_None) {
+    QT = Ctx.getElaboratedType(Keyword, Prefix, QT);
+    QT = Ctx.getQualifiedType(QT, PrefixQualifiers);
+  }
+  return QT;
+}
+
+std::string getFullyQualifiedName(QualType QT,
+                                  const ASTContext &Ctx) {
+  PrintingPolicy Policy(Ctx.getPrintingPolicy());
+  Policy.SuppressScope = false;
+  Policy.AnonymousTagLocations = false;
+  Policy.PolishForDeclaration = true;
+  Policy.SuppressUnwrittenScope = true;
+  QualType FQQT = getFullyQualifiedType(QT, Ctx);
+  return FQQT.getAsString(Policy);
+}
+
+}  // end namespace TypeName
+}  // end namespace clang
index 33b2046ae928888cf8cd866752dc7b9e859842aa..3035fdcc015155270b935a9139bbabded16a5cc6 100644 (file)
@@ -17,6 +17,7 @@ add_clang_unittest(ToolingTests
   RewriterTest.cpp
   RefactoringCallbacksTest.cpp
   ReplacementsYamlTest.cpp
+  QualTypeNamesTest.cpp
   )
 
 target_link_libraries(ToolingTests
diff --git a/unittests/Tooling/QualTypeNamesTest.cpp b/unittests/Tooling/QualTypeNamesTest.cpp
new file mode 100644 (file)
index 0000000..889c525
--- /dev/null
@@ -0,0 +1,166 @@
+//===- unittest/Tooling/QualTypeNameTest.cpp ------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Core/QualTypeNames.h"
+#include "TestVisitor.h"
+using namespace clang;
+
+namespace {
+struct TypeNameVisitor : TestVisitor<TypeNameVisitor> {
+  llvm::StringMap<std::string> ExpectedQualTypeNames;
+
+  // ValueDecls are the least-derived decl with both a qualtype and a
+  // name.
+  bool traverseDecl(Decl *D) {
+    return true;  // Always continue
+  }
+
+  bool VisitValueDecl(const ValueDecl *VD) {
+    std::string ExpectedName =
+        ExpectedQualTypeNames.lookup(VD->getNameAsString());
+    if (ExpectedName != "") {
+      std::string ActualName =
+          TypeName::getFullyQualifiedName(VD->getType(), *Context);
+      if (ExpectedName != ActualName) {
+        // A custom message makes it much easier to see what declaration
+        // failed compared to EXPECT_EQ.
+        EXPECT_TRUE(false) << "Typename::getFullyQualifiedName failed for "
+                           << VD->getQualifiedNameAsString() << std::endl
+                           << "   Actual: " << ActualName << std::endl
+                           << " Exepcted: " << ExpectedName;
+      }
+    }
+    return true;
+  }
+};
+
+// named namespaces inside anonymous namespaces
+
+TEST(QualTypeNameTest, getFullyQualifiedName) {
+  TypeNameVisitor Visitor;
+  // Simple case to test the test framework itself.
+  Visitor.ExpectedQualTypeNames["CheckInt"] = "int";
+
+  // Keeping the names of the variables whose types we check unique
+  // within the entire test--regardless of their own scope--makes it
+  // easier to diagnose test failures.
+
+  // Simple namespace qualifier
+  Visitor.ExpectedQualTypeNames["CheckA"] = "A::B::Class0";
+  // Lookup up the enclosing scopes, then down another one. (These
+  // appear as elaborated type in the AST. In that case--even if
+  // policy.SuppressScope = 0--qual_type.getAsString(policy) only
+  // gives the name as it appears in the source, not the full name.
+  Visitor.ExpectedQualTypeNames["CheckB"] = "A::B::C::Class1";
+  // Template parameter expansion.
+  Visitor.ExpectedQualTypeNames["CheckC"] =
+      "A::B::Template0<A::B::C::MyInt, A::B::AnotherClass>";
+  // Recursive template parameter expansion.
+  Visitor.ExpectedQualTypeNames["CheckD"] =
+      "A::B::Template0<A::B::Template1<A::B::C::MyInt, A::B::AnotherClass>, "
+      "A::B::Template0<int, long> >";
+  // Variadic Template expansion.
+  Visitor.ExpectedQualTypeNames["CheckE"] =
+      "A::Variadic<int, A::B::Template0<int, char>, "
+      "A::B::Template1<int, long>, A::B::C::MyInt>";
+  // Using declarations should be fully expanded.
+  Visitor.ExpectedQualTypeNames["CheckF"] = "A::B::Class0";
+  // Elements found within "using namespace foo;" should be fully
+  // expanded.
+  Visitor.ExpectedQualTypeNames["CheckG"] = "A::B::C::MyInt";
+  // Type inside function
+  Visitor.ExpectedQualTypeNames["CheckH"] = "struct X";
+  // Anonymous Namespaces
+  Visitor.ExpectedQualTypeNames["CheckI"] = "aClass";
+  // Keyword inclusion with namespaces
+  Visitor.ExpectedQualTypeNames["CheckJ"] = "struct A::aStruct";
+  // Anonymous Namespaces nested in named namespaces and vice-versa.
+  Visitor.ExpectedQualTypeNames["CheckK"] = "D::aStruct";
+  // Namespace alias
+  Visitor.ExpectedQualTypeNames["CheckL"] = "A::B::C::MyInt";
+  Visitor.ExpectedQualTypeNames["non_dependent_type_var"] =
+      "template Foo<X>::non_dependent_type";
+  Visitor.runOver(
+      "int CheckInt;\n"
+      "namespace A {\n"
+      " namespace B {\n"
+      "   class Class0 { };\n"
+      "   namespace C {\n"
+      "     typedef int MyInt;"
+      "   }\n"
+      "   template<class X, class Y> class Template0;"
+      "   template<class X, class Y> class Template1;"
+      "   typedef B::Class0 AnotherClass;\n"
+      "   void Function1(Template0<C::MyInt,\n"
+      "                  AnotherClass> CheckC);\n"
+      "   void Function2(Template0<Template1<C::MyInt, AnotherClass>,\n"
+      "                            Template0<int, long> > CheckD);\n"
+      "  }\n"
+      "template<typename... Values> class Variadic {};\n"
+      "Variadic<int, B::Template0<int, char>, "
+      "         B::Template1<int, long>, "
+      "         B::C::MyInt > CheckE;\n"
+      " namespace BC = B::C;\n"
+      " BC::MyInt CheckL;\n"
+      "}\n"
+      "using A::B::Class0;\n"
+      "void Function(Class0 CheckF);\n"
+      "using namespace A::B::C;\n"
+      "void Function(MyInt CheckG);\n"
+      "void f() {\n"
+      "  struct X {} CheckH;\n"
+      "}\n"
+      "namespace {\n"
+      "  class aClass {};\n"
+      "   aClass CheckI;\n"
+      "}\n"
+      "namespace A {\n"
+      "  struct aStruct {} CheckJ;\n"
+      "}\n"
+      "namespace {\n"
+      "  namespace D {\n"
+      "    namespace {\n"
+      "      class aStruct {};\n"
+      "      aStruct CheckK;\n"
+      "    }\n"
+      "  }\n"
+      "}\n"
+      "template<class T> struct Foo {\n"
+      "  typedef typename T::A dependent_type;\n"
+      "  typedef int non_dependent_type;\n"
+      "  dependent_type dependent_type_var;\n"
+      "  non_dependent_type non_dependent_type_var;\n"
+      "};\n"
+      "struct X { typedef int A; };"
+      "Foo<X> var;"
+      "void F() {\n"
+      "  var.dependent_type_var = 0;\n"
+      "var.non_dependent_type_var = 0;\n"
+      "}\n"
+);
+
+  TypeNameVisitor Complex;
+  Complex.ExpectedQualTypeNames["CheckTX"] = "B::TX";
+  Complex.runOver(
+      "namespace A {"
+      "  struct X {};"
+      "}"
+      "using A::X;"
+      "namespace fake_std {"
+      "  template<class... Types > class tuple {};"
+      "}"
+      "namespace B {"
+      "  using fake_std::tuple;"
+      "  typedef tuple<X> TX;"
+      "  TX CheckTX;"
+      "  struct A { typedef int X; };"
+      "}");
+}
+
+}  // end anonymous namespace