[clang] Qualify non-dependent types of a class template with the class declar… (PR #67566)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 27 08:02:10 PDT 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
<details>
<summary>Changes</summary>
…ation
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/67566.diff
2 Files Affected:
- (modified) clang/lib/AST/QualTypeNames.cpp (-21)
- (modified) clang/unittests/Tooling/QualTypeNamesTest.cpp (+40-43)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/67566
More information about the cfe-commits
mailing list