From: Richard Smith Date: Tue, 9 Feb 2016 21:04:04 +0000 (+0000) Subject: Add Tooling functionality to get a name for a QualType that can be used to name X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=adee5efbad8218f61f41d06e1aa315e34da95bc9;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260278 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Tooling/Core/QualTypeNames.h b/include/clang/Tooling/Core/QualTypeNames.h new file mode 100644 index 0000000000..49624a331f --- /dev/null +++ b/include/clang/Tooling/Core/QualTypeNames.h @@ -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 TX; +// TX t; +// } +// +// B::t's type is reported as "B::TX", rather than std::tuple. +// +// Also, this code replaces types found via using declarations with +// their more qualified name, so for the code: +// +// using std::tuple; +// tuple TInt; +// +// TInt's type will be named, "std::tuple". +// +// 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 diff --git a/lib/Tooling/Core/CMakeLists.txt b/lib/Tooling/Core/CMakeLists.txt index b88e1f8333..f6348cbf80 100644 --- a/lib/Tooling/Core/CMakeLists.txt +++ b/lib/Tooling/Core/CMakeLists.txt @@ -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 index 0000000000..6c3ff145cc --- /dev/null +++ b/lib/Tooling/Core/QualTypeNames.cpp @@ -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 +#include + +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(TypePtr)); + // In case of template specializations, iterate over the arguments + // and fully qualify them as well. + if (const auto *TST = dyn_cast(TypePtr)) { + bool MightHaveChanged = false; + SmallVector 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(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(TSTRecord->getDecl())) { + const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs(); + + bool MightHaveChanged = false; + SmallVector 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(DC)) { + while (NS && NS->isInline()) { + // Ignore inline namespace; + NS = dyn_cast(NS->getDeclContext()); + } + if (NS->getDeclName()) return createNestedNameSpecifier(Ctx, NS); + return nullptr; // no starting '::', no anonymous + } else if (const auto *TD = dyn_cast(DC)) { + return createNestedNameSpecifier(Ctx, TD, FullyQualify); + } else if (const auto *TDD = dyn_cast(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()) { + TD = TagDeclType->getDecl(); + } else { + TD = Type->getAsCXXRecordDecl(); + } + if (TD) { + return TypeName::createNestedNameSpecifier(Ctx, TD, + true /*FullyQualified*/); + } else if (const auto *TDD = dyn_cast(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(DC); + const auto *OuterNS = dyn_cast_or_null(DC); + if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) { + if (const auto *CxxDecl = dyn_cast(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(Decl); + OuterNS = dyn_cast(Decl); + } + } + } + + if (OuterNS) { + return createNestedNameSpecifier(Ctx, OuterNS); + } else if (const auto *TD = dyn_cast(Outer)) { + return createNestedNameSpecifier(Ctx, TD, FullyQualified); + } else if (dyn_cast(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(TypePtr)) { + Decl = TDT->getDecl(); + } else if (const auto *TagDeclType = dyn_cast(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(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(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(QT.getTypePtr())) { + // Get the qualifiers. + bool IsLValueRefTy = isa(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(QT.getTypePtr())) { + // Get the qualifiers. + Qualifiers Quals = QT.getQualifiers(); + + QT = dyn_cast(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(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(QT.getTypePtr()) || + isa(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 diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt index 33b2046ae9..3035fdcc01 100644 --- a/unittests/Tooling/CMakeLists.txt +++ b/unittests/Tooling/CMakeLists.txt @@ -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 index 0000000000..889c525231 --- /dev/null +++ b/unittests/Tooling/QualTypeNamesTest.cpp @@ -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 { + llvm::StringMap 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"; + // Recursive template parameter expansion. + Visitor.ExpectedQualTypeNames["CheckD"] = + "A::B::Template0, " + "A::B::Template0 >"; + // Variadic Template expansion. + Visitor.ExpectedQualTypeNames["CheckE"] = + "A::Variadic, " + "A::B::Template1, 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::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 Template0;" + " template class Template1;" + " typedef B::Class0 AnotherClass;\n" + " void Function1(Template0 CheckC);\n" + " void Function2(Template0,\n" + " Template0 > CheckD);\n" + " }\n" + "template class Variadic {};\n" + "Variadic, " + " B::Template1, " + " 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 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 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 tuple {};" + "}" + "namespace B {" + " using fake_std::tuple;" + " typedef tuple TX;" + " TX CheckTX;" + " struct A { typedef int X; };" + "}"); +} + +} // end anonymous namespace