Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -18,6 +18,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/ExternalASTSource.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/AST/Redeclarable.h" #include "clang/AST/Type.h" #include "clang/Basic/Linkage.h" @@ -27,8 +28,8 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/TrailingObjects.h" +#include "llvm/Support/raw_ostream.h" namespace clang { struct ASTTemplateArgumentListInfo; @@ -266,8 +267,10 @@ /// namespace), it will return same result as printName(). /// Creating this name is expensive, so it should be called only when /// performance doesn't matter. - void printQualifiedName(raw_ostream &OS) const; - void printQualifiedName(raw_ostream &OS, const PrintingPolicy &Policy) const; + void printQualifiedName(raw_ostream &OS, + PrintingContext Context = PrintingContext()) const; + void printQualifiedName(raw_ostream &OS, const PrintingPolicy &Policy, + PrintingContext Context = PrintingContext()) const; // FIXME: Remove string version. std::string getQualifiedNameAsString() const; Index: include/clang/AST/DeclBase.h =================================================================== --- include/clang/AST/DeclBase.h +++ include/clang/AST/DeclBase.h @@ -16,6 +16,7 @@ #include "clang/AST/AttrIterator.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/VersionTuple.h" #include "llvm/ADT/PointerUnion.h" @@ -1090,7 +1091,8 @@ void print(raw_ostream &Out, unsigned Indentation = 0, bool PrintInstantiation = false) const; void print(raw_ostream &Out, const PrintingPolicy &Policy, - unsigned Indentation = 0, bool PrintInstantiation = false) const; + unsigned Indentation = 0, bool PrintInstantiation = false, + PrintingContext Context = PrintingContext()) const; static void printGroup(Decl** Begin, unsigned NumDecls, raw_ostream &Out, const PrintingPolicy &Policy, unsigned Indentation = 0); Index: include/clang/AST/NestedNameSpecifier.h =================================================================== --- include/clang/AST/NestedNameSpecifier.h +++ include/clang/AST/NestedNameSpecifier.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H #define LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H +#include "clang/AST/PrettyPrinter.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" @@ -208,7 +209,8 @@ /// \brief Print this nested name specifier to the given output /// stream. - void print(raw_ostream &OS, const PrintingPolicy &Policy) const; + void print(raw_ostream &OS, const PrintingPolicy &Policy, + PrintingContext Context = PrintingContext()) const; void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(Prefix.getOpaqueValue()); Index: include/clang/AST/PrettyPrinter.h =================================================================== --- include/clang/AST/PrettyPrinter.h +++ include/clang/AST/PrettyPrinter.h @@ -30,27 +30,94 @@ virtual bool handledStmt(Stmt* E, raw_ostream& OS) = 0; }; -/// \brief Describes how types, statements, expressions, and -/// declarations should be printed. +// 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 Provides a context for one printing process. This should be used for +/// mutable information that need to be shared across the member functions +/// involved in the printing process of types and declarations. +struct PrintingContext { + /// \brief Creates a default printing context. + PrintingContext() : TemporarySuppressScope(false) {} + + /// \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 only used internally. + bool TemporarySuppressScope : 1; +}; + +/// \brief Describes how types, statements, expressions, and declarations should +/// be printed. +/// +/// This should not be mutated during the printing process. Use PrintingContext +/// for mutable information instead. /// /// 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), - SuppressUnwrittenScope(false), SuppressInitializers(false), - ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), - SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false), - SuppressTemplateArgsInCXXConstructors(false), - Bool(LO.Bool), Restrict(LO.C99), - Alignof(LO.CPlusPlus11), UnderscoreAlignof(LO.C11), - UseVoidForZeroParams(!LO.CPlusPlus), - TerseOutput(false), PolishForDeclaration(false), - Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), - IncludeNewlines(true), MSVCFormatting(false) { } + : Indentation(2), SuppressSpecifiers(false), + SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false), + Scope(ScopePrintingKind::DefaultScope), SuppressUnwrittenScope(false), + SuppressInitializers(false), ConstantArraySizeAsWritten(false), + AnonymousTagLocations(true), SuppressStrongLifetime(false), + SuppressLifetimeQualifiers(false), + SuppressTemplateArgsInCXXConstructors(false), Bool(LO.Bool), + Restrict(LO.C99), Alignof(LO.CPlusPlus11), UnderscoreAlignof(LO.C11), + UseVoidForZeroParams(!LO.CPlusPlus), TerseOutput(false), + PolishForDeclaration(false), Half(LO.Half), + MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), + MSVCFormatting(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 +168,14 @@ /// \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 +206,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 +247,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. /// Index: include/clang/AST/TemplateBase.h =================================================================== --- include/clang/AST/TemplateBase.h +++ include/clang/AST/TemplateBase.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_AST_TEMPLATEBASE_H #define LLVM_CLANG_AST_TEMPLATEBASE_H +#include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" #include "llvm/ADT/APSInt.h" @@ -353,8 +354,9 @@ TemplateArgument getPackExpansionPattern() const; /// \brief Print this template argument to the given output stream. - void print(const PrintingPolicy &Policy, raw_ostream &Out) const; - + void print(const PrintingPolicy &Policy, raw_ostream &Out, + PrintingContext Context = PrintingContext()) const; + /// \brief Debugging aid that dumps the template argument. void dump(raw_ostream &Out) const; Index: include/clang/AST/TemplateName.h =================================================================== --- include/clang/AST/TemplateName.h +++ include/clang/AST/TemplateName.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_AST_TEMPLATENAME_H #include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerUnion.h" @@ -277,12 +278,8 @@ /// /// \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; + PrintingContext Context = PrintingContext()) const; /// \brief Debugging aid that dumps the template name. void dump(raw_ostream &OS) const; Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -18,6 +18,7 @@ #define LLVM_CLANG_AST_TYPE_H #include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateName.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/Diagnostic.h" @@ -950,19 +951,21 @@ std::string getAsString(const PrintingPolicy &Policy) const; void print(raw_ostream &OS, const PrintingPolicy &Policy, - const Twine &PlaceHolder = Twine(), - unsigned Indentation = 0) const { - print(split(), OS, Policy, PlaceHolder, Indentation); + const Twine &PlaceHolder = Twine(), unsigned Indentation = 0, + PrintingContext Context = PrintingContext()) const { + print(split(), OS, Policy, PlaceHolder, Indentation, Context); } static void print(SplitQualType split, raw_ostream &OS, const PrintingPolicy &policy, const Twine &PlaceHolder, - unsigned Indentation = 0) { - return print(split.Ty, split.Quals, OS, policy, PlaceHolder, Indentation); + unsigned Indentation = 0, + PrintingContext Context = PrintingContext()) { + return print(split.Ty, split.Quals, OS, policy, PlaceHolder, Indentation, + Context); } - static void print(const Type *ty, Qualifiers qs, - raw_ostream &OS, const PrintingPolicy &policy, - const Twine &PlaceHolder, - unsigned Indentation = 0); + static void print(const Type *ty, Qualifiers qs, raw_ostream &OS, + const PrintingPolicy &policy, const Twine &PlaceHolder, + unsigned Indentation = 0, + PrintingContext Context = PrintingContext()); void getAsStringInternal(std::string &Str, const PrintingPolicy &Policy) const { @@ -979,25 +982,30 @@ class StreamedQualTypeHelper { const QualType &T; const PrintingPolicy &Policy; + PrintingContext Context; const Twine &PlaceHolder; unsigned Indentation; public: StreamedQualTypeHelper(const QualType &T, const PrintingPolicy &Policy, - const Twine &PlaceHolder, unsigned Indentation) - : T(T), Policy(Policy), PlaceHolder(PlaceHolder), - Indentation(Indentation) { } + const Twine &PlaceHolder, unsigned Indentation, + PrintingContext Context = PrintingContext()) + : T(T), Policy(Policy), Context(Context), PlaceHolder(PlaceHolder), + Indentation(Indentation) {} friend raw_ostream &operator<<(raw_ostream &OS, const StreamedQualTypeHelper &SQT) { - SQT.T.print(OS, SQT.Policy, SQT.PlaceHolder, SQT.Indentation); + SQT.T.print(OS, SQT.Policy, SQT.PlaceHolder, SQT.Indentation, + SQT.Context); return OS; } }; - StreamedQualTypeHelper stream(const PrintingPolicy &Policy, - const Twine &PlaceHolder = Twine(), - unsigned Indentation = 0) const { - return StreamedQualTypeHelper(*this, Policy, PlaceHolder, Indentation); + StreamedQualTypeHelper + stream(const PrintingPolicy &Policy, const Twine &PlaceHolder = Twine(), + unsigned Indentation = 0, + PrintingContext Context = PrintingContext()) const { + return StreamedQualTypeHelper(*this, Policy, PlaceHolder, Indentation, + Context); } void dump(const char *s) const; @@ -3527,8 +3535,9 @@ bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } - void printExceptionSpecification(raw_ostream &OS, - const PrintingPolicy &Policy) const; + void printExceptionSpecification( + raw_ostream &OS, const PrintingPolicy &Policy, + PrintingContext Context = PrintingContext()) const; static bool classof(const Type *T) { return T->getTypeClass() == FunctionProto; @@ -4328,18 +4337,21 @@ /// \brief Print a template argument list, including the '<' and '>' /// enclosing the template arguments. - static void PrintTemplateArgumentList(raw_ostream &OS, - ArrayRef Args, - const PrintingPolicy &Policy, - bool SkipBrackets = false); - - static void PrintTemplateArgumentList(raw_ostream &OS, - ArrayRef Args, - const PrintingPolicy &Policy); - - static void PrintTemplateArgumentList(raw_ostream &OS, - const TemplateArgumentListInfo &, - const PrintingPolicy &Policy); + static void + PrintTemplateArgumentList(raw_ostream &OS, ArrayRef Args, + const PrintingPolicy &Policy, + bool SkipBrackets = false, + PrintingContext Context = PrintingContext()); + + static void + PrintTemplateArgumentList(raw_ostream &OS, ArrayRef Args, + const PrintingPolicy &Policy, + PrintingContext Context = PrintingContext()); + + static void + PrintTemplateArgumentList(raw_ostream &OS, const TemplateArgumentListInfo &, + const PrintingPolicy &Policy, + PrintingContext Context = PrintingContext()); /// True if this template specialization type matches a current /// instantiation in the context in which it is found. Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1404,12 +1404,13 @@ return OS.str(); } -void NamedDecl::printQualifiedName(raw_ostream &OS) const { - printQualifiedName(OS, getASTContext().getPrintingPolicy()); +void NamedDecl::printQualifiedName(raw_ostream &OS, + PrintingContext Context) const { + printQualifiedName(OS, getASTContext().getPrintingPolicy(), Context); } -void NamedDecl::printQualifiedName(raw_ostream &OS, - const PrintingPolicy &P) const { +void NamedDecl::printQualifiedName(raw_ostream &OS, const PrintingPolicy &P, + PrintingContext Context) const { const DeclContext *Ctx = getDeclContext(); // For ObjC methods, look through categories and use the interface as context. @@ -1425,10 +1426,18 @@ typedef SmallVector ContextsTy; ContextsTy Contexts; - // Collect contexts. - while (Ctx && isa(Ctx)) { - Contexts.push_back(Ctx); - Ctx = Ctx->getParent(); + if (P.Scope != ScopePrintingKind::SuppressScope && + !Context.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/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -28,6 +28,7 @@ class DeclPrinter : public DeclVisitor { raw_ostream &Out; PrintingPolicy Policy; + PrintingContext Context; unsigned Indentation; bool PrintInstantiation; @@ -48,9 +49,10 @@ public: DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy, - unsigned Indentation = 0, bool PrintInstantiation = false) - : Out(Out), Policy(Policy), Indentation(Indentation), - PrintInstantiation(PrintInstantiation) { } + unsigned Indentation = 0, bool PrintInstantiation = false, + PrintingContext Context = PrintingContext()) + : Out(Out), Policy(Policy), Context(Context), Indentation(Indentation), + PrintInstantiation(PrintInstantiation) {} void VisitDeclContext(DeclContext *DC, bool Indent = true); @@ -114,8 +116,9 @@ } void Decl::print(raw_ostream &Out, const PrintingPolicy &Policy, - unsigned Indentation, bool PrintInstantiation) const { - DeclPrinter Printer(Out, Policy, Indentation, PrintInstantiation); + unsigned Indentation, bool PrintInstantiation, + PrintingContext Context) const { + DeclPrinter Printer(Out, Policy, Indentation, PrintInstantiation, Context); Printer.Visit(const_cast(this)); } @@ -510,7 +513,7 @@ PrintingPolicy SubPolicy(Policy); SubPolicy.SuppressSpecifiers = false; std::string Proto; - if (!Policy.SuppressScope) { + if (Policy.Scope != ScopePrintingKind::SuppressScope) { if (const NestedNameSpecifier *NS = D->getQualifier()) { llvm::raw_string_ostream OS(Proto); NS->print(OS, Policy); Index: lib/AST/DeclarationName.cpp =================================================================== --- lib/AST/DeclarationName.cpp +++ lib/AST/DeclarationName.cpp @@ -653,15 +653,18 @@ case DeclarationName::CXXDestructorName: case DeclarationName::CXXConversionFunctionName: if (TypeSourceInfo *TInfo = LocInfo.NamedType.TInfo) { - if (Name.getNameKind() == DeclarationName::CXXDestructorName) - OS << '~'; - else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) - OS << "operator "; LangOptions LO; LO.CPlusPlus = true; LO.Bool = true; PrintingPolicy PP(LO); - PP.SuppressScope = true; + PP.Scope = ScopePrintingKind::SuppressScope; + if (Name.getNameKind() == DeclarationName::CXXDestructorName) + OS << '~'; + else if (Name.getNameKind() == + DeclarationName::CXXConversionFunctionName) { + OS << "operator "; + PP.Scope = ScopePrintingKind::DefaultScope; + } OS << TInfo->getType().getAsString(PP); } else OS << Name; Index: lib/AST/NestedNameSpecifier.cpp =================================================================== --- lib/AST/NestedNameSpecifier.cpp +++ lib/AST/NestedNameSpecifier.cpp @@ -258,11 +258,31 @@ /// \brief Print this nested name specifier to the given output /// stream. -void -NestedNameSpecifier::print(raw_ostream &OS, - const PrintingPolicy &Policy) const { +void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, + PrintingContext Context) const { + if (Policy.Scope == ScopePrintingKind::SuppressScope || + Context.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; @@ -295,8 +325,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 @@ -305,21 +337,8 @@ // 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); - } + Context.TemporarySuppressScope = true; + QualType(T, 0).print(OS, Policy, Twine(), 0, Context); break; } } Index: lib/AST/TemplateBase.cpp =================================================================== --- lib/AST/TemplateBase.cpp +++ lib/AST/TemplateBase.cpp @@ -35,8 +35,9 @@ /// \param Out the raw_ostream instance to use for printing. /// /// \param Policy the printing policy for EnumConstantDecl printing. -static void printIntegral(const TemplateArgument &TemplArg, - raw_ostream &Out, const PrintingPolicy& Policy) { +static void printIntegral(const TemplateArgument &TemplArg, raw_ostream &Out, + const PrintingPolicy &Policy, + PrintingContext Context = PrintingContext()) { const ::clang::Type *T = TemplArg.getIntegralType().getTypePtr(); const llvm::APSInt &Val = TemplArg.getAsIntegral(); @@ -47,7 +48,7 @@ // may create a size difference between the enum value and template // argument value, requiring isSameValue here instead of operator==. if (llvm::APSInt::isSameValue(ECD->getInitVal(), Val)) { - ECD->printQualifiedName(Out, Policy); + ECD->printQualifiedName(Out, Policy, Context); return; } } @@ -376,8 +377,8 @@ llvm_unreachable("Invalid TemplateArgument Kind!"); } -void TemplateArgument::print(const PrintingPolicy &Policy, - raw_ostream &Out) const { +void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out, + PrintingContext Context) const { switch (getKind()) { case Null: Out << "(no value)"; @@ -386,7 +387,7 @@ case Type: { PrintingPolicy SubPolicy(Policy); SubPolicy.SuppressStrongLifetime = true; - getAsType().print(Out, SubPolicy); + getAsType().print(Out, SubPolicy, Twine(), 0, Context); break; } @@ -395,7 +396,7 @@ Out << '&'; if (ND->getDeclName()) { // FIXME: distinguish between pointer and reference args? - ND->printQualifiedName(Out); + ND->printQualifiedName(Out, Context); } else { Out << "(anonymous)"; } @@ -407,16 +408,16 @@ break; case Template: - getAsTemplate().print(Out, Policy); + getAsTemplate().print(Out, Policy, Context); break; case TemplateExpansion: - getAsTemplateOrTemplatePattern().print(Out, Policy); + getAsTemplateOrTemplatePattern().print(Out, Policy, Context); Out << "..."; break; case Integral: { - printIntegral(*this, Out, Policy); + printIntegral(*this, Out, Policy, Context); break; } @@ -432,8 +433,8 @@ First = false; else Out << ", "; - - P.print(Policy, Out); + + P.print(Policy, Out, Context); } Out << ">"; break; Index: lib/AST/TemplateName.cpp =================================================================== --- lib/AST/TemplateName.cpp +++ lib/AST/TemplateName.cpp @@ -174,20 +174,24 @@ return getAsSubstTemplateTemplateParmPack() != nullptr; } -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) - QTN->getQualifier()->print(OS, Policy); +void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, + PrintingContext Context) const { + if (TemplateDecl *Template = Storage.dyn_cast()) { + if (Policy.Scope == ScopePrintingKind::FullScope && + !Context.TemporarySuppressScope) { + Template->printQualifiedName(OS, Policy, Context); + } else { + OS << *Template; + } + } else if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) { + if (!Context.TemporarySuppressScope) + QTN->getQualifier()->print(OS, Policy, Context); if (QTN->hasTemplateKeyword()) OS << "template "; OS << *QTN->getDecl(); } else if (DependentTemplateName *DTN = getAsDependentTemplateName()) { - if (!SuppressNNS && DTN->getQualifier()) - DTN->getQualifier()->print(OS, Policy); + if (!Context.TemporarySuppressScope && DTN->getQualifier()) + DTN->getQualifier()->print(OS, Policy, Context); OS << "template "; if (DTN->isIdentifier()) @@ -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,23 +26,21 @@ #include "llvm/Support/raw_ostream.h" using namespace clang; -namespace { - /// \brief RAII object that enables printing of the ARC __strong lifetime - /// qualifier. - class IncludeStrongLifetimeRAII { - PrintingPolicy &Policy; - bool Old; - - public: - explicit IncludeStrongLifetimeRAII(PrintingPolicy &Policy) +namespace clang { +/// \brief RAII object that enables printing of the ARC __strong lifetime +/// qualifier. +class IncludeStrongLifetimeRAII { + PrintingPolicy &Policy; + bool Old; + +public: + explicit IncludeStrongLifetimeRAII(PrintingPolicy &Policy) : Policy(Policy), Old(Policy.SuppressStrongLifetime) { - if (!Policy.SuppressLifetimeQualifiers) - Policy.SuppressStrongLifetime = false; - } - - ~IncludeStrongLifetimeRAII() { - Policy.SuppressStrongLifetime = Old; - } + if (!Policy.SuppressLifetimeQualifiers) + Policy.SuppressStrongLifetime = false; + } + + ~IncludeStrongLifetimeRAII() { Policy.SuppressStrongLifetime = Old; } }; class ParamPolicyRAII { @@ -63,32 +61,30 @@ class ElaboratedTypePolicyRAII { PrintingPolicy &Policy; bool SuppressTagKeyword; - bool SuppressScope; public: explicit ElaboratedTypePolicyRAII(PrintingPolicy &Policy) : Policy(Policy) { SuppressTagKeyword = Policy.SuppressTagKeyword; - SuppressScope = Policy.SuppressScope; Policy.SuppressTagKeyword = true; - Policy.SuppressScope = true; } ~ElaboratedTypePolicyRAII() { Policy.SuppressTagKeyword = SuppressTagKeyword; - Policy.SuppressScope = SuppressScope; } }; class TypePrinter { PrintingPolicy Policy; + PrintingContext Context; unsigned Indentation; bool HasEmptyPlaceHolder; bool InsideCCAttribute; public: - explicit TypePrinter(const PrintingPolicy &Policy, unsigned Indentation = 0) - : Policy(Policy), Indentation(Indentation), - HasEmptyPlaceHolder(false), InsideCCAttribute(false) { } + explicit TypePrinter(const PrintingPolicy &Policy, unsigned Indentation = 0, + PrintingContext Context = PrintingContext()) + : Policy(Policy), Context(Context), Indentation(Indentation), + HasEmptyPlaceHolder(false), InsideCCAttribute(false) {} void print(const Type *ty, Qualifiers qs, raw_ostream &OS, StringRef PlaceHolder); @@ -417,7 +413,8 @@ PrintingPolicy InnerPolicy(Policy); InnerPolicy.IncludeTagDefinition = false; - TypePrinter(InnerPolicy).print(QualType(T->getClass(), 0), OS, StringRef()); + TypePrinter(InnerPolicy, 0, Context) + .print(QualType(T->getClass(), 0), OS, StringRef()); OS << "::*"; } @@ -592,11 +589,10 @@ OS << ")))"; } -void -FunctionProtoType::printExceptionSpecification(raw_ostream &OS, - const PrintingPolicy &Policy) - const { - +void FunctionProtoType::printExceptionSpecification( + raw_ostream &OS, const PrintingPolicy &Policy, + PrintingContext Context) const { + if (hasDynamicExceptionSpec()) { OS << " throw("; if (getExceptionSpecType() == EST_MSAny) @@ -605,8 +601,8 @@ for (unsigned I = 0, N = getNumExceptions(); I != N; ++I) { if (I) OS << ", "; - - OS << getExceptionType(I).stream(Policy); + + OS << getExceptionType(I).stream(Policy, Twine(), 0, Context); } OS << ')'; } else if (isNoexceptExceptionSpec(getExceptionSpecType())) { @@ -770,7 +766,7 @@ OS << " &&"; break; } - T->printExceptionSpecification(OS, Policy); + T->printExceptionSpecification(OS, Policy, Context); if (T->hasTrailingReturn()) { OS << " -> "; @@ -801,13 +797,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 && + !Context.TemporarySuppressScope) { + // Print the scope: AppendScope(D->getDeclContext(), OS); - + } IdentifierInfo *II = D->getIdentifier(); OS << II->getName(); spaceBeforePlaceHolder(OS); @@ -820,10 +814,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) { @@ -905,7 +899,7 @@ printBefore(T->getDeducedType(), OS); } else { IncludeStrongLifetimeRAII Strong(Policy); - T->getTemplateName().print(OS, Policy); + T->getTemplateName().print(OS, Policy, Context); spaceBeforePlaceHolder(OS); } } @@ -942,7 +936,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); @@ -960,7 +959,7 @@ OS << Spec->getIdentifier()->getName(); const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); TemplateSpecializationType::PrintTemplateArgumentList( - OS, TemplateArgs.asArray(), Policy); + OS, TemplateArgs.asArray(), Policy, false, Context); OS << "::"; } else if (TagDecl *Tag = dyn_cast(DC)) { if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl()) @@ -976,7 +975,7 @@ if (Policy.IncludeTagDefinition) { PrintingPolicy SubPolicy = Policy; SubPolicy.IncludeTagDefinition = false; - D->print(OS, SubPolicy, Indentation); + D->print(OS, SubPolicy, Indentation, false, Context); spaceBeforePlaceHolder(OS); return; } @@ -994,7 +993,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 && + !Context.TemporarySuppressScope) AppendScope(D->getDeclContext(), OS); if (const IdentifierInfo *II = D->getIdentifier()) @@ -1033,6 +1033,8 @@ OS << (Policy.MSVCFormatting ? '\'' : ')'); } + Context.TemporarySuppressScope = false; + // If this is a class template specialization, print the template // arguments. if (ClassTemplateSpecializationDecl *Spec @@ -1047,7 +1049,8 @@ Args = TemplateArgs.asArray(); } IncludeStrongLifetimeRAII Strong(Policy); - TemplateSpecializationType::PrintTemplateArgumentList(OS, Args, Policy); + TemplateSpecializationType::PrintTemplateArgumentList(OS, Args, Policy, + false, Context); } spaceBeforePlaceHolder(OS); @@ -1101,13 +1104,14 @@ } void TypePrinter::printTemplateSpecializationBefore( - const TemplateSpecializationType *T, - raw_ostream &OS) { + const TemplateSpecializationType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - T->getTemplateName().print(OS, Policy); + T->getTemplateName().print(OS, Policy, Context); + + Context.TemporarySuppressScope = false; TemplateSpecializationType::PrintTemplateArgumentList( - OS, T->template_arguments(), Policy); + OS, T->template_arguments(), Policy, false, Context); spaceBeforePlaceHolder(OS); } void TypePrinter::printTemplateSpecializationAfter( @@ -1123,17 +1127,35 @@ 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) - Qualifier->print(OS, Policy); + NestedNameSpecifier *Qualifier = T->getQualifier(); + if (Qualifier) { + Qualifier->print(OS, Policy, Context); + ScopeHasBeenPrinted = true; + } + // If there are no nested name specifiers, the complete scope will be + // printed later (location depends on the sub-type). } - + + // 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. + Context.TemporarySuppressScope = + Policy.Scope == ScopePrintingKind::DefaultScope || ScopeHasBeenPrinted; ElaboratedTypePolicyRAII PolicyRAII(Policy); printBefore(T->getNamedType(), OS); } @@ -1163,9 +1185,8 @@ OS << TypeWithKeyword::getKeywordName(T->getKeyword()); if (T->getKeyword() != ETK_None) OS << " "; - - T->getQualifier()->print(OS, Policy); - + T->getQualifier()->print(OS, Policy, Context); + OS << T->getIdentifier()->getName(); spaceBeforePlaceHolder(OS); } @@ -1179,13 +1200,15 @@ OS << TypeWithKeyword::getKeywordName(T->getKeyword()); if (T->getKeyword() != ETK_None) OS << " "; - + if (T->getQualifier()) - T->getQualifier()->print(OS, Policy); + T->getQualifier()->print(OS, Policy, Context); OS << T->getIdentifier()->getName(); - TemplateSpecializationType::PrintTemplateArgumentList(OS, - T->template_arguments(), - Policy); + + Context.TemporarySuppressScope = false; + + TemplateSpecializationType::PrintTemplateArgumentList( + OS, T->template_arguments(), Policy, false, Context); spaceBeforePlaceHolder(OS); } void TypePrinter::printDependentTemplateSpecializationAfter( @@ -1489,18 +1512,17 @@ void TypePrinter::printObjCObjectPointerAfter(const ObjCObjectPointerType *T, raw_ostream &OS) { } -void TemplateSpecializationType:: - PrintTemplateArgumentList(raw_ostream &OS, - const TemplateArgumentListInfo &Args, - const PrintingPolicy &Policy) { - return PrintTemplateArgumentList(OS, - Args.arguments(), - Policy); +void TemplateSpecializationType::PrintTemplateArgumentList( + raw_ostream &OS, const TemplateArgumentListInfo &Args, + const PrintingPolicy &Policy, PrintingContext Context) { + return PrintTemplateArgumentList(OS, Args.arguments(), Policy, Context); } void TemplateSpecializationType::PrintTemplateArgumentList( raw_ostream &OS, ArrayRef Args, - const PrintingPolicy &Policy, bool SkipBrackets) { + const PrintingPolicy &Policy, bool SkipBrackets, PrintingContext Context) { + assert(!Context.TemporarySuppressScope && + "Suppressing the scope of each argument is not supported."); const char *Comma = Policy.MSVCFormatting ? "," : ", "; if (!SkipBrackets) OS << '<'; @@ -1514,13 +1536,12 @@ if (Arg.getKind() == TemplateArgument::Pack) { if (Arg.pack_size() && !FirstArg) OS << Comma; - PrintTemplateArgumentList(ArgOS, - Arg.getPackAsArray(), - Policy, true); + PrintTemplateArgumentList(ArgOS, Arg.getPackAsArray(), Policy, true, + Context); } else { if (!FirstArg) OS << Comma; - Arg.print(Policy, ArgOS); + Arg.print(Policy, ArgOS, Context); } StringRef ArgString = ArgOS.str(); @@ -1547,10 +1568,9 @@ } // Sadly, repeat all that with TemplateArgLoc. -void TemplateSpecializationType:: -PrintTemplateArgumentList(raw_ostream &OS, - ArrayRef Args, - const PrintingPolicy &Policy) { +void TemplateSpecializationType::PrintTemplateArgumentList( + raw_ostream &OS, ArrayRef Args, + const PrintingPolicy &Policy, PrintingContext Context) { OS << '<'; const char *Comma = Policy.MSVCFormatting ? "," : ", "; @@ -1564,11 +1584,10 @@ SmallString<128> Buf; llvm::raw_svector_ostream ArgOS(Buf); if (Arg.getArgument().getKind() == TemplateArgument::Pack) { - PrintTemplateArgumentList(ArgOS, - Arg.getArgument().getPackAsArray(), - Policy, true); + PrintTemplateArgumentList(ArgOS, Arg.getArgument().getPackAsArray(), + Policy, true, Context); } else { - Arg.getArgument().print(Policy, ArgOS); + Arg.getArgument().print(Policy, ArgOS, Context); } StringRef ArgString = ArgOS.str(); @@ -1720,13 +1739,13 @@ return buffer; } -void QualType::print(const Type *ty, Qualifiers qs, - raw_ostream &OS, const PrintingPolicy &policy, - const Twine &PlaceHolder, unsigned Indentation) { +void QualType::print(const Type *ty, Qualifiers qs, raw_ostream &OS, + const PrintingPolicy &policy, const Twine &PlaceHolder, + unsigned Indentation, PrintingContext Context) { SmallString<128> PHBuf; StringRef PH = PlaceHolder.toStringRef(PHBuf); - TypePrinter(policy, Indentation).print(ty, qs, OS, PH); + TypePrinter(policy, Indentation, Context).print(ty, qs, OS, PH); } void QualType::getAsStringInternal(const Type *ty, Qualifiers qs, Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -909,8 +909,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: test/Index/comment-cplus-decls.cpp =================================================================== --- test/Index/comment-cplus-decls.cpp +++ test/Index/comment-cplus-decls.cpp @@ -149,7 +149,7 @@ }; } // CHECK: void f(const T &t = T()) -// CHECK: friend void vector<A>::f(const test3::A &) +// CHECK: friend void vector<test3::A>::f(const test3::A &) class MyClass { 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); } @@ -60,12 +64,11 @@ } }; -::testing::AssertionResult -PrintedNamedDeclMatches(StringRef Code, const std::vector &Args, - bool SuppressUnwrittenScope, - const DeclarationMatcher &NodeMatch, - StringRef ExpectedPrinted, StringRef FileName) { - PrintMatch Printer(SuppressUnwrittenScope); +::testing::AssertionResult PrintedNamedDeclMatches( + StringRef Code, const std::vector &Args, + bool SuppressUnwrittenScope, const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted, StringRef FileName, bool MakeAbsolute) { + PrintMatch Printer(SuppressUnwrittenScope, MakeAbsolute); MatchFinder Finder; Finder.addMatcher(NodeMatch, &Printer); std::unique_ptr Factory = @@ -94,26 +97,24 @@ ::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, + return PrintedNamedDeclMatches(Code, Args, /*SuppressUnwrittenScope*/ false, namedDecl(hasName(DeclName)).bind("id"), - ExpectedPrinted, - "input.cc"); + ExpectedPrinted, "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, + return PrintedNamedDeclMatches(Code, Args, /*SuppressUnwrittenScope*/ true, namedDecl(hasName(DeclName)).bind("id"), - ExpectedPrinted, - "input.cc"); + ExpectedPrinted, "input.cc", MakeAbsolute); } } // unnamed namespace @@ -125,6 +126,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 +141,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 +156,14 @@ "A")); } +TEST(NamedDeclPrinter, TestUnscopedUnnamedEnumAbsolute) { + ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( + "enum { A };", + "A", + "::A", + true)); +} + TEST(NamedDeclPrinter, TestNamedEnum) { ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( "enum X { A };", @@ -146,6 +171,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 +200,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,464 @@ +//===- 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")); +}