diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -21,6 +21,7 @@ class DeclContext; class LangOptions; class Stmt; +class ClassTemplateSpecializationDecl; class PrinterHelper { public: @@ -47,6 +48,14 @@ /// The printing stops at the first isScopeVisible() == true, so there will /// be no calls with outer scopes. virtual bool isScopeVisible(const DeclContext *DC) const { return false; } + + enum class TriState : int { kYes, kNo, kUnknown }; + + virtual TriState + IsTemplateArgumentDefaulted(clang::ClassTemplateSpecializationDecl const *D, + size_t ArgIndex) const { + return TriState::kNo; + } }; /// Describes how types, statements, expressions, and declarations should be diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -63,6 +63,7 @@ class TagDecl; class TemplateParameterList; class Type; +class ClassTemplateSpecializationDecl; enum { TypeAlignmentInBits = 4, @@ -5463,6 +5464,11 @@ const PrintingPolicy &Policy, const TemplateParameterList *TPL = nullptr); +void printTemplateArgumentList( + raw_ostream &OS, ArrayRef Args, + const PrintingPolicy &Policy, const TemplateParameterList *TPL, + const clang::ClassTemplateSpecializationDecl *CTSD); + /// Make a best-effort determination of whether the type T can be produced by /// substituting Args into the default argument of Param. bool isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg, diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1285,7 +1285,7 @@ const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); printTemplateArgumentList( OS, TemplateArgs.asArray(), Policy, - Spec->getSpecializedTemplate()->getTemplateParameters()); + Spec->getSpecializedTemplate()->getTemplateParameters(), Spec); OS << "::"; } else if (const auto *Tag = dyn_cast(DC)) { AppendScope(DC->getParent(), OS, Tag->getDeclName()); @@ -1383,7 +1383,7 @@ IncludeStrongLifetimeRAII Strong(Policy); printTemplateArgumentList( OS, Args, Policy, - Spec->getSpecializedTemplate()->getTemplateParameters()); + Spec->getSpecializedTemplate()->getTemplateParameters(), Spec); } spaceBeforePlaceHolder(OS); @@ -2079,18 +2079,37 @@ } template -static ArrayRef dropDefaultedArguments(ArrayRef Args, - const PrintingPolicy &Policy, - const TemplateParameterList *TPL) { +static ArrayRef +dropDefaultedArguments(ArrayRef Args, const PrintingPolicy &Policy, + const TemplateParameterList *TPL, + const ClassTemplateSpecializationDecl *CTSD) { + using TriState = PrintingCallbacks::TriState; + ASTContext &Ctx = TPL->getParam(0)->getASTContext(); llvm::SmallVector OrigArgs; for (const TA &A : Args) OrigArgs.push_back(getArgument(A)); - while (!Args.empty() && - isSubstitutedDefaultArgument(Ctx, getArgument(Args.back()), - TPL->getParam(Args.size() - 1), OrigArgs, - TPL->getDepth())) + + auto const *Callbacks = Policy.Callbacks; + + while (!Args.empty()) { + auto IsDefaulted = PrintingCallbacks::TriState::kUnknown; + if (Callbacks != nullptr) + IsDefaulted = + Callbacks->IsTemplateArgumentDefaulted(CTSD, Args.size() - 1); + + if (IsDefaulted == TriState::kUnknown) + IsDefaulted = isSubstitutedDefaultArgument(Ctx, getArgument(Args.back()), + TPL->getParam(Args.size() - 1), + OrigArgs, TPL->getDepth()) + ? TriState::kYes + : TriState::kNo; + + if (IsDefaulted != TriState::kYes) + break; + Args = Args.drop_back(); + } return Args; } @@ -2098,12 +2117,13 @@ template static void printTo(raw_ostream &OS, ArrayRef Args, const PrintingPolicy &Policy, - const TemplateParameterList *TPL, bool IsPack, unsigned ParmIndex) { + const TemplateParameterList *TPL, bool IsPack, unsigned ParmIndex, + const ClassTemplateSpecializationDecl *CTSD) { // Drop trailing template arguments that match default arguments. if (TPL && Policy.SuppressDefaultTemplateArgs && !Policy.PrintCanonicalTypes && !Args.empty() && !IsPack && Args.size() <= TPL->size()) { - Args = dropDefaultedArguments(Args, Policy, TPL); + Args = dropDefaultedArguments(Args, Policy, TPL, CTSD); } const char *Comma = Policy.MSVCFormatting ? "," : ", "; @@ -2121,7 +2141,7 @@ if (Argument.pack_size() && !FirstArg) OS << Comma; printTo(ArgOS, Argument.getPackAsArray(), Policy, TPL, - /*IsPack*/ true, ParmIndex); + /*IsPack*/ true, ParmIndex, CTSD); } else { if (!FirstArg) OS << Comma; @@ -2170,14 +2190,21 @@ ArrayRef Args, const PrintingPolicy &Policy, const TemplateParameterList *TPL) { - printTo(OS, Args, Policy, TPL, /*isPack*/ false, /*parmIndex*/ 0); + printTo(OS, Args, Policy, TPL, /*isPack*/ false, /*parmIndex*/ 0, nullptr); } void clang::printTemplateArgumentList(raw_ostream &OS, ArrayRef Args, const PrintingPolicy &Policy, const TemplateParameterList *TPL) { - printTo(OS, Args, Policy, TPL, /*isPack*/ false, /*parmIndex*/ 0); + printTo(OS, Args, Policy, TPL, /*isPack*/ false, /*parmIndex*/ 0, nullptr); +} + +void clang::printTemplateArgumentList( + raw_ostream &OS, ArrayRef Args, + const PrintingPolicy &Policy, const TemplateParameterList *TPL, + const clang::ClassTemplateSpecializationDecl *CTSD) { + printTo(OS, Args, Policy, TPL, /*isPack*/ false, /*parmIndex*/ 0, CTSD); } std::string Qualifiers::getAsString() const { diff --git a/clang/unittests/AST/TypePrinterTest.cpp b/clang/unittests/AST/TypePrinterTest.cpp --- a/clang/unittests/AST/TypePrinterTest.cpp +++ b/clang/unittests/AST/TypePrinterTest.cpp @@ -127,3 +127,39 @@ Policy.EntireContentsOfLargeArray = true; })); } + +TEST(TypePrinter, TemplateParameterListWithCallback) { + constexpr char Code[] = R"cpp( + template + struct Foo { + }; + + Foo X; + )cpp"; + auto Matcher = classTemplateSpecializationDecl( + hasName("Foo"), has(cxxConstructorDecl( + has(parmVarDecl(hasType(qualType().bind("id"))))))); + + struct DefaulterCallback final : public PrintingCallbacks { + virtual TriState + IsTemplateArgumentDefaulted(clang::ClassTemplateSpecializationDecl const *, + size_t ArgIndex) const { + if (ArgIndex > 1) + return TriState::kYes; + + return TriState::kNo; + } + }; + + DefaulterCallback Callback; + + ASSERT_TRUE(PrintedTypeMatches(Code, {""}, Matcher, + R"(const Foo &)", + [&Callback](PrintingPolicy &Policy) { + Policy.SuppressDefaultTemplateArgs = true; + Policy.Callbacks = &Callback; + })); +}