Index: include/clang/AST/PrettyPrinter.h =================================================================== --- include/clang/AST/PrettyPrinter.h +++ include/clang/AST/PrettyPrinter.h @@ -30,17 +30,68 @@ virtual bool handledStmt(Stmt* E, raw_ostream& OS) = 0; }; +// namespace with enum (instead of enum class) to avoid +// 'PrintingPolicy::Scope' is too small to hold all values of +// 'ScopePrintingKind' warnings. +namespace ScopePrintingKind { + enum ScopePrintingKind { + /// \brief Print all nested name specifiers (including the global scope + /// specifier). This is necessary if a printed non-absolute scope would not + /// select the desired scope. + /// + /// Example: Consider the following code: + /// \code + /// namespace Z { + /// namespace Z { + /// namespace Y { + /// class X { }; // (1) + /// } + /// } + /// namespace Y { + /// class X { }; // (2) + /// } + /// // (3) + /// } + /// \endcode + /// Printing type ::Z::Y::X (marked with (2)) without FullScope results in + /// "Z::Y::X". If this is used at the position marked with (3), it + /// will select the wrong type ::Z::Z::Y::X (marked with (1)). With FullScope + /// the result is "::Z::Y::X" and the correct type is selected. + /// + /// Please note that in some cases it is not possible to print the full scope. + /// For example in case of a local class or a dependent name. + FullScope, + + /// \brief In case of an elaborated type print the outer scope as written in + /// the source. (If there is a tag keyword and no scope in the source then no + /// scope is printed.) + /// Otherwise print the full scope but without the global scope specifier. + /// + /// This distinction is made for inner scopes recursively. + DefaultScope, + + /// \brief Do not print any scope. + SuppressScope + }; +} + /// \brief Describes how types, statements, expressions, and /// declarations should be printed. /// /// This type is intended to be small and suitable for passing by value. /// It is very frequently copied. struct PrintingPolicy { + friend class TypePrinter; + friend class NestedNameSpecifier; + friend class TemplateName; + friend class ElaboratedTypePolicyRAII; + friend class NamedDecl; + /// \brief Create a default printing policy for the specified language. PrintingPolicy(const LangOptions &LO) : Indentation(2), SuppressSpecifiers(false), SuppressTagKeyword(LO.CPlusPlus), - IncludeTagDefinition(false), SuppressScope(false), + IncludeTagDefinition(false), Scope(ScopePrintingKind::DefaultScope), SuppressUnwrittenScope(false), SuppressInitializers(false), ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false), @@ -50,7 +101,8 @@ UseVoidForZeroParams(!LO.CPlusPlus), TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), - IncludeNewlines(true), MSVCFormatting(false) { } + IncludeNewlines(true), MSVCFormatting(false), + TemporarySuppressScope(false){ } /// \brief Adjust this printing policy for cases where it's known that /// we're printing C++ code (for instance, if AST dumping reaches a @@ -101,13 +153,15 @@ /// \endcode bool IncludeTagDefinition : 1; - /// \brief Suppresses printing of scope specifiers. - bool SuppressScope : 1; + /// \brief Specifies whether the scope should be printed, and if so, how + /// detailed. + ScopePrintingKind::ScopePrintingKind Scope : 2; + /// \brief Suppress printing parts of scope specifiers that don't need /// to be written, e.g., for inline or anonymous namespaces. bool SuppressUnwrittenScope : 1; - + /// \brief Suppress printing of variable initializers. /// /// This flag is used when printing the loop variable in a for-range @@ -138,16 +192,16 @@ /// char a[9] = "A string"; /// \endcode bool ConstantArraySizeAsWritten : 1; - + /// \brief When printing an anonymous tag name, also print the location of - /// that entity (e.g., "enum "). Otherwise, just + /// that entity (e.g., "enum "). Otherwise, just /// prints "(anonymous)" for the name. bool AnonymousTagLocations : 1; - + /// \brief When true, suppress printing of the __strong lifetime qualifier in /// ARC. unsigned SuppressStrongLifetime : 1; - + /// \brief When true, suppress printing of lifetime qualifier in /// ARC. unsigned SuppressLifetimeQualifiers : 1; @@ -179,7 +233,7 @@ /// declarations inside namespaces etc. Effectively, this should print /// only the requested declaration. unsigned TerseOutput : 1; - + /// \brief When true, do certain refinement needed for producing proper /// declaration tag; such as, do not print attributes attached to the declaration. /// @@ -200,6 +254,13 @@ /// prints anonymous namespaces as `anonymous namespace' and does not insert /// spaces after template arguments. bool MSVCFormatting : 1; + +private: + /// \brief When true, suppress printing of current outer scope. For example + /// with TemporarySuppressScope set to true "::A::B::C" ist + /// printed as "C". This is currently only supported in some cases + /// and is currently only used internally. + bool TemporarySuppressScope : 1; }; } // end namespace clang Index: include/clang/AST/TemplateName.h =================================================================== --- include/clang/AST/TemplateName.h +++ include/clang/AST/TemplateName.h @@ -277,12 +277,7 @@ /// /// \param OS the output stream to which the template name will be /// printed. - /// - /// \param SuppressNNS if true, don't print the - /// nested-name-specifier that precedes the template name (if it has - /// one). - void print(raw_ostream &OS, const PrintingPolicy &Policy, - bool SuppressNNS = false) const; + void print(raw_ostream &OS, const PrintingPolicy &Policy) const; /// \brief Debugging aid that dumps the template name. void dump(raw_ostream &OS) const; Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1427,10 +1427,17 @@ typedef SmallVector ContextsTy; ContextsTy Contexts; - // Collect contexts. - while (Ctx && isa(Ctx)) { - Contexts.push_back(Ctx); - Ctx = Ctx->getParent(); + if(P.Scope != ScopePrintingKind::SuppressScope && !P.TemporarySuppressScope) { + // Collect contexts. + while (Ctx && isa(Ctx)) { + Contexts.push_back(Ctx); + Ctx = Ctx->getParent(); + } + + if (P.Scope == ScopePrintingKind::FullScope) { + // Add global scope specifier up front + OS << "::"; + } } for (const DeclContext *DC : reverse(Contexts)) { Index: lib/AST/NestedNameSpecifier.cpp =================================================================== --- lib/AST/NestedNameSpecifier.cpp +++ lib/AST/NestedNameSpecifier.cpp @@ -261,8 +261,28 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy) const { + if(Policy.Scope == ScopePrintingKind::SuppressScope + || Policy.TemporarySuppressScope) { + return; + } + + // Nested name specifiers describe the scope as written in the source code. Thus + // these specifier may not contain the full scope if parts of the scope were + // not written in the source code. If IncludeUnwrittenScopes is set to true + // the parts of the scope that were not written in the source code are printed. + bool IncludeUnwrittenScopes = false; + if (getPrefix()) getPrefix()->print(OS, Policy); + else if(Policy.Scope == ScopePrintingKind::FullScope + && getKind() != NestedNameSpecifier::Global) { + // If this is the first written nested name specifier and it is not the global + // scope specifier then maybe not all scopes have been written in the source + // code ("maybe" because sometimes is not possible to write an absolute scope + // e.g. dependent names or local classes). Try to include the unwritten + // scopes if the printing of all scopes is requested: + IncludeUnwrittenScopes = true; + } switch (getKind()) { case Identifier: @@ -270,13 +290,23 @@ break; case Namespace: + if(IncludeUnwrittenScopes) { + getAsNamespace()->printQualifiedName(OS, Policy); + break; + } + if (getAsNamespace()->isAnonymousNamespace()) return; - + OS << getAsNamespace()->getName(); break; case NamespaceAlias: + if(IncludeUnwrittenScopes) { + getAsNamespaceAlias()->getNamespace()->printQualifiedName(OS, Policy); + break; + } + OS << getAsNamespaceAlias()->getName(); break; @@ -294,8 +324,10 @@ case TypeSpec: { const Type *T = getAsType(); - PrintingPolicy InnerPolicy(Policy); - InnerPolicy.SuppressScope = true; + if(IncludeUnwrittenScopes) { + QualType(T, 0).print(OS, Policy); + break; + } // Nested-name-specifiers are intended to contain minimally-qualified // types. An actual ElaboratedType will not occur, since we'll store @@ -304,21 +336,9 @@ // dependent template-id types (e.g., Outer::template Inner), // the type requires its own nested-name-specifier for uniqueness, so we // suppress that nested-name-specifier during printing. - assert(!isa(T) && - "Elaborated type in nested-name-specifier"); - if (const TemplateSpecializationType *SpecType - = dyn_cast(T)) { - // Print the template name without its corresponding - // nested-name-specifier. - SpecType->getTemplateName().print(OS, InnerPolicy, true); - - // Print the template argument list. - TemplateSpecializationType::PrintTemplateArgumentList( - OS, SpecType->template_arguments(), InnerPolicy); - } else { - // Print the type normally - QualType(T, 0).print(OS, InnerPolicy); - } + PrintingPolicy InnerPolicy(Policy); + InnerPolicy.TemporarySuppressScope = true; + QualType(T, 0).print(OS, InnerPolicy); break; } } Index: lib/AST/TemplateName.cpp =================================================================== --- lib/AST/TemplateName.cpp +++ lib/AST/TemplateName.cpp @@ -175,18 +175,22 @@ } void -TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, - bool SuppressNNS) const { - if (TemplateDecl *Template = Storage.dyn_cast()) - OS << *Template; - else if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) { - if (!SuppressNNS) +TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy) const { + if (TemplateDecl *Template = Storage.dyn_cast()) { + if (Policy.Scope == ScopePrintingKind::FullScope && + !Policy.TemporarySuppressScope) { + Template->printQualifiedName(OS, Policy); + } else { + OS << *Template; + } + } else if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) { + if (!Policy.TemporarySuppressScope) QTN->getQualifier()->print(OS, Policy); if (QTN->hasTemplateKeyword()) OS << "template "; OS << *QTN->getDecl(); } else if (DependentTemplateName *DTN = getAsDependentTemplateName()) { - if (!SuppressNNS && DTN->getQualifier()) + if (!Policy.TemporarySuppressScope && DTN->getQualifier()) DTN->getQualifier()->print(OS, Policy); OS << "template "; @@ -196,7 +200,7 @@ OS << "operator " << getOperatorSpelling(DTN->getOperator()); } else if (SubstTemplateTemplateParmStorage *subst = getAsSubstTemplateTemplateParm()) { - subst->getReplacement().print(OS, Policy, SuppressNNS); + subst->getReplacement().print(OS, Policy); } else if (SubstTemplateTemplateParmPackStorage *SubstPack = getAsSubstTemplateTemplateParmPack()) OS << *SubstPack->getParameterPack(); Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ lib/AST/TypePrinter.cpp @@ -26,7 +26,7 @@ #include "llvm/Support/raw_ostream.h" using namespace clang; -namespace { +namespace clang { /// \brief RAII object that enables printing of the ARC __strong lifetime /// qualifier. class IncludeStrongLifetimeRAII { @@ -63,19 +63,19 @@ class ElaboratedTypePolicyRAII { PrintingPolicy &Policy; bool SuppressTagKeyword; - bool SuppressScope; public: - explicit ElaboratedTypePolicyRAII(PrintingPolicy &Policy) : Policy(Policy) { + explicit ElaboratedTypePolicyRAII(PrintingPolicy &Policy, + bool TemporarySuppressScope = true) + : Policy(Policy) { SuppressTagKeyword = Policy.SuppressTagKeyword; - SuppressScope = Policy.SuppressScope; Policy.SuppressTagKeyword = true; - Policy.SuppressScope = true; + Policy.TemporarySuppressScope = TemporarySuppressScope; } ~ElaboratedTypePolicyRAII() { Policy.SuppressTagKeyword = SuppressTagKeyword; - Policy.SuppressScope = SuppressScope; + Policy.TemporarySuppressScope = false; } }; @@ -799,13 +799,11 @@ } void TypePrinter::printTypeSpec(NamedDecl *D, raw_ostream &OS) { - - // Compute the full nested-name-specifier for this type. - // In C, this will always be empty except when the type - // being printed is anonymous within other Record. - if (!Policy.SuppressScope) + if(Policy.Scope != ScopePrintingKind::SuppressScope && + !Policy.TemporarySuppressScope) { + // Print the scope: AppendScope(D->getDeclContext(), OS); - + } IdentifierInfo *II = D->getIdentifier(); OS << II->getName(); spaceBeforePlaceHolder(OS); @@ -818,10 +816,10 @@ void TypePrinter::printUnresolvedUsingAfter(const UnresolvedUsingType *T, raw_ostream &OS) { } -void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) { +void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) { printTypeSpec(T->getDecl(), OS); } -void TypePrinter::printTypedefAfter(const TypedefType *T, raw_ostream &OS) { } +void TypePrinter::printTypedefAfter(const TypedefType *T, raw_ostream &OS) { } void TypePrinter::printTypeOfExprBefore(const TypeOfExprType *T, raw_ostream &OS) { @@ -940,7 +938,12 @@ } /// Appends the given scope to the end of a string. void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) { - if (DC->isTranslationUnit()) return; + if (DC->isTranslationUnit()) { + if(Policy.Scope == ScopePrintingKind::FullScope) { + OS << "::"; + } + return; + } if (DC->isFunctionOrMethod()) return; AppendScope(DC->getParent(), OS); @@ -992,7 +995,8 @@ // Compute the full nested-name-specifier for this type. // In C, this will always be empty except when the type // being printed is anonymous within other Record. - if (!Policy.SuppressScope) + if (Policy.Scope != ScopePrintingKind::SuppressScope && + !Policy.TemporarySuppressScope) AppendScope(D->getDeclContext(), OS); if (const IdentifierInfo *II = D->getIdentifier()) @@ -1031,6 +1035,8 @@ OS << (Policy.MSVCFormatting ? '\'' : ')'); } + Policy.TemporarySuppressScope = false; + // If this is a class template specialization, print the template // arguments. if (ClassTemplateSpecializationDecl *Spec @@ -1100,10 +1106,12 @@ void TypePrinter::printTemplateSpecializationBefore( const TemplateSpecializationType *T, - raw_ostream &OS) { + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); T->getTemplateName().print(OS, Policy); + Policy.TemporarySuppressScope = false; + TemplateSpecializationType::PrintTemplateArgumentList( OS, T->template_arguments(), Policy); spaceBeforePlaceHolder(OS); @@ -1121,18 +1129,36 @@ void TypePrinter::printElaboratedBefore(const ElaboratedType *T, raw_ostream &OS) { + bool ScopeHasBeenPrinted = false; // The tag definition will take care of these. - if (!Policy.IncludeTagDefinition) - { + if (!Policy.IncludeTagDefinition) { OS << TypeWithKeyword::getKeywordName(T->getKeyword()); if (T->getKeyword() != ETK_None) OS << " "; - NestedNameSpecifier* Qualifier = T->getQualifier(); - if (Qualifier) + NestedNameSpecifier *Qualifier = T->getQualifier(); + if (Qualifier) { Qualifier->print(OS, Policy); + ScopeHasBeenPrinted = true; + } + // If there are no nested name specifiers, the complete scope will be + // printed later (location depends on the sub-type). } - - ElaboratedTypePolicyRAII PolicyRAII(Policy); + + // Currently the following sub-types are known: + if(!isa(T->getNamedType()) && + !isa(T->getNamedType()) && + !isa(T->getNamedType()) && + !isa(T->getNamedType())) { + llvm::errs() << "Unknown elaborated sub-type: " + << T->getNamedType()->getTypeClassName() << "\n"; + llvm_unreachable("Unknown elaborated sub-type (see error output)"); + } + + // Suppress the outer nested name specifier of the underlying type in case + // of DefaultScope and if a scope has already been printed. + bool temporarySuppressScope = + Policy.Scope == ScopePrintingKind::DefaultScope || ScopeHasBeenPrinted; + ElaboratedTypePolicyRAII PolicyRAII(Policy, temporarySuppressScope); printBefore(T->getNamedType(), OS); } void TypePrinter::printElaboratedAfter(const ElaboratedType *T, @@ -1161,7 +1187,6 @@ OS << TypeWithKeyword::getKeywordName(T->getKeyword()); if (T->getKeyword() != ETK_None) OS << " "; - T->getQualifier()->print(OS, Policy); OS << T->getIdentifier()->getName(); @@ -1177,10 +1202,13 @@ OS << TypeWithKeyword::getKeywordName(T->getKeyword()); if (T->getKeyword() != ETK_None) OS << " "; - + if (T->getQualifier()) - T->getQualifier()->print(OS, Policy); + T->getQualifier()->print(OS, Policy); OS << T->getIdentifier()->getName(); + + Policy.TemporarySuppressScope = false; + TemplateSpecializationType::PrintTemplateArgumentList(OS, T->template_arguments(), Policy); Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -907,8 +907,7 @@ SmallString<128> NS; llvm::raw_svector_ostream OS(NS); - Ty->getTemplateName().print(OS, CGM.getContext().getPrintingPolicy(), - /*qualified*/ false); + Ty->getTemplateName().print(OS, CGM.getContext().getPrintingPolicy()); TemplateSpecializationType::PrintTemplateArgumentList( OS, Ty->template_arguments(), Index: lib/Tooling/Core/QualTypeNames.cpp =================================================================== --- lib/Tooling/Core/QualTypeNames.cpp +++ lib/Tooling/Core/QualTypeNames.cpp @@ -465,7 +465,7 @@ const ASTContext &Ctx, bool WithGlobalNsPrefix) { PrintingPolicy Policy(Ctx.getPrintingPolicy()); - Policy.SuppressScope = false; + Policy.Scope = ScopePrintingKind::DefaultScope; Policy.AnonymousTagLocations = false; Policy.PolishForDeclaration = true; Policy.SuppressUnwrittenScope = true; Index: test/CXX/class.access/p6.cpp =================================================================== --- test/CXX/class.access/p6.cpp +++ test/CXX/class.access/p6.cpp @@ -92,7 +92,7 @@ template class Outer::A { public: - static void foo(); // expected-note {{'Outer::A::foo' declared here}} + static void foo(); // expected-note {{'Outer::A::foo' declared here}} }; class B { @@ -102,7 +102,38 @@ void test() { Outer::A::foo(); - Outer::A::foo(); // expected-error {{no member named 'foo' in 'test3::Outer::A'; did you mean 'Outer::A::foo'?}} + Outer::A::foo(); // expected-error {{no member named 'foo' in 'test3::Outer::A'; did you mean 'Outer::A::foo'?}} + } +} + +// Modified version of test3 showing that inner scopes of a nested name specifier are necessary to create a valid suggestion. +namespace test3_1 { + + namespace Colors { + class Green { }; + class Blue { }; + } + + // We have to wrap this in a class because a partial specialization + // isn't actually in the context of the template. + struct Outer { + template class A { + }; + }; + + template class Outer::A { + public: + static void foo(); // expected-note {{'Outer::A::foo' declared here}} + }; + + class B { + private: typedef Colors::Green nature; + friend class Outer; + }; + + void test() { + Outer::A::foo(); + Outer::A::foo(); // expected-error {{no member named 'foo' in 'test3_1::Outer::A'; did you mean 'Outer::A::foo'?}} } } Index: unittests/AST/AbsoluteScopeTest.cpp =================================================================== --- /dev/null +++ unittests/AST/AbsoluteScopeTest.cpp @@ -0,0 +1,218 @@ +//===- unittests/AST/AbsoluteScopeTest.cpp - absolute scope printing test -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for ScopePrintingKind::FullScope. In each test, +// code is rewritten by changing the type of a variable declaration to +// another class or function type. Without printing the full scopes the +// resulting code would be ill-formed. +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +#include + +using namespace clang; +using namespace tooling; + +namespace { + +class DeclASTVisitor : public RecursiveASTVisitor, + public ASTConsumer { + StringRef VarDeclName; + StringRef DeclNameOfNewType; + unsigned int NumFoundVarDecls; + unsigned int NumFoundDeclsOfNewtype; + const VarDecl *FoundVarDecl; + QualType FoundNewType; + +public: + DeclASTVisitor(StringRef declName, StringRef namedDeclNameOfNewType) + : VarDeclName(declName), DeclNameOfNewType(namedDeclNameOfNewType), + NumFoundVarDecls(0), NumFoundDeclsOfNewtype(0) {} + + // Look for the variable declaration described by the given name: + bool VisitVarDecl(VarDecl *VD) { + if (VD->getNameAsString() == VarDeclName) { + NumFoundVarDecls++; + FoundVarDecl = VD; + } + return true; + } + + // Look for the declaration described by the given name and store the type. + bool VisitTypeDecl(TypeDecl *TD) { + if (TD->getNameAsString() == DeclNameOfNewType) { + NumFoundDeclsOfNewtype++; + FoundNewType = QualType(TD->getTypeForDecl(), 0); + } + return true; + } + // ... also accept value declarations (e.g. functions) because they have a + // type too + bool VisitValueDecl(ValueDecl *VD) { + if (VD->getNameAsString() == DeclNameOfNewType) { + NumFoundDeclsOfNewtype++; + FoundNewType = VD->getType(); + } + return true; + } + + virtual bool HandleTopLevelDecl(DeclGroupRef DR) { + for (DeclGroupRef::iterator i = DR.begin(), e = DR.end(); i != e; ++i) { + TraverseDecl(*i); + } + return true; + } + + unsigned int getNumFoundVarDecls() const { return NumFoundVarDecls; } + + unsigned int getNumFoundDeclsOfNewType() const { + return NumFoundDeclsOfNewtype; + } + + const VarDecl *getFoundVarDecl() const { return FoundVarDecl; } + + QualType getFoundNewType() const { return FoundNewType; } +}; + +::testing::AssertionResult ChangeTypeOfDeclaration(StringRef Code, + StringRef VarDeclName, + StringRef DeclNameOfNewType, + bool CPP11 = true) { + CompilerInstance compilerInstance; + compilerInstance.createDiagnostics(); + + LangOptions &lo = compilerInstance.getLangOpts(); + lo.CPlusPlus = 1; + if (CPP11) { + lo.CPlusPlus11 = 1; + } + + auto TO = std::make_shared(); + TO->Triple = llvm::sys::getDefaultTargetTriple(); + TargetInfo *TI = + TargetInfo::CreateTargetInfo(compilerInstance.getDiagnostics(), TO); + compilerInstance.setTarget(TI); + + compilerInstance.createFileManager(); + compilerInstance.createSourceManager(compilerInstance.getFileManager()); + SourceManager &sourceManager = compilerInstance.getSourceManager(); + compilerInstance.createPreprocessor(TU_Module); + compilerInstance.createASTContext(); + + // Set the main file handled by the source manager to the input code: + std::unique_ptr mb( + llvm::MemoryBuffer::getMemBufferCopy(Code, "input.cc")); + sourceManager.setMainFileID(sourceManager.createFileID(std::move(mb))); + compilerInstance.getDiagnosticClient().BeginSourceFile( + compilerInstance.getLangOpts(), &compilerInstance.getPreprocessor()); + + // Create the declaration visitor: + DeclASTVisitor declVisitor(VarDeclName, DeclNameOfNewType); + + // Parse the code: + ParseAST(compilerInstance.getPreprocessor(), &declVisitor, + compilerInstance.getASTContext()); + + // Evaluate the result of the AST traverse: + if (declVisitor.getNumFoundVarDecls() != 1) { + return testing::AssertionFailure() + << "Expected exactly one variable declaration with the name \"" + << VarDeclName << "\" but " << declVisitor.getNumFoundVarDecls() + << " were found"; + } + if (declVisitor.getNumFoundDeclsOfNewType() != 1) { + return testing::AssertionFailure() + << "Expected exactly one declaration with the name \"" + << DeclNameOfNewType << "\" but " + << declVisitor.getNumFoundDeclsOfNewType() << " were found"; + } + + // Found information on the basis of which the transformation will take place: + const VarDecl *foundVarDecl = declVisitor.getFoundVarDecl(); + QualType foundNewType = declVisitor.getFoundNewType(); + + Rewriter rewriter; + rewriter.setSourceMgr(sourceManager, compilerInstance.getLangOpts()); + + // Create declaration with the old name and the new type: + std::string newDeclText; + llvm::raw_string_ostream newDeclTextStream(newDeclText); + PrintingPolicy policy = foundVarDecl->getASTContext().getPrintingPolicy(); + policy.PolishForDeclaration = true; + // The important flag: + policy.Scope = ScopePrintingKind::FullScope; + foundNewType.print(newDeclTextStream, policy, VarDeclName); + + // Replace the old declaration placeholder with the new declaration: + rewriter.ReplaceText(foundVarDecl->getSourceRange(), newDeclTextStream.str()); + + const RewriteBuffer *rewriteBuffer = + rewriter.getRewriteBufferFor(sourceManager.getMainFileID()); + if (!rewriteBuffer) { + return testing::AssertionFailure() << "No changes have been made"; + } else { + // Check whether the transformed/rewritten code is valid: + std::unique_ptr Factory = + newFrontendActionFactory(); + std::vector args(1, "-std=c++11"); + std::string rewrittenCode = + std::string(rewriteBuffer->begin(), rewriteBuffer->end()); + if (!runToolOnCodeWithArgs(Factory->create(), rewrittenCode, args, + "input.cc")) { + return testing::AssertionFailure() + << "Parsing error in rewritten code \"" << rewrittenCode << "\""; + } else { + return testing::AssertionSuccess(); + } + } +} + +} // unnamed namespace + +TEST(AbsoluteScope, Test01) { + ASSERT_TRUE(ChangeTypeOfDeclaration("namespace Z {" + " namespace Z { }" + " namespace Y {" + " class X3 { };" + " }" + " int A;" + "}", + "A", "X3")); +} + +TEST(AbsoluteScope, Test02) { + ASSERT_TRUE(ChangeTypeOfDeclaration("namespace Z {" + " namespace Y {" + " class X3 { };" + " void F(X3) { }" + " }" + " namespace Z {" + " namespace Z { }" + " int A;" + " }" + "}", + "A", "F")); +} Index: unittests/AST/CMakeLists.txt =================================================================== --- unittests/AST/CMakeLists.txt +++ unittests/AST/CMakeLists.txt @@ -3,6 +3,7 @@ ) add_clang_unittest(ASTTests + AbsoluteScopeTest.cpp ASTContextParentMapTest.cpp ASTImporterTest.cpp ASTTypeTraitsTest.cpp @@ -17,6 +18,7 @@ PostOrderASTVisitor.cpp SourceLocationTest.cpp StmtPrinterTest.cpp + TypePrinterTest.cpp ) target_link_libraries(ASTTests Index: unittests/AST/NamedDeclPrinterTest.cpp =================================================================== --- unittests/AST/NamedDeclPrinterTest.cpp +++ unittests/AST/NamedDeclPrinterTest.cpp @@ -32,10 +32,12 @@ SmallString<1024> Printed; unsigned NumFoundDecls; bool SuppressUnwrittenScope; + bool MakeAbsolute; public: - explicit PrintMatch(bool suppressUnwrittenScope) - : NumFoundDecls(0), SuppressUnwrittenScope(suppressUnwrittenScope) {} + explicit PrintMatch(bool suppressUnwrittenScope, bool makeAbsolute) + : NumFoundDecls(0), SuppressUnwrittenScope(suppressUnwrittenScope), + MakeAbsolute(makeAbsolute){} void run(const MatchFinder::MatchResult &Result) override { const NamedDecl *ND = Result.Nodes.getNodeAs("id"); @@ -48,6 +50,8 @@ llvm::raw_svector_ostream Out(Printed); PrintingPolicy Policy = Result.Context->getPrintingPolicy(); Policy.SuppressUnwrittenScope = SuppressUnwrittenScope; + Policy.Scope = MakeAbsolute ? ScopePrintingKind::FullScope + : ScopePrintingKind::DefaultScope; ND->printQualifiedName(Out, Policy); } @@ -64,8 +68,9 @@ PrintedNamedDeclMatches(StringRef Code, const std::vector &Args, bool SuppressUnwrittenScope, const DeclarationMatcher &NodeMatch, - StringRef ExpectedPrinted, StringRef FileName) { - PrintMatch Printer(SuppressUnwrittenScope); + StringRef ExpectedPrinted, StringRef FileName, + bool MakeAbsolute) { + PrintMatch Printer(SuppressUnwrittenScope, MakeAbsolute); MatchFinder Finder; Finder.addMatcher(NodeMatch, &Printer); std::unique_ptr Factory = @@ -94,26 +99,30 @@ ::testing::AssertionResult PrintedNamedDeclCXX98Matches(StringRef Code, StringRef DeclName, - StringRef ExpectedPrinted) { + StringRef ExpectedPrinted, + bool MakeAbsolute = false) { std::vector Args(1, "-std=c++98"); return PrintedNamedDeclMatches(Code, Args, /*SuppressUnwrittenScope*/ false, namedDecl(hasName(DeclName)).bind("id"), ExpectedPrinted, - "input.cc"); + "input.cc", + MakeAbsolute); } ::testing::AssertionResult PrintedWrittenNamedDeclCXX11Matches(StringRef Code, StringRef DeclName, - StringRef ExpectedPrinted) { + StringRef ExpectedPrinted, + bool MakeAbsolute = false) { std::vector Args(1, "-std=c++11"); return PrintedNamedDeclMatches(Code, Args, /*SuppressUnwrittenScope*/ true, namedDecl(hasName(DeclName)).bind("id"), ExpectedPrinted, - "input.cc"); + "input.cc", + MakeAbsolute); } } // unnamed namespace @@ -125,6 +134,14 @@ "(anonymous namespace)::A")); } +TEST(NamedDeclPrinter, TestNamespace1Absolute) { + ASSERT_TRUE(PrintedNamedDeclCXX98Matches( + "namespace { int A; }", + "A", + "::(anonymous namespace)::A", + true)); +} + TEST(NamedDeclPrinter, TestNamespace2) { ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( "inline namespace Z { namespace { int A; } }", @@ -132,6 +149,14 @@ "A")); } +TEST(NamedDeclPrinter, TestNamespace2Absolute) { + ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( + "inline namespace Z { namespace { int A; } }", + "A", + "::A", + true)); +} + TEST(NamedDeclPrinter, TestUnscopedUnnamedEnum) { ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( "enum { A };", @@ -139,6 +164,14 @@ "A")); } +TEST(NamedDeclPrinter, TestUnscopedUnnamedEnumAbsolute) { + ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( + "enum { A };", + "A", + "::A", + true)); +} + TEST(NamedDeclPrinter, TestNamedEnum) { ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( "enum X { A };", @@ -146,6 +179,14 @@ "X::A")); } +TEST(NamedDeclPrinter, TestNamedEnumAbsolute) { + ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( + "enum X { A };", + "A", + "::X::A", + true)); +} + TEST(NamedDeclPrinter, TestScopedNamedEnum) { ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( "enum class X { A };", @@ -167,9 +208,33 @@ "X::Y::A")); } +TEST(NamedDeclPrinter, TestClassWithUnscopedNamedEnumAbsolute) { + ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( + "class X { enum Y { A }; };", + "A", + "::X::Y::A", + true)); +} + TEST(NamedDeclPrinter, TestClassWithScopedNamedEnum) { ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( "class X { enum class Y { A }; };", "A", "X::Y::A")); } + +TEST(NamedDeclPrinter, TestClassWithScopedNamedEnumAbsolute) { + ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( + "class X { enum class Y { A }; };", + "A", + "::X::Y::A", + true)); +} + +TEST(NamedDeclPrinter, TestLocalClass) { + ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( + "class X { void f() { enum class Y { A }; } };", + "A", + "::X::f()::Y::A", + true)); +} Index: unittests/AST/TypePrinterTest.cpp =================================================================== --- /dev/null +++ unittests/AST/TypePrinterTest.cpp @@ -0,0 +1,471 @@ +//===- unittests/AST/TypePrinterTest.cpp ------ TypePrinter printer tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for TypePrinter::print(...). +// +// These tests have a coding convention: +// * variable whose type to be printed is named 'A' unless it should have some +// special name +// * additional helper classes/namespaces/... are 'Z', 'Y', 'X' and so on. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallString.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace ast_matchers; +using namespace tooling; +namespace { + +class PrintMatch : public MatchFinder::MatchCallback { + SmallString<1024> Printed; + unsigned NumFoundDecls; + bool SuppressUnwrittenScope; + ScopePrintingKind::ScopePrintingKind Scope; + +public: + explicit PrintMatch(bool suppressUnwrittenScope, + ScopePrintingKind::ScopePrintingKind scope) + : NumFoundDecls(0), SuppressUnwrittenScope(suppressUnwrittenScope), + Scope(scope) { } + + void run(const MatchFinder::MatchResult &Result) override { + const ValueDecl *VD = Result.Nodes.getNodeAs("id"); + if (!VD) + return; + NumFoundDecls++; + if (NumFoundDecls > 1) + return; + + llvm::raw_svector_ostream Out(Printed); + PrintingPolicy Policy = Result.Context->getPrintingPolicy(); + Policy.SuppressUnwrittenScope = SuppressUnwrittenScope; + Policy.Scope = Scope; + QualType Type = VD->getType(); + Type.print(Out, Policy); + } + + StringRef getPrinted() const { + return Printed; + } + + unsigned getNumFoundDecls() const { + return NumFoundDecls; + } +}; + +::testing::AssertionResult +PrintedTypeMatches(StringRef Code, const std::vector &Args, + bool SuppressUnwrittenScope, + const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted, StringRef FileName, + ScopePrintingKind::ScopePrintingKind Scope) { + PrintMatch Printer(SuppressUnwrittenScope, Scope); + MatchFinder Finder; + Finder.addMatcher(NodeMatch, &Printer); + std::unique_ptr Factory = + newFrontendActionFactory(&Finder); + + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName)) + return testing::AssertionFailure() + << "Parsing error in \"" << Code.str() << "\""; + + if (Printer.getNumFoundDecls() == 0) + return testing::AssertionFailure() + << "Matcher didn't find any value declarations"; + + if (Printer.getNumFoundDecls() > 1) + return testing::AssertionFailure() + << "Matcher should match only one value declaration " + "(found " << Printer.getNumFoundDecls() << ")"; + + if (Printer.getPrinted() != ExpectedPrinted) + return ::testing::AssertionFailure() + << "Expected \"" << ExpectedPrinted.str() << "\", " + "got \"" << Printer.getPrinted().str() << "\""; + + return ::testing::AssertionSuccess(); +} + +::testing::AssertionResult +PrintedTypeCXX11MatchesWrapper(StringRef Code, StringRef DeclName, + StringRef ExpectedPrinted, + ScopePrintingKind::ScopePrintingKind Scope) { + std::vector Args(1, "-std=c++11"); + return PrintedTypeMatches(Code, + Args, + /*SuppressUnwrittenScope*/ true, + valueDecl(hasName(DeclName)).bind("id"), + ExpectedPrinted, + "input.cc", + Scope); +} + +::testing::AssertionResult +PrintedTypeCXX11Matches(StringRef Code, StringRef DeclName, + StringRef ExpectedPrintedAllScopes, + StringRef ExpectedPrintedScopesAsWritten, + StringRef ExpectedPrintedNoScopes) { + std::vector Args(1, "-std=c++11"); + + // Scope == FullScope + ::testing::AssertionResult result + = PrintedTypeCXX11MatchesWrapper(Code, DeclName, + ExpectedPrintedAllScopes, + ScopePrintingKind::FullScope); + if(!result.operator bool()) { + return result << ", with: FullScope"; + } + + // TODO: Activate this as soon as something similar to ScopeAsWritten is + // implemented. +// // Scope == ScopeAsWritten +// result = +// PrintedTypeCXX11MatchesWrapper(Code, DeclName, +// ExpectedPrintedScopesAsWritten, +// ScopePrintingKind::ScopeAsWritten); +// if(!result.operator bool()) { +// return result << ", with: ScopeAsWritten"; +// } + + // Scope == SuppressScope + result = + PrintedTypeCXX11MatchesWrapper(Code, DeclName, + ExpectedPrintedNoScopes, + ScopePrintingKind::SuppressScope); + if(!result.operator bool()) { + return result << ", with: SuppressScope"; + } + + return ::testing::AssertionSuccess(); +} + +} // unnamed namespace + +TEST(TypePrinter, UnwrittenScope) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "class W { };" + "namespace Y {" + "class W { };" + "namespace X {" + "class W { };" + "}" + "W A;" + "}" + "}", + "A", + "::Z::Y::W", + "W", + "W")); +} + +TEST(TypePrinter, UnwrittenScopeWithTag) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "class W { };" + "namespace Y {" + "class W { };" + "namespace X {" + "class W { };" + "}" + "class W A;" + "}" + "}", + "A", + "class ::Z::Y::W", + "class W", + "class W")); +} + +TEST(TypePrinter, WrittenScopeAbsolute1) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "class W { };" + "namespace Y {" + "class W { };" + "namespace X {" + "class W { };" + "}" + "::Z::Y::W A;" + "}" + "}", + "A", + "::Z::Y::W", + "::Z::Y::W", + "W")); +} + +TEST(TypePrinter, WrittenScopeAbsolute2) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "class W { };" + "namespace Y {" + "class W { };" + "namespace X {" + "class W { };" + "}" + "::W A;" + "}" + "}", + "A", + "::W", + "::W", + "W")); +} + +TEST(TypePrinter, WrittenScopeNonAbsolute1) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "class W { };" + "namespace Y {" + "class W { };" + "namespace X {" + "class W { };" + "}" + "X::W A;" + "}" + "}", + "A", + "::Z::Y::X::W", + "X::W", + "W")); +} + +TEST(TypePrinter, WrittenScopeNonAbsolute2) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "class W { };" + "namespace Y {" + "class W { };" + "namespace X {" + "class W { };" + "}" + "Z::Y::X::W A;" + "}" + "}", + "A", + "::Z::Y::X::W", + "Z::Y::X::W", + "W")); +} + +TEST(TypePrinter, WrittenScopeNonAbsolute3) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "class W { };" + "namespace Y {" + "class W { };" + "namespace X {" + "class W {" + "Z::W (*A)(W);" + "};" + "}" + "}" + "}", + "A", + "::Z::W (*)(::Z::Y::X::W)", + "Z::W (*)(W)", + "W (*)(W)")); +} + +TEST(TypePrinter, WrittenScopeNonAbsoluteNested1) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "class W { };" + "namespace Y {" + "class W { };" + "namespace X {" + "class W { };" + "}" + "::W (*(Z::W::* A)(void (W::*)(X::W, W, ::Z::W, int, void (Y::W::*)(int))))();" + "}" + "}", + "A", + "::W (*(::Z::W::*)(void (::Z::Y::W::*)(::Z::Y::X::W, ::Z::Y::W, ::Z::W, int, void (::Z::Y::W::*)(int))))()", + //FIXME: The written scope of a member pointer seems not to be in the AST: + "::W (*("/*Z::*/"W::*)(void (W::*)(X::W, W, ::Z::W, int, void ("/*Y::*/"W::*)(int))))()", + "W (*(W::*)(void (W::*)(W, W, W, int, void (W::*)(int))))()")); +} + + +TEST(TypePrinter, TemplClassWithSimpleTemplArg) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "namespace Z { template class X { }; class Y { };" + "X A;" + "}", + "A", + "::Z::X< ::Z::Y>", + "X", + "X")); +} + +TEST(TypePrinter, TemplClassWithTemplClassTemplArg1) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "namespace Z { template class X { public: template class Y { }; };" + "namespace W { namespace V { class U { }; } }" + "X< X::Y > A;" + "}", + "A", + "::Z::X< ::Z::X::Y >", + "X::Y >", + "X >")); +} + +TEST(TypePrinter, TypeDef1) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "namespace Y {" + "class W { };" + "namespace X {" + "class W {" + "public:" + "class V { };" + "};" + "}" + "typedef X::W WT1;" + "}" + "class W { };" + "typedef Y::WT1 WT2;" + "WT2::V A;" + "}", + "A", + "::Z::WT2::V", + "WT2::V", + "V")); +} + +TEST(TypePrinter, TypeDef2) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "namespace Y {" + "class W { };" + "namespace X {" + "class W {" + "public:" + "class V { };" + "};" + "}" + "typedef X::W WT1;" + "}" + "class W { };" + "typedef Y::WT1 WT2;" + "WT2 A;" + "}", + "A", + "::Z::WT2", + "WT2", + "WT2")); +} + + +TEST(TypePrinter, TypeDef3) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "class W { };" + "namespace Z {" + "namespace Y {" + "class W { };" + "namespace X {" + "class W {" + "public:" + "class V { };" + "typedef V VT1;" + "};" + "}" + "typedef X::W WT1;" + "}" + "class W { };" + "Y::WT1::VT1 A;" + "}", + "A", + "::Z::Y::WT1::VT1", + "Y::WT1::VT1", + "VT1")); +} + +TEST(TypePrinter, AnonymousNamespace1) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "namespace Z {" + "namespace {" + "namespace {" + "namespace Y {" + "namespace {" + "class X { };" + "}" + "X A;" + "}" + "}" + "}" + "}", + "A", + "::Z::Y::X", + "X", + "X")); +} + +// Template dependent type: Printing of absolute scope not possible. +TEST(TypePrinter, TemplateDependent1) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "template " + "struct Z : public Y {" + "template struct V {" + "class W { };" + "typename Y::template V::W A;" + "};" + "};", + "A", + "typename Y::template V::W", + "typename Y::template V::W", + "typename W")); +} + +TEST(TypePrinter, TemplateDependent2) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "template " + "struct Z : public Y {" + "template struct V {" + "class W { };" + "V::W A;" + "};" + "};", + "A", + "::Z::V::W", + "V::W", + "W")); +} + + +// Local class: Printing of absolute scope not possible. +TEST(TypePrinter, LocalClasses1) { + ASSERT_TRUE(PrintedTypeCXX11Matches( + "void f() {" + "class Z {" + "public: class Y { };" + "};" + "Z::Y A;" + "}", + "A", + "Z::Y", // TODO: or "::f()::Z::Y"? + "Z::Y", + "Y")); +}