Skip to content

release/20.x: [clang] Forward TPL of NestedNameSpecifier #137806

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

Merged
merged 1 commit into from
May 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions clang/lib/AST/NestedNameSpecifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,13 +283,16 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,
case TypeSpec: {
const auto *Record =
dyn_cast_or_null<ClassTemplateSpecializationDecl>(getAsRecordDecl());
if (ResolveTemplateArguments && Record) {
const TemplateParameterList *TPL = nullptr;
if (Record) {
TPL = Record->getSpecializedTemplate()->getTemplateParameters();
if (ResolveTemplateArguments) {
// Print the type trait with resolved template parameters.
Record->printName(OS, Policy);
printTemplateArgumentList(
OS, Record->getTemplateArgs().asArray(), Policy,
Record->getSpecializedTemplate()->getTemplateParameters());
printTemplateArgumentList(OS, Record->getTemplateArgs().asArray(),
Policy, TPL);
break;
}
}
const Type *T = getAsType();

Expand All @@ -313,16 +316,16 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,
TemplateName::Qualified::None);

// Print the template argument list.
printTemplateArgumentList(OS, SpecType->template_arguments(),
InnerPolicy);
printTemplateArgumentList(OS, SpecType->template_arguments(), InnerPolicy,
TPL);
} else if (const auto *DepSpecType =
dyn_cast<DependentTemplateSpecializationType>(T)) {
// Print the template name without its corresponding
// nested-name-specifier.
OS << DepSpecType->getIdentifier()->getName();
// Print the template argument list.
printTemplateArgumentList(OS, DepSpecType->template_arguments(),
InnerPolicy);
InnerPolicy, TPL);
} else {
// Print the type normally
QualType(T, 0).print(OS, InnerPolicy);
Expand Down
96 changes: 96 additions & 0 deletions clang/unittests/Tooling/QualTypeNamesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,102 @@ TEST(QualTypeNameTest, InlineNamespace) {
TypeNameVisitor::Lang_CXX11);
}

TEST(QualTypeNameTest, TemplatedClass) {
std::unique_ptr<ASTUnit> AST =
tooling::buildASTFromCode("template <unsigned U1> struct A {\n"
" template <unsigned U2> struct B {};\n"
"};\n"
"template struct A<1>;\n"
"template struct A<2u>;\n"
"template struct A<1>::B<3>;\n"
"template struct A<2u>::B<4u>;\n");

auto &Context = AST->getASTContext();
auto &Policy = Context.getPrintingPolicy();
auto getFullyQualifiedName = [&](QualType QT) {
return TypeName::getFullyQualifiedName(QT, Context, Policy);
};

auto *A = Context.getTranslationUnitDecl()
->lookup(&Context.Idents.get("A"))
.find_first<ClassTemplateDecl>();
ASSERT_NE(A, nullptr);

// A has two explicit instantiations: A<1> and A<2u>
auto ASpec = A->spec_begin();
ASSERT_NE(ASpec, A->spec_end());
auto *A1 = *ASpec;
ASpec++;
ASSERT_NE(ASpec, A->spec_end());
auto *A2 = *ASpec;

// Their type names follow the records.
QualType A1RecordTy = Context.getRecordType(A1);
EXPECT_EQ(getFullyQualifiedName(A1RecordTy), "A<1>");
QualType A2RecordTy = Context.getRecordType(A2);
EXPECT_EQ(getFullyQualifiedName(A2RecordTy), "A<2U>");

// getTemplateSpecializationType() gives types that print the integral
// argument directly.
TemplateArgument Args1[] = {
{Context, llvm::APSInt::getUnsigned(1u), Context.UnsignedIntTy}};
QualType A1TemplateSpecTy =
Context.getTemplateSpecializationType(TemplateName(A), Args1, A1RecordTy);
EXPECT_EQ(A1TemplateSpecTy.getAsString(), "A<1>");

TemplateArgument Args2[] = {
{Context, llvm::APSInt::getUnsigned(2u), Context.UnsignedIntTy}};
QualType A2TemplateSpecTy =
Context.getTemplateSpecializationType(TemplateName(A), Args2, A2RecordTy);
EXPECT_EQ(A2TemplateSpecTy.getAsString(), "A<2>");

// Find A<1>::B and its specialization B<3>.
auto *A1B =
A1->lookup(&Context.Idents.get("B")).find_first<ClassTemplateDecl>();
ASSERT_NE(A1B, nullptr);
auto A1BSpec = A1B->spec_begin();
ASSERT_NE(A1BSpec, A1B->spec_end());
auto *A1B3 = *A1BSpec;
QualType A1B3RecordTy = Context.getRecordType(A1B3);
EXPECT_EQ(getFullyQualifiedName(A1B3RecordTy), "A<1>::B<3>");

// Construct A<1>::B<3> and check name.
TemplateArgument Args3[] = {
{Context, llvm::APSInt::getUnsigned(3u), Context.UnsignedIntTy}};
QualType A1B3TemplateSpecTy = Context.getTemplateSpecializationType(
TemplateName(A1B), Args3, A1B3RecordTy);
EXPECT_EQ(A1B3TemplateSpecTy.getAsString(), "B<3>");

NestedNameSpecifier *A1Nested = NestedNameSpecifier::Create(
Context, nullptr, false, A1TemplateSpecTy.getTypePtr());
QualType A1B3ElaboratedTy = Context.getElaboratedType(
ElaboratedTypeKeyword::None, A1Nested, A1B3TemplateSpecTy);
EXPECT_EQ(A1B3ElaboratedTy.getAsString(), "A<1>::B<3>");

// Find A<2u>::B and its specialization B<4u>.
auto *A2B =
A2->lookup(&Context.Idents.get("B")).find_first<ClassTemplateDecl>();
ASSERT_NE(A2B, nullptr);
auto A2BSpec = A2B->spec_begin();
ASSERT_NE(A2BSpec, A2B->spec_end());
auto *A2B4 = *A2BSpec;
QualType A2B4RecordTy = Context.getRecordType(A2B4);
EXPECT_EQ(getFullyQualifiedName(A2B4RecordTy), "A<2U>::B<4U>");

// Construct A<2>::B<4> and check name.
TemplateArgument Args4[] = {
{Context, llvm::APSInt::getUnsigned(4u), Context.UnsignedIntTy}};
QualType A2B4TemplateSpecTy = Context.getTemplateSpecializationType(
TemplateName(A2B), Args4, A2B4RecordTy);
EXPECT_EQ(A2B4TemplateSpecTy.getAsString(), "B<4>");

NestedNameSpecifier *A2Nested = NestedNameSpecifier::Create(
Context, nullptr, false, A2TemplateSpecTy.getTypePtr());
QualType A2B4ElaboratedTy = Context.getElaboratedType(
ElaboratedTypeKeyword::None, A2Nested, A2B4TemplateSpecTy);
EXPECT_EQ(A2B4ElaboratedTy.getAsString(), "A<2>::B<4>");
}

TEST(QualTypeNameTest, AnonStrucs) {
TypeNameVisitor AnonStrucs;
AnonStrucs.ExpectedQualTypeNames["a"] = "short";
Expand Down
Loading