Skip to content

Commit

Permalink
[clang] Improve ast-dumper text printing of TemplateArgument
Browse files Browse the repository at this point in the history
This improves and unifies our approach to printing all template
arguments.

The same approach to printing types is extended to all
TemplateArguments: A sugared version is printed in quotes,
followed by printing the canonical form, unless they would print
the same.

Special improvements are done to add more detail to template template
arguments.

It's planned in a future patch to use this improved TemplateName
printer for other places besides TemplateArguments.

Note: The sugared/desugared printing does not show up for
TemplateNames in tests yet, because we do a poor job of preserving
their type sugar. This will be improved in a future patch.
  • Loading branch information
mizvekov committed May 29, 2024
1 parent 1de6011 commit fabcce0
Show file tree
Hide file tree
Showing 17 changed files with 276 additions and 128 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ ABI Changes in This Version
AST Dumping Potentially Breaking Changes
----------------------------------------

- The text ast-dumper has improved printing of TemplateArguments.

Clang Frontend Potentially Breaking Changes
-------------------------------------------
- Removed support for constructing on-stack ``TemplateArgumentList``\ s; interfaces should instead
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/TextNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ class TextNodeDumper
void dumpTemplateSpecializationKind(TemplateSpecializationKind TSK);
void dumpNestedNameSpecifier(const NestedNameSpecifier *NNS);
void dumpConceptReference(const ConceptReference *R);
void dumpTemplateArgument(const TemplateArgument &TA);
void dumpTemplateName(TemplateName TN);

void dumpDeclRef(const Decl *D, StringRef Label = {});

Expand Down
104 changes: 90 additions & 14 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,26 @@ void TextNodeDumper::dumpDeclRef(const Decl *D, StringRef Label) {
});
}

void TextNodeDumper::dumpTemplateArgument(const TemplateArgument &TA) {
llvm::SmallString<128> Str;
{
llvm::raw_svector_ostream SS(Str);
TA.print(PrintPolicy, SS, /*IncludeType=*/true);
}
OS << " '" << Str << "'";

if (TemplateArgument CanonTA = Context->getCanonicalTemplateArgument(TA);
!CanonTA.structurallyEquals(TA)) {
llvm::SmallString<128> CanonStr;
{
llvm::raw_svector_ostream SS(CanonStr);
CanonTA.print(PrintPolicy, SS, /*IncludeType=*/true);
}
if (CanonStr != Str)
OS << ":'" << CanonStr << "'";
}
}

