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, @@ -5462,6 +5463,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); @@ -2078,23 +2078,52 @@ return false; } +template +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)); + + TriState IsDefaulted = PrintingCallbacks::TriState::kUnknown; + auto const *Callbacks = Policy.Callbacks; + + while (!Args.empty()) { + 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; +} + 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()) { - 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())) - Args = Args.drop_back(); + Args = dropDefaultedArguments(Args, Policy, TPL, CTSD); } const char *Comma = Policy.MSVCFormatting ? "," : ", "; @@ -2112,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; @@ -2161,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,42 @@ Policy.EntireContentsOfLargeArray = true; })); } + +TEST(TypePrinter, TemplateParameterListWithCallback) { + constexpr char Code[] = R"cpp( + template + struct Str { + constexpr Str(char const (&s)[N]) { __builtin_memcpy(value, s, N); } + char value[N]; + }; + template class ASCII {}; + + ASCII<"this nontype template argument is too long to print"> x; + )cpp"; + + auto Matcher = classTemplateSpecializationDecl( + hasName("ASCII"), has(cxxConstructorDecl( + isMoveConstructor(), + 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, {"-std=c++20"}, Matcher, + R"(ASCII{"this nontype template argument is [...]"}, int> &&)", + [&Callback](PrintingPolicy &Policy) { + Policy.SuppressDefaultTemplateArgs = true; + Policy.Callbacks = &Callback; + })); +}