r260278 - Add Tooling functionality to get a name for a QualType that can be used to name

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 9 13:04:04 PST 2016


Author: rsmith
Date: Tue Feb  9 15:04:04 2016
New Revision: 260278

URL: http://llvm.org/viewvc/llvm-project?rev=260278&view=rev
Log:
Add Tooling functionality to get a name for a QualType that can be used to name
that type from the global scope.

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

Added:
    cfe/trunk/include/clang/Tooling/Core/QualTypeNames.h
    cfe/trunk/lib/Tooling/Core/QualTypeNames.cpp
    cfe/trunk/unittests/Tooling/QualTypeNamesTest.cpp
Modified:
    cfe/trunk/lib/Tooling/Core/CMakeLists.txt
    cfe/trunk/unittests/Tooling/CMakeLists.txt

Added: cfe/trunk/include/clang/Tooling/Core/QualTypeNames.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Core/QualTypeNames.h?rev=260278&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Core/QualTypeNames.h (added)
+++ cfe/trunk/include/clang/Tooling/Core/QualTypeNames.h Tue Feb  9 15:04:04 2016
@@ -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

Modified: cfe/trunk/lib/Tooling/Core/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Core/CMakeLists.txt?rev=260278&r1=260277&r2=260278&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Core/CMakeLists.txt (original)
+++ cfe/trunk/lib/Tooling/Core/CMakeLists.txt Tue Feb  9 15:04:04 2016
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support)
 add_clang_library(clangToolingCore
   Lookup.cpp
   Replacement.cpp
+  QualTypeNames.cpp
 
   LINK_LIBS
   clangAST

Added: cfe/trunk/lib/Tooling/Core/QualTypeNames.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Core/QualTypeNames.cpp?rev=260278&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Core/QualTypeNames.cpp (added)
+++ cfe/trunk/lib/Tooling/Core/QualTypeNames.cpp Tue Feb  9 15:04:04 2016
@@ -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

Modified: cfe/trunk/unittests/Tooling/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/CMakeLists.txt?rev=260278&r1=260277&r2=260278&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/CMakeLists.txt (original)
+++ cfe/trunk/unittests/Tooling/CMakeLists.txt Tue Feb  9 15:04:04 2016
@@ -17,6 +17,7 @@ add_clang_unittest(ToolingTests
   RewriterTest.cpp
   RefactoringCallbacksTest.cpp
   ReplacementsYamlTest.cpp
+  QualTypeNamesTest.cpp
   )
 
 target_link_libraries(ToolingTests

Added: cfe/trunk/unittests/Tooling/QualTypeNamesTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/QualTypeNamesTest.cpp?rev=260278&view=auto
==============================================================================
--- cfe/trunk/unittests/Tooling/QualTypeNamesTest.cpp (added)
+++ cfe/trunk/unittests/Tooling/QualTypeNamesTest.cpp Tue Feb  9 15:04:04 2016
@@ -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




More information about the cfe-commits mailing list