[clang] Qualify non-dependent types of a class template with the class declar… (PR #67566)
Luca Di sera via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 27 09:54:30 PDT 2023
https://github.com/diseraluca updated https://github.com/llvm/llvm-project/pull/67566
>From 34b4e7939c88627ce433c96cdcc5e54d71ada94d Mon Sep 17 00:00:00 2001
From: Luca Di Sera <luca.disera at qt.io>
Date: Wed, 27 Sep 2023 15:20:22 +0200
Subject: [PATCH 1/2] Qualify non-dependent types of a class template with the
class declaration
When
`clang::TypeName::getFullyQualifiedType`/`clang::TypeName::getFullyQualifiedName`
encounter a non-dependent type/type alias that is defined under a templated
class/struct, it qualifies the type/type alias under a specialization of
the templated class, if any was declared.
That is, in:
```
template<typename T>
class Foo {
using Bar = T;
}
using Baz = Foo<int>;
```
Usages of `Foo::Bar` will be qualified as `Foo<int>::Bar`.
When the `using Baz = Foo<int>;` line is removed, as there are would be no
specialization encountered in the translation unit, usages of
`Foo::Bar` would instead be qualified as `Foo<T>::Bar`.
When multiple specializations are present, the one that is chosen is the
first one that is encountered; due to the current implementation of the
behavior and the fact that iterating the specialization of a
`ClassTemplateDecl` respects the order in which the specializations were
stored in.
That is, if we were to add a reference to some other specialization in
the above example, so that it would be parsed before the current `using
Baz = Foo<int>;`, say:
```
template<typename T>
class Foo {
using Bar = T;
}
using Bat = Foo<double>;
using Baz = Foo<int>;
```
Then usages of `Foo::Bar` would be qualified as `Foo<double>::Bar`
instead of `Foo<int>::Bar`.
Should the same declaration be added, instead, after the `using Baz =
Foo<int>;` line, then the qualification would remain consistent with our
previous version.
To provide a more consistent behavior, that avoids changing the output
when relatively unrelated declarations are present in the translation
unit,
`clang::TypeName::getFullyQualifiedType`/`clang::TypeName::getFullyQualifiedName`
will now qualify such types using the original templated declaration
instead of one of the specializations.
---
clang/lib/AST/QualTypeNames.cpp | 21 -----
clang/unittests/Tooling/QualTypeNamesTest.cpp | 83 +++++++++----------
2 files changed, 40 insertions(+), 64 deletions(-)
diff --git a/clang/lib/AST/QualTypeNames.cpp b/clang/lib/AST/QualTypeNames.cpp
index 7557336f0aafa88..32fba2ab887d27c 100644
--- a/clang/lib/AST/QualTypeNames.cpp
+++ b/clang/lib/AST/QualTypeNames.cpp
@@ -272,27 +272,6 @@ static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
const auto *Outer = dyn_cast_or_null<NamedDecl>(DC);
const auto *OuterNS = dyn_cast_or_null<NamespaceDecl>(DC);
if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) {
- if (const auto *CxxDecl = dyn_cast<CXXRecordDecl>(DC)) {
- if (ClassTemplateDecl *ClassTempl =
- CxxDecl->getDescribedClassTemplate()) {
- // We are in the case of a type(def) that was declared in a
- // class template but is *not* type dependent. In clang, it
- // gets attached to the class template declaration rather than
- // any specific class template instantiation. This result in
- // 'odd' fully qualified typename:
- //
- // vector<_Tp,_Alloc>::size_type
- //
- // Make the situation is 'useable' but looking a bit odd by
- // picking a random instance as the declaring context.
- if (ClassTempl->spec_begin() != ClassTempl->spec_end()) {
- Decl = *(ClassTempl->spec_begin());
- Outer = dyn_cast<NamedDecl>(Decl);
- OuterNS = dyn_cast<NamespaceDecl>(Decl);
- }
- }
- }
-
if (OuterNS) {
return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);
} else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
diff --git a/clang/unittests/Tooling/QualTypeNamesTest.cpp b/clang/unittests/Tooling/QualTypeNamesTest.cpp
index 686d189cf69eb2f..18286f9e74921dd 100644
--- a/clang/unittests/Tooling/QualTypeNamesTest.cpp
+++ b/clang/unittests/Tooling/QualTypeNamesTest.cpp
@@ -85,7 +85,7 @@ TEST(QualTypeNameTest, Simple) {
// Namespace alias
Visitor.ExpectedQualTypeNames["CheckL"] = "A::B::C::MyInt";
Visitor.ExpectedQualTypeNames["non_dependent_type_var"] =
- "Foo<X>::non_dependent_type";
+ "Foo<T>::non_dependent_type";
Visitor.ExpectedQualTypeNames["AnEnumVar"] = "EnumScopeClass::AnEnum";
Visitor.ExpectedQualTypeNames["AliasTypeVal"] = "A::B::C::InnerAlias<int>";
Visitor.ExpectedQualTypeNames["AliasInnerTypeVal"] =
@@ -175,20 +175,19 @@ TEST(QualTypeNameTest, Simple) {
TEST(QualTypeNameTest, Complex) {
TypeNameVisitor Complex;
Complex.ExpectedQualTypeNames["CheckTX"] = "B::TX";
- Complex.runOver(
- "namespace A {"
- " struct X {};"
- "}"
- "using A::X;"
- "namespace fake_std {"
- " template<class... Types > class tuple {};"
- "}"
- "namespace B {"
- " using fake_std::tuple;"
- " typedef tuple<X> TX;"
- " TX CheckTX;"
- " struct A { typedef int X; };"
- "}");
+ Complex.runOver("namespace A {"
+ " struct X {};"
+ "}"
+ "using A::X;"
+ "namespace fake_std {"
+ " template<class... Types > class tuple {};"
+ "}"
+ "namespace B {"
+ " using fake_std::tuple;"
+ " typedef tuple<X> TX;"
+ " TX CheckTX;"
+ " struct A { typedef int X; };"
+ "}");
}
TEST(QualTypeNameTest, DoubleUsing) {
@@ -223,33 +222,31 @@ TEST(QualTypeNameTest, GlobalNsPrefix) {
GlobalNsPrefix.ExpectedQualTypeNames["GlobalZVal"] = "::Z";
GlobalNsPrefix.ExpectedQualTypeNames["CheckK"] = "D::aStruct";
GlobalNsPrefix.ExpectedQualTypeNames["YZMPtr"] = "::A::B::X ::A::B::Y::Z::*";
- GlobalNsPrefix.runOver(
- "namespace A {\n"
- " namespace B {\n"
- " int IntVal;\n"
- " bool BoolVal;\n"
- " struct X {};\n"
- " X XVal;\n"
- " template <typename T> class CCC { };\n"
- " template <typename T>\n"
- " using Alias = CCC<T>;\n"
- " Alias<int> IntAliasVal;\n"
- " struct Y { struct Z { X YZIPtr; }; };\n"
- " Y::Z ZVal;\n"
- " X Y::Z::*YZMPtr;\n"
- " }\n"
- "}\n"
- "struct Z {};\n"
- "Z GlobalZVal;\n"
- "namespace {\n"
- " namespace D {\n"
- " namespace {\n"
- " class aStruct {};\n"
- " aStruct CheckK;\n"
- " }\n"
- " }\n"
- "}\n"
- );
+ GlobalNsPrefix.runOver("namespace A {\n"
+ " namespace B {\n"
+ " int IntVal;\n"
+ " bool BoolVal;\n"
+ " struct X {};\n"
+ " X XVal;\n"
+ " template <typename T> class CCC { };\n"
+ " template <typename T>\n"
+ " using Alias = CCC<T>;\n"
+ " Alias<int> IntAliasVal;\n"
+ " struct Y { struct Z { X YZIPtr; }; };\n"
+ " Y::Z ZVal;\n"
+ " X Y::Z::*YZMPtr;\n"
+ " }\n"
+ "}\n"
+ "struct Z {};\n"
+ "Z GlobalZVal;\n"
+ "namespace {\n"
+ " namespace D {\n"
+ " namespace {\n"
+ " class aStruct {};\n"
+ " aStruct CheckK;\n"
+ " }\n"
+ " }\n"
+ "}\n");
}
TEST(QualTypeNameTest, InlineNamespace) {
@@ -297,4 +294,4 @@ TEST(QualTypeNameTest, ConstUsing) {
using ::A::S;
void foo(const S& param1, const S param2);)");
}
-} // end anonymous namespace
+} // end anonymous namespace
>From d306cc701c19bbca3e160171d23100e3bdf570fa Mon Sep 17 00:00:00 2001
From: Luca Di Sera <luca.disera at qt.io>
Date: Wed, 27 Sep 2023 15:20:22 +0200
Subject: [PATCH 2/2] Qualify non-dependent types of a class template with the
class declaration
When
`clang::TypeName::getFullyQualifiedType`/`clang::TypeName::getFullyQualifiedName`
encounter a non-dependent type/type alias that is defined under a templated
class/struct, it qualifies the type/type alias under a specialization of
the templated class, if any was declared.
That is, in:
```
template<typename T>
class Foo {
using Bar = T;
}
using Baz = Foo<int>;
```
Usages of `Foo::Bar` will be qualified as `Foo<int>::Bar`.
When the `using Baz = Foo<int>;` line is removed, as there are would be no
specialization encountered in the translation unit, usages of
`Foo::Bar` would instead be qualified as `Foo<T>::Bar`.
When multiple specializations are present, the one that is chosen is the
first one that is encountered; due to the current implementation of the
behavior and the fact that iterating the specialization of a
`ClassTemplateDecl` respects the order in which the specializations were
stored in.
That is, if we were to add a reference to some other specialization in
the above example, so that it would be parsed before the current `using
Baz = Foo<int>;`, say:
```
template<typename T>
class Foo {
using Bar = T;
}
using Bat = Foo<double>;
using Baz = Foo<int>;
```
Then usages of `Foo::Bar` would be qualified as `Foo<double>::Bar`
instead of `Foo<int>::Bar`.
Should the same declaration be added, instead, after the `using Baz =
Foo<int>;` line, then the qualification would remain consistent with our
previous version.
To provide a more consistent behavior, that avoids changing the output
when relatively unrelated declarations are present in the translation
unit,
`clang::TypeName::getFullyQualifiedType`/`clang::TypeName::getFullyQualifiedName`
will now qualify such types using the original templated declaration
instead of one of the specializations.
---
clang/lib/AST/QualTypeNames.cpp | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/clang/lib/AST/QualTypeNames.cpp b/clang/lib/AST/QualTypeNames.cpp
index 32fba2ab887d27c..6ed5c9c3b2dee24 100644
--- a/clang/lib/AST/QualTypeNames.cpp
+++ b/clang/lib/AST/QualTypeNames.cpp
@@ -272,22 +272,22 @@ static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
const auto *Outer = dyn_cast_or_null<NamedDecl>(DC);
const auto *OuterNS = dyn_cast_or_null<NamespaceDecl>(DC);
if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) {
- if (OuterNS) {
- return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);
- } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
- return createNestedNameSpecifier(
- Ctx, TD, FullyQualified, WithGlobalNsPrefix);
- } else if (isa<TranslationUnitDecl>(Outer)) {
- // Context is the TU. Nothing needs to be done.
- return nullptr;
- } else {
- // Decl's context was neither the TU, a namespace, nor a
- // TagDecl, which means it is a type local to a scope, and not
- // accessible at the end of the TU.
- return nullptr;
- }
+ if (OuterNS) {
+ return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);
+ } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
+ return createNestedNameSpecifier(Ctx, TD, FullyQualified,
+ WithGlobalNsPrefix);
+ } else if (isa<TranslationUnitDecl>(Outer)) {
+ // Context is the TU. Nothing needs to be done.
+ return nullptr;
+ } else {
+ // Decl's context was neither the TU, a namespace, nor a
+ // TagDecl, which means it is a type local to a scope, and not
+ // accessible at the end of the TU.
+ return nullptr;
+ }
} else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
- return NestedNameSpecifier::GlobalSpecifier(Ctx);
+ return NestedNameSpecifier::GlobalSpecifier(Ctx);
}
return nullptr;
}
More information about the cfe-commits
mailing list