-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang] Improve ast-dumper text printing of TemplateArgument #93431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
!CanonTA.structurallyEquals(TA)) { | ||||||
llvm::SmallString<128> CanonStr; | ||||||
{ | ||||||
llvm::raw_svector_ostream SS(CanonStr); | ||||||
CanonTA.print(PrintPolicy, SS, /*IncludeType=*/true); | ||||||
} | ||||||
if (CanonStr != Str) | ||||||
OS << ":'" << CanonStr << "'"; | ||||||
} | ||||||
mizvekov marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
|
||||||
const char *TextNodeDumper::getCommandName(unsigned CommandID) { | ||||||
if (Traits) | ||||||
return Traits->getCommandInfo(CommandID)->Name; | ||||||
|
@@ -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"; | ||||||
mizvekov marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
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) { | ||||||
|
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{{$}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how i feel about the quotes. It's particularly awkward in the char literal case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the quotes are what we already do for types, and I think the problem shows up there, though of course not as self-evident as here.
I don't think there is a formal solution besides to start escaping the string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, lets keep the quotes. I'm not a fan but it appears consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to fight over this but I feel like it makes less sense for integer literals and char literals. Expressions, type etc sure. Consistency for consistency sake while tempting does not feel convincing to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the problem is that it makes little sense to show the char as a char literal for this integral dumper.
This is a resolved argument, it's not supposed to print as-written anyway.
This dumper is a debugging aid and the most important things are showing the value and the type it had.
The suffixed literals are going to obscure any typedefs. The char literal is going to obscure the signed-ness of the char besides that.
The most helpful representation is the cast followed by the plain literal. Ie (uintptr_t)28999
I think we should have a mode in that literal printer for that.
This would remove the plain ugly case of the char literal, but still the single quotes can appear within the printed type of the literal, which can already happen anyway.
Shafik would that also alleviate your concern?