const char *TextNodeDumper::getCommandName(unsigned CommandID) {
if (Traits)
return Traits->getCommandInfo(CommandID)->Name;
Expand Down Expand Up @@ -1086,45 +1106,101 @@ void TextNodeDumper::VisitNullTemplateArgument(const TemplateArgument &) {

void TextNodeDumper::VisitTypeTemplateArgument(const TemplateArgument &TA) {
OS << " type";
dumpType(TA.getAsType());
dumpTemplateArgument(TA);
}

void TextNodeDumper::VisitDeclarationTemplateArgument(
const TemplateArgument &TA) {
OS << " decl";
dumpTemplateArgument(TA);
dumpDeclRef(TA.getAsDecl());
}

void TextNodeDumper::VisitNullPtrTemplateArgument(const TemplateArgument &) {
void TextNodeDumper::VisitNullPtrTemplateArgument(const TemplateArgument &TA) {
OS << " nullptr";
dumpTemplateArgument(TA);
}

void TextNodeDumper::VisitIntegralTemplateArgument(const TemplateArgument &TA) {
OS << " integral " << TA.getAsIntegral();
OS << " integral";
dumpTemplateArgument(TA);
}

void TextNodeDumper::dumpTemplateName(TemplateName TN) {
switch (TN.getKind()) {
case TemplateName::Template:
AddChild([=] { Visit(TN.getAsTemplateDecl()); });
return;
case TemplateName::UsingTemplate: {
const UsingShadowDecl *USD = TN.getAsUsingShadowDecl();
AddChild([=] { Visit(USD); });
AddChild("target", [=] { Visit(USD->getTargetDecl()); });
return;
}
case TemplateName::QualifiedTemplate: {
OS << " qualified";
const QualifiedTemplateName *QTN = TN.getAsQualifiedTemplateName();
if (QTN->hasTemplateKeyword())
OS << " keyword";
dumpNestedNameSpecifier(QTN->getQualifier());
dumpTemplateName(QTN->getUnderlyingTemplate());
return;
}
case TemplateName::DependentTemplate: {
OS << " dependent";
const DependentTemplateName *DTN = TN.getAsDependentTemplateName();
dumpNestedNameSpecifier(DTN->getQualifier());
return;
}
case TemplateName::SubstTemplateTemplateParm: {
OS << " subst";
const SubstTemplateTemplateParmStorage *STS =
TN.getAsSubstTemplateTemplateParm();
OS << " index " << STS->getIndex();
if (std::optional<unsigned int> PackIndex = STS->getPackIndex())
OS << " pack_index " << *PackIndex;
if (const TemplateTemplateParmDecl *P = STS->getParameter())
AddChild("parameter", [=] { Visit(P); });
dumpDeclRef(STS->getAssociatedDecl(), "associated");
AddChild("replacement", [=] { dumpTemplateName(STS->getReplacement()); });
return;
}
// FIXME: Implement these.
case TemplateName::OverloadedTemplate:
OS << " overloaded";
return;
case TemplateName::AssumedTemplate:
OS << " assumed";
return;
case TemplateName::SubstTemplateTemplateParmPack:
OS << " subst_pack";
return;
}
llvm_unreachable("Unexpected TemplateName Kind");
}

void TextNodeDumper::VisitTemplateTemplateArgument(const TemplateArgument &TA) {
if (TA.getAsTemplate().getKind() == TemplateName::UsingTemplate)
OS << " using";
OS << " template ";
TA.getAsTemplate().dump(OS);
OS << " template";
dumpTemplateArgument(TA);
dumpTemplateName(TA.getAsTemplate());
}

void TextNodeDumper::VisitTemplateExpansionTemplateArgument(
const TemplateArgument &TA) {
if (TA.getAsTemplateOrTemplatePattern().getKind() ==
TemplateName::UsingTemplate)
OS << " using";
OS << " template expansion ";
TA.getAsTemplateOrTemplatePattern().dump(OS);
OS << " template expansion";
dumpTemplateArgument(TA);
dumpTemplateName(TA.getAsTemplateOrTemplatePattern());
}

void TextNodeDumper::VisitExpressionTemplateArgument(const TemplateArgument &) {
void TextNodeDumper::VisitExpressionTemplateArgument(
const TemplateArgument &TA) {
OS << " expr";
dumpTemplateArgument(TA);
}

void TextNodeDumper::VisitPackTemplateArgument(const TemplateArgument &) {
void TextNodeDumper::VisitPackTemplateArgument(const TemplateArgument &TA) {
OS << " pack";
dumpTemplateArgument(TA);
}

static void dumpBasePath(raw_ostream &OS, const CastExpr *Node) {
Expand Down
25 changes: 14 additions & 11 deletions clang/test/AST/ast-dump-decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,21 +459,23 @@ namespace testClassTemplateDecl {

// CHECK: ClassTemplateDecl 0x{{.+}} <{{.+}}:[[@LINE-148]]:3, col:31> col:31 TestTemplateDefaultNonType{{$}}
// CHECK-NEXT: |-NonTypeTemplateParmDecl 0x{{.+}} <col:12, col:20> col:16 'int' depth 0 index 0 I{{$}}
// CHECK-NEXT: | `-TemplateArgument <col:20> expr{{$}}
// CHECK-NEXT: | `-TemplateArgument <col:20> expr '42'{{$}}
// CHECK-NEXT: | `-IntegerLiteral 0x{{.+}} <col:20> 'int' 42{{$}}
// CHECK-NEXT: `-CXXRecordDecl 0x{{.+}} <col:24, col:31> col:31 struct TestTemplateDefaultNonType{{$}}

// CHECK: ClassTemplateDecl 0x{{.+}} <{{.+}}:{{.*}}:3, col:68> col:68 TestTemplateTemplateDefaultType{{$}}
// CHECK-NEXT: |-TemplateTemplateParmDecl 0x{{.+}} <col:12, col:42> col:37 depth 0 index 0 TT{{$}}
// CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} <col:21> col:29 typename depth 1 index 0{{$}}
// CHECK-NEXT: | `-TemplateArgument <col:42> template TestClassTemplate{{$}}
// CHECK-NEXT: `-CXXRecordDecl 0x{{.+}} <col:61, col:68> col:68 struct TestTemplateTemplateDefaultType{{$}}
// CHECK-NEXT: | `-TemplateArgument <col:42> template 'testClassTemplateDecl::TestClassTemplate'{{$}}
// CHECK-NEXT: | `-ClassTemplateDecl 0x{{.+}} <line:{{.+}}:3, line:{{.+}}:3> line:{{.+}}:30 TestClassTemplate{{$}}
// CHECK-NEXT: `-CXXRecordDecl 0x{{.+}} <line:{{.*}}:61, col:68> col:68 struct TestTemplateTemplateDefaultType{{$}}

// CHECK: ClassTemplateDecl 0x{{.+}} prev 0x{{.+}} <{{.+}}:{{.*}}:3, col:82> col:48 TestTemplateTemplateDefaultType{{$}}
// CHECK-NEXT: |-TemplateTemplateParmDecl 0x{{.+}} <col:12, col:37> col:37 depth 0 index 0 TT{{$}}
// CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} <col:21> col:29 typename depth 1 index 0{{$}}
// CHECK-NEXT: | `-TemplateArgument <line:{{.*}}:42> template TestClassTemplate{{$}}
// CHECK-NEXT: | `-inherited from TemplateTemplateParm 0x{{.+}} 'TT'{{$}}
// CHECK-NEXT: | `-TemplateArgument <line:{{.*}}:42> template 'testClassTemplateDecl::TestClassTemplate'{{$}}
// CHECK-NEXT: | |-inherited from TemplateTemplateParm 0x{{.+}} 'TT'{{$}}
// CHECK-NEXT: | `-ClassTemplateDecl 0x{{.+}} <line:{{.+}}:3, line:{{.+}}:3> line:{{.+}}:30 TestClassTemplate
// CHECK-NEXT: `-CXXRecordDecl 0x{{.+}} prev 0x{{.+}} <line:{{.*}}:41, col:82> col:48 struct TestTemplateTemplateDefaultType definition{{$}}
// CHECK-NEXT: |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init{{$}}
// CHECK-NEXT: | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr{{$}}
Expand Down Expand Up @@ -683,7 +685,8 @@ namespace TestTemplateTemplateParmDecl {
// CHECK: FunctionTemplateDecl
// CHECK-NEXT: TemplateTemplateParmDecl{{.*}} T
// CHECK-NEXT: TemplateTypeParmDecl{{.*}} typename
// CHECK-NEXT: TemplateArgument{{.*}} template A
// CHECK-NEXT: TemplateArgument{{.*}} template 'TestTemplateTemplateParmDecl::A'
// CHECK-NEXT: ClassTemplateDecl {{.*}} A
// CHECK-NEXT: TemplateTemplateParmDecl{{.*}} ... U
// CHECK-NEXT: TemplateTypeParmDecl{{.*}} typename

Expand All @@ -710,12 +713,12 @@ namespace TestTemplateArgument {
template<int> class testIntegral { };
template class testIntegral<1>;
// CHECK: ClassTemplateSpecializationDecl{{.*}} class testIntegral
// CHECK: TemplateArgument{{.*}} integral 1
// CHECK: TemplateArgument{{.*}} integral '1'

template<template<typename> class> class testTemplate { };
template class testTemplate<A>;
// CHECK: ClassTemplateSpecializationDecl{{.*}} class testTemplate
// CHECK: TemplateArgument{{.*}} A
// CHECK: TemplateArgument{{.*}} 'TestTemplateArgument::A'

template<template<typename> class ...T> class C {
B<T...> testTemplateExpansion;
Expand All @@ -731,10 +734,10 @@ namespace TestTemplateArgument {
template<int, int ...> class testPack { };
template class testPack<0, 1, 2>;
// CHECK: ClassTemplateSpecializationDecl{{.*}} class testPack
// CHECK: TemplateArgument{{.*}} integral 0
// CHECK: TemplateArgument{{.*}} integral '0'
// CHECK-NEXT: TemplateArgument{{.*}} pack
// CHECK-NEXT: TemplateArgument{{.*}} integral 1
// CHECK-NEXT: TemplateArgument{{.*}} integral 2
// CHECK-NEXT: TemplateArgument{{.*}} integral '1'
// CHECK-NEXT: TemplateArgument{{.*}} integral '2'
}

namespace testUsingDecl {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ int test() {
// CHECK-NEXT: | | `-ReturnStmt [[ADDR_22:0x[a-z0-9]*]] <line:10:3, col:10>
// CHECK-NEXT: | | `-IntegerLiteral [[ADDR_23:0x[a-z0-9]*]] <col:10> 'int' 0
// CHECK-NEXT: | `-FunctionDecl [[ADDR_24:0x[a-z0-9]*]] <line:9:1, line:11:1> line:9:5 used also_before_mismatch 'int ({{.*}})'
// CHECK-NEXT: | |-TemplateArgument integral 0
// CHECK-NEXT: | |-TemplateArgument integral '0'
// CHECK-NEXT: | `-CompoundStmt [[ADDR_25:0x[a-z0-9]*]] <col:32, line:11:1>
// CHECK-NEXT: | `-ReturnStmt [[ADDR_26:0x[a-z0-9]*]] <line:10:3, col:10>
// CHECK-NEXT: | `-IntegerLiteral [[ADDR_23]] <col:10> 'int' 0
Expand Down Expand Up @@ -179,7 +179,7 @@ int test() {
// CHECK-NEXT: | | `-OMPDeclareVariantAttr [[ADDR_101:0x[a-z0-9]*]] <<invalid sloc>> Implicit implementation={extension(allow_templates)}
// CHECK-NEXT: | | `-DeclRefExpr [[ADDR_102:0x[a-z0-9]*]] <col:1> 'int ({{.*}})' {{.*}}Function [[ADDR_103:0x[a-z0-9]*]] 'only_def[implementation={extension(allow_templates)}]' 'int ({{.*}})'
// CHECK-NEXT: | `-FunctionDecl [[ADDR_104:0x[a-z0-9]*]] <col:1, col:18> col:5 used only_def 'int ({{.*}})'
// CHECK-NEXT: | |-TemplateArgument integral 0
// CHECK-NEXT: | |-TemplateArgument integral '0'
// CHECK-NEXT: | `-OMPDeclareVariantAttr [[ADDR_105:0x[a-z0-9]*]] <<invalid sloc>> Implicit implementation={extension(allow_templates)}
// CHECK-NEXT: | `-DeclRefExpr [[ADDR_106:0x[a-z0-9]*]] <col:1> 'int ({{.*}})' {{.*}}Function [[ADDR_107:0x[a-z0-9]*]] 'only_def[implementation={extension(allow_templates)}]' 'int ({{.*}})'
// CHECK-NEXT: |-FunctionTemplateDecl [[ADDR_108:0x[a-z0-9]*]] <line:37:1, line:40:1> line:38:1 only_def[implementation={extension(allow_templates)}]
Expand All @@ -189,7 +189,7 @@ int test() {
// CHECK-NEXT: | | `-ReturnStmt [[ADDR_110:0x[a-z0-9]*]] <line:39:3, col:10>
// CHECK-NEXT: | | `-IntegerLiteral [[ADDR_111:0x[a-z0-9]*]] <col:10> 'int' 0
// CHECK-NEXT: | `-FunctionDecl [[ADDR_107]] <line:38:1, line:40:1> line:38:1 only_def[implementation={extension(allow_templates)}] 'int ({{.*}})'
// CHECK-NEXT: | |-TemplateArgument integral 0
// CHECK-NEXT: | |-TemplateArgument integral '0'
// CHECK-NEXT: | `-CompoundStmt [[ADDR_112:0x[a-z0-9]*]] <col:20, line:40:1>
// CHECK-NEXT: | `-ReturnStmt [[ADDR_113:0x[a-z0-9]*]] <line:39:3, col:10>
// CHECK-NEXT: | `-IntegerLiteral [[ADDR_111]] <col:10> 'int' 0
Expand Down
54 changes: 54 additions & 0 deletions clang/test/AST/ast-dump-template-name.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// RUN: %clang_cc1 -std=c++26 -ast-dump -ast-dump-filter=Test %s | FileCheck %s

template <template <class> class TT> using N = TT<int>;

namespace qualified {
namespace foo {
template <class T> struct A;
} // namespace foo
using TestQualified = N<foo::A>;
} // namespace qualified

// CHECK: Dumping qualified::TestQualified:
// CHECK-NEXT: TypeAliasDecl
// CHECK-NEXT: `-ElaboratedType
// CHECK-NEXT: `-TemplateSpecializationType
// CHECK-NEXT: |-TemplateArgument template 'qualified::foo::A' qualified{{$}}
// CHECK-NEXT: | |-NestedNameSpecifier Namespace 0x{{.+}} 'foo'{{$}}
// CHECK-NEXT: | `-ClassTemplateDecl {{.+}} A{{$}}

namespace dependent {
template <class T> struct B {
using TestDependent = N<T::template X>;
};
} // namespace dependent

// CHECK: Dumping dependent::B::TestDependent:
// CHECK-NEXT: TypeAliasDecl
// CHECK-NEXT: `-ElaboratedType
// CHECK-NEXT: `-TemplateSpecializationType
// CHECK-NEXT: |-TemplateArgument template 'template X' dependent{{$}}
// CHECK-NEXT: | `-NestedNameSpecifier TypeSpec 'T'{{$}}

namespace subst {
template <class> struct A;

template <template <class> class TT> struct B {
template <template <class> class> struct C {};
using type = C<TT>;
};
using TestSubst = B<A>::type;
} // namespace subst

// CHECK: Dumping subst::TestSubst:
// CHECK-NEXT: TypeAliasDecl
// CHECK-NEXT: `-ElaboratedType
// CHECK-NEXT: `-TypedefType
// CHECK-NEXT: |-TypeAlias
// CHECK-NEXT: `-ElaboratedType
// CHECK-NEXT: `-TemplateSpecializationType
// CHECK-NEXT: |-TemplateArgument template 'subst::A' subst index 0
// CHECK-NEXT: | |-parameter: TemplateTemplateParmDecl {{.+}} depth 0 index 0 TT{{$}}
// CHECK-NEXT: | |-associated ClassTemplateSpecialization {{.+}} 'B'{{$}}
// CHECK-NEXT: | `-replacement:
// CHECK-NEXT: | `-ClassTemplateDecl {{.+}} A{{$}}
8 changes: 5 additions & 3 deletions clang/test/AST/ast-dump-using-template.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ using B = X<S>;
// CHECK: TypeAliasDecl
// CHECK-NEXT: `-ElaboratedType {{.*}} 'X<ns::S>' sugar
// CHECK-NEXT: `-TemplateSpecializationType {{.*}} 'X<ns::S>' sugar X
// CHECK-NEXT: |-TemplateArgument using template S
// CHECK-NEXT: `-RecordType {{.*}} 'X<ns::S>'
// CHECK-NEXT: `-ClassTemplateSpecialization {{.*}} 'X'
// CHECK-NEXT: |-TemplateArgument template 'ns::S'
// CHECK-NEXT: | |-UsingShadowDecl {{.*}} implicit ClassTemplate {{.*}} 'S'
// CHECK-NEXT: | `-target: ClassTemplateDecl {{.*}} S
// CHECK-NEXT: `-RecordType {{.*}} 'X<ns::S>'
// CHECK-NEXT: `-ClassTemplateSpecialization {{.*}} 'X'

// TemplateName in DeducedTemplateSpecializationType.
S DeducedTemplateSpecializationT(123);
Expand Down
6 changes: 3 additions & 3 deletions clang/test/AST/constraints-explicit-instantiation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ struct A {

// This checks that `canary1<1>` and `canaray2<2>` are instantiated, thus
// indirectly validating that the correct candidates of `A::f` were really
// instantiated each time.
// instantiated each time.
// The `static_assert`s validate we don't instantiate wrong candidates.

// CHECK:{{.*}}FunctionTemplateDecl {{.*}} canary1
// CHECK: {{.*}}TemplateArgument integral
// CHECK-SAME: {{1$}}
// CHECK-SAME: {{'1'$}}
template struct A<1>;

// CHECK: {{.*}}FunctionTemplateDecl {{.*}} canary2
// CHECK: {{.*}}TemplateArgument integral
// CHECK-SAME: {{2$}}
// CHECK-SAME: {{'2'$}}
template struct A<2>;

template struct A<3>;
Expand Down
2 changes: 1 addition & 1 deletion clang/test/OpenMP/align_clause_ast_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ int template_test() {
// DUMP: FunctionDecl {{.*}}run 'double ()'
// DUMP: TemplateArgument type 'double'
// DUMP: BuiltinType {{.*}}'double'
// DUMP: TemplateArgument integral 1
// DUMP: TemplateArgument integral '1U'
// DUMP: OMPAllocateDeclAttr {{.*}}Implicit OMPNullMemAlloc
// DUMP: ConstantExpr {{.*}}'unsigned int'
// DUMP: value: Int 1
Expand Down
2 changes: 1 addition & 1 deletion clang/test/OpenMP/generic_loop_ast_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
//PRINT: }
//DUMP: FunctionDecl{{.*}}templ_foo 'void (int)'
//DUMP: TemplateArgument type 'int'
//DUMP: TemplateArgument integral 2
//DUMP: TemplateArgument integral '2'
//DUMP: ParmVarDecl{{.*}}'int'
//DUMP: OMPSimdDirective
//DUMP: OMPCollapseClause
Expand Down
2 changes: 1 addition & 1 deletion clang/test/OpenMP/interop_ast_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ void fooTemp() {

//PRINT: #pragma omp interop init(prefer_type(3,4,"level_one"), target : interop_var)
//DUMP: FunctionDecl{{.*}}fooTemp
//DUMP: TemplateArgument integral 3
//DUMP: TemplateArgument integral '3'
//DUMP: OMPInteropDirective
//DUMP: OMPInitClause
//DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}'interop_var'
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaOpenACC/sub-array-ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ void Templ(int i){
// CHECK-NEXT: FunctionDecl{{.*}} Templ 'void (int)' implicit_instantiation
// CHECK-NEXT: TemplateArgument{{.*}} 'int'
// CHECK-NEXT: BuiltinType{{.*}} 'int'
// CHECK-NEXT: TemplateArgument integral 3
// CHECK-NEXT: TemplateArgument integral '3U'
// CHECK-NEXT: TemplateArgument decl
// CHECK-NEXT: Var{{.*}} 'CEArray' 'const int[5]'
// CHECK-NEXT: ParmVarDecl{{.*}} i 'int'
Expand Down
Loading

0 comments on commit fabcce0

Please sign in to comment.