Index: include/clang/AST/NestedNameSpecifier.h =================================================================== --- include/clang/AST/NestedNameSpecifier.h +++ include/clang/AST/NestedNameSpecifier.h @@ -143,6 +143,13 @@ static NestedNameSpecifier *Create(const ASTContext &Context, IdentifierInfo *II); + /// \brief Builds a specifier with the global specifier ("::") prepended. + /// If the input specifier already has the global specifier, then the input + /// specifier itself is returned. + static NestedNameSpecifier *PrependGlobalSpecifier( + const ASTContext &Context, const NestedNameSpecifier *Specifier); + + /// \brief Returns the nested name specifier representing the global /// scope. static NestedNameSpecifier *GlobalSpecifier(const ASTContext &Context); Index: include/clang/Tooling/Core/QualTypeNames.h =================================================================== --- include/clang/Tooling/Core/QualTypeNames.h +++ include/clang/Tooling/Core/QualTypeNames.h @@ -69,8 +69,11 @@ /// \param[in] QT - the type for which the fully qualified name will be /// returned. /// \param[in] Ctx - the ASTContext to be used. +/// \param[in] WithGlobalNsPrefix - If true, then the global namespace +/// specifier "::" will be prepended to the fully qualified name. std::string getFullyQualifiedName(QualType QT, - const ASTContext &Ctx); + const ASTContext &Ctx, + bool WithGlobalNsPrefix = false); } // end namespace TypeName } // end namespace clang #endif // LLVM_CLANG_TOOLING_CORE_QUALTYPENAMES_H Index: lib/AST/NestedNameSpecifier.cpp =================================================================== --- lib/AST/NestedNameSpecifier.cpp +++ lib/AST/NestedNameSpecifier.cpp @@ -43,6 +43,45 @@ } NestedNameSpecifier * +NestedNameSpecifier::PrependGlobalSpecifier( + const ASTContext &Context, const NestedNameSpecifier *Specifier) { + assert(Specifier && "Input nested name specifier cannot be NULL"); + + NestedNameSpecifier *P = const_cast(Specifier); + while (P->getPrefix() != nullptr) { + P = P->getPrefix(); + } + + SpecifierKind Kind = P->getKind(); + if (Kind == Global) { + return const_cast(Specifier); + } + + P->Prefix.setPointer(GlobalSpecifier(Context)); + switch (Kind) { + case Namespace: + case NamespaceAlias: + P->Prefix.setInt(StoredDecl); + break; + case Identifier: + P->Prefix.setInt(StoredIdentifier); + break; + case TypeSpec: + case Super: + P->Prefix.setInt(StoredTypeSpec); + break; + case TypeSpecWithTemplate: + P->Prefix.setInt(StoredTypeSpecWithTemplate); + break; + default: + // Global is handled as a special case above. + assert(false && "Invalid NNS Kind!"); + } + + return FindOrInsert(Context, *Specifier); +} + +NestedNameSpecifier * NestedNameSpecifier::Create(const ASTContext &Context, NestedNameSpecifier *Prefix, IdentifierInfo *II) { assert(II && "Identifier cannot be NULL"); Index: lib/Tooling/Core/QualTypeNames.cpp =================================================================== --- lib/Tooling/Core/QualTypeNames.cpp +++ lib/Tooling/Core/QualTypeNames.cpp @@ -30,7 +30,8 @@ /// \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); +static QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx, + bool WithGlobalNsPrefix); /// \brief Create a NestedNameSpecifier for Namesp and its enclosing /// scopes. @@ -90,7 +91,8 @@ } static bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx, - TemplateArgument &Arg) { + TemplateArgument &Arg, + bool WithGlobalNsPrefix) { bool Changed = false; // Note: we do not handle TemplateArgument::Expression, to replace it @@ -105,7 +107,7 @@ } else if (Arg.getKind() == TemplateArgument::Type) { QualType SubTy = Arg.getAsType(); // Check if the type needs more desugaring and recurse. - QualType QTFQ = getFullyQualifiedType(SubTy, Ctx); + QualType QTFQ = getFullyQualifiedType(SubTy, Ctx, WithGlobalNsPrefix); if (QTFQ != SubTy) { Arg = TemplateArgument(QTFQ); Changed = true; @@ -115,7 +117,8 @@ } static const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx, - const Type *TypePtr) { + const Type *TypePtr, + bool WithGlobalNsPrefix) { // 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. @@ -130,7 +133,8 @@ // Cheap to copy and potentially modified by // getFullyQualifedTemplateArgument. TemplateArgument Arg(*I); - MightHaveChanged |= getFullyQualifiedTemplateArgument(Ctx, Arg); + MightHaveChanged |= getFullyQualifiedTemplateArgument( + Ctx, Arg, WithGlobalNsPrefix); FQArgs.push_back(Arg); } @@ -160,7 +164,8 @@ // cheap to copy and potentially modified by // getFullyQualifedTemplateArgument TemplateArgument Arg(TemplateArgs[I]); - MightHaveChanged |= getFullyQualifiedTemplateArgument(Ctx, Arg); + MightHaveChanged |= getFullyQualifiedTemplateArgument( + Ctx, Arg, WithGlobalNsPrefix); FQArgs.push_back(Arg); } @@ -337,13 +342,14 @@ /// \brief Return the fully qualified type, including fully-qualified /// versions of any template parameters. -QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx) { +QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx, + bool WithGlobalNsPrefix) { // 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 = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix); QT = Ctx.getPointerType(QT); // Add back the qualifiers. QT = Ctx.getQualifiedType(QT, Quals); @@ -356,7 +362,7 @@ // Get the qualifiers. bool IsLValueRefTy = isa(QT.getTypePtr()); Qualifiers Quals = QT.getQualifiers(); - QT = getFullyQualifiedType(QT->getPointeeType(), Ctx); + QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix); // Add the r- or l-value reference type back to the fully // qualified one. if (IsLValueRefTy) @@ -398,6 +404,13 @@ // is not the global scope. Prefix = createNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(), true /*FullyQualified*/); + if (WithGlobalNsPrefix && !QT->isBuiltinType()) { + if (Prefix == nullptr) { + Prefix = NestedNameSpecifier::GlobalSpecifier(Ctx); + } else { + Prefix = NestedNameSpecifier::PrependGlobalSpecifier(Ctx, Prefix); + } + } // In case of template specializations iterate over the arguments and // fully qualify them as well. @@ -407,7 +420,8 @@ // may point to a template specialization) or Template // Specialization Type. We need to fully qualify their arguments. - const Type *TypePtr = getFullyQualifiedTemplateType(Ctx, QT.getTypePtr()); + const Type *TypePtr = getFullyQualifiedTemplateType( + Ctx, QT.getTypePtr(), WithGlobalNsPrefix); QT = QualType(TypePtr, 0); } if (Prefix || Keyword != ETK_None) { @@ -418,13 +432,14 @@ } std::string getFullyQualifiedName(QualType QT, - const ASTContext &Ctx) { + const ASTContext &Ctx, + bool WithGlobalNsPrefix) { PrintingPolicy Policy(Ctx.getPrintingPolicy()); Policy.SuppressScope = false; Policy.AnonymousTagLocations = false; Policy.PolishForDeclaration = true; Policy.SuppressUnwrittenScope = true; - QualType FQQT = getFullyQualifiedType(QT, Ctx); + QualType FQQT = getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix); return FQQT.getAsString(Policy); } Index: unittests/Tooling/QualTypeNamesTest.cpp =================================================================== --- unittests/Tooling/QualTypeNamesTest.cpp +++ unittests/Tooling/QualTypeNamesTest.cpp @@ -14,6 +14,7 @@ namespace { struct TypeNameVisitor : TestVisitor { llvm::StringMap ExpectedQualTypeNames; + bool WithGlobalNsPrefix = false; // ValueDecls are the least-derived decl with both a qualtype and a // name. @@ -26,7 +27,8 @@ ExpectedQualTypeNames.lookup(VD->getNameAsString()); if (ExpectedName != "") { std::string ActualName = - TypeName::getFullyQualifiedName(VD->getType(), *Context); + TypeName::getFullyQualifiedName(VD->getType(), *Context, + WithGlobalNsPrefix); if (ExpectedName != ActualName) { // A custom message makes it much easier to see what declaration // failed compared to EXPECT_EQ. @@ -179,6 +181,27 @@ " TX CheckTX;" " struct A { typedef int X; };" "}"); + + TypeNameVisitor GlobalNsPrefix; + GlobalNsPrefix.WithGlobalNsPrefix = true; + GlobalNsPrefix.ExpectedQualTypeNames["IntVal"] = "int"; + GlobalNsPrefix.ExpectedQualTypeNames["BoolVal"] = "bool"; + GlobalNsPrefix.ExpectedQualTypeNames["XVal"] = "::A::B::X"; + GlobalNsPrefix.ExpectedQualTypeNames["IntAliasVal"] = "::A::B::Alias"; + GlobalNsPrefix.runOver( + "namespace A {" + " namespace B {" + " int IntVal;" + " bool BoolVal;" + " struct X {};" + " X XVal;" + " template class CCC { };" + " template \n" + " using Alias = CCC;\n" + " Alias IntAliasVal;\n" + " }" + "}" + ); } } // end anonymous namespace