420 lines · cpp
1//===- unittest/Tooling/QualTypeNameTest.cpp ------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/AST/QualTypeNames.h"10#include "TestVisitor.h"11using namespace clang;12 13namespace {14struct TypeNameVisitor : TestVisitor {15 llvm::StringMap<std::string> ExpectedQualTypeNames;16 bool WithGlobalNsPrefix = false;17 18 // ValueDecls are the least-derived decl with both a qualtype and a name.19 bool VisitValueDecl(ValueDecl *VD) override {20 std::string ExpectedName =21 ExpectedQualTypeNames.lookup(VD->getNameAsString());22 if (ExpectedName != "") {23 PrintingPolicy Policy(Context->getPrintingPolicy());24 Policy.SuppressScope = false;25 Policy.AnonymousTagLocations = true;26 Policy.PolishForDeclaration = true;27 Policy.SuppressUnwrittenScope = true;28 std::string ActualName = TypeName::getFullyQualifiedName(29 VD->getType(), *Context, Policy, WithGlobalNsPrefix);30 if (ExpectedName != ActualName) {31 // A custom message makes it much easier to see what declaration32 // failed compared to EXPECT_EQ.33 ADD_FAILURE() << "Typename::getFullyQualifiedName failed for "34 << VD->getQualifiedNameAsString() << std::endl35 << " Actual: " << ActualName << std::endl36 << " Expected: " << ExpectedName;37 }38 }39 return true;40 }41};42 43// named namespaces inside anonymous namespaces44 45TEST(QualTypeNameTest, Simple) {46 TypeNameVisitor Visitor;47 // Simple case to test the test framework itself.48 Visitor.ExpectedQualTypeNames["CheckInt"] = "int";49 50 // Keeping the names of the variables whose types we check unique51 // within the entire test--regardless of their own scope--makes it52 // easier to diagnose test failures.53 54 // Simple namespace qualifier55 Visitor.ExpectedQualTypeNames["CheckA"] = "A::B::Class0";56 // Lookup up the enclosing scopes, then down another one. (These57 // appear as elaborated type in the AST. In that case--even if58 // policy.SuppressScope = 0--qual_type.getAsString(policy) only59 // gives the name as it appears in the source, not the full name.60 Visitor.ExpectedQualTypeNames["CheckB"] = "A::B::C::Class1";61 // Template parameter expansion.62 Visitor.ExpectedQualTypeNames["CheckC"] =63 "A::B::Template0<A::B::C::MyInt, A::B::AnotherClass>";64 // Recursive template parameter expansion.65 Visitor.ExpectedQualTypeNames["CheckD"] =66 "A::B::Template0<A::B::Template1<A::B::C::MyInt, A::B::AnotherClass>, "67 "A::B::Template0<int, long>>";68 // Variadic Template expansion.69 Visitor.ExpectedQualTypeNames["CheckE"] =70 "A::Variadic<int, A::B::Template0<int, char>, "71 "A::B::Template1<int, long>, A::B::C::MyInt>";72 // Using declarations should be fully expanded.73 Visitor.ExpectedQualTypeNames["CheckF"] = "A::B::Class0";74 // Elements found within "using namespace foo;" should be fully75 // expanded.76 Visitor.ExpectedQualTypeNames["CheckG"] = "A::B::C::MyInt";77 // Type inside function78 Visitor.ExpectedQualTypeNames["CheckH"] = "struct X";79 // Anonymous Namespaces80 Visitor.ExpectedQualTypeNames["CheckI"] = "aClass";81 // Keyword inclusion with namespaces82 Visitor.ExpectedQualTypeNames["CheckJ"] = "struct A::aStruct";83 // Anonymous Namespaces nested in named namespaces and vice-versa.84 Visitor.ExpectedQualTypeNames["CheckK"] = "D::aStruct";85 // Namespace alias86 Visitor.ExpectedQualTypeNames["CheckL"] = "A::B::C::MyInt";87 Visitor.ExpectedQualTypeNames["non_dependent_type_var"] =88 "Foo<X>::non_dependent_type";89 Visitor.ExpectedQualTypeNames["AnEnumVar"] = "EnumScopeClass::AnEnum";90 Visitor.ExpectedQualTypeNames["AliasTypeVal"] = "A::B::C::InnerAlias<int>";91 Visitor.ExpectedQualTypeNames["AliasInnerTypeVal"] =92 "OuterTemplateClass<A::B::Class0>::Inner";93 Visitor.ExpectedQualTypeNames["CheckM"] = "const A::B::Class0 *";94 Visitor.ExpectedQualTypeNames["CheckN"] = "const X *";95 Visitor.ExpectedQualTypeNames["ttp_using"] =96 "OuterTemplateClass<A::B::Class0>";97 Visitor.ExpectedQualTypeNames["alias_of_template"] = "ABTemplate0IntInt";98 Visitor.runOver(99 "int CheckInt;\n"100 "template <typename T>\n"101 "class OuterTemplateClass { public: struct Inner {}; };\n"102 "namespace A {\n"103 " namespace B {\n"104 " class Class0 { };\n"105 " namespace C {\n"106 " typedef int MyInt;"107 " template <typename T>\n"108 " using InnerAlias = OuterTemplateClass<T>;\n"109 " InnerAlias<int> AliasTypeVal;\n"110 " InnerAlias<Class0>::Inner AliasInnerTypeVal;\n"111 " }\n"112 " template<class X, class Y> class Template0;"113 " template<class X, class Y> class Template1;"114 " typedef B::Class0 AnotherClass;\n"115 " void Function1(Template0<C::MyInt,\n"116 " AnotherClass> CheckC);\n"117 " void Function2(Template0<Template1<C::MyInt, AnotherClass>,\n"118 " Template0<int, long> > CheckD);\n"119 " void Function3(const B::Class0* CheckM);\n"120 " }\n"121 "template<typename... Values> class Variadic {};\n"122 "Variadic<int, B::Template0<int, char>, "123 " B::Template1<int, long>, "124 " B::C::MyInt > CheckE;\n"125 " namespace BC = B::C;\n"126 " BC::MyInt CheckL;\n"127 "}\n"128 "using A::B::Class0;\n"129 "void Function(Class0 CheckF);\n"130 "OuterTemplateClass<Class0> ttp_using;\n"131 "using ABTemplate0IntInt = A::B::Template0<int, int>;\n"132 "void Function(ABTemplate0IntInt alias_of_template);\n"133 "using namespace A::B::C;\n"134 "void Function(MyInt CheckG);\n"135 "void f() {\n"136 " struct X {} CheckH;\n"137 "}\n"138 "struct X;\n"139 "void f(const ::X* CheckN) {}\n"140 "namespace {\n"141 " class aClass {};\n"142 " aClass CheckI;\n"143 "}\n"144 "namespace A {\n"145 " struct aStruct {} CheckJ;\n"146 "}\n"147 "namespace {\n"148 " namespace D {\n"149 " namespace {\n"150 " class aStruct {};\n"151 " aStruct CheckK;\n"152 " }\n"153 " }\n"154 "}\n"155 "template<class T> struct Foo {\n"156 " typedef typename T::A dependent_type;\n"157 " typedef int non_dependent_type;\n"158 " dependent_type dependent_type_var;\n"159 " non_dependent_type non_dependent_type_var;\n"160 "};\n"161 "struct X { typedef int A; };"162 "Foo<X> var;"163 "void F() {\n"164 " var.dependent_type_var = 0;\n"165 "var.non_dependent_type_var = 0;\n"166 "}\n"167 "class EnumScopeClass {\n"168 "public:\n"169 " enum AnEnum { ZERO, ONE };\n"170 "};\n"171 "EnumScopeClass::AnEnum AnEnumVar;\n",172 TypeNameVisitor::Lang_CXX11);173}174 175TEST(QualTypeNameTest, Complex) {176 TypeNameVisitor Complex;177 Complex.ExpectedQualTypeNames["CheckTX"] = "B::TX";178 Complex.runOver(179 "namespace A {"180 " struct X {};"181 "}"182 "using A::X;"183 "namespace fake_std {"184 " template<class... Types > class tuple {};"185 "}"186 "namespace B {"187 " using fake_std::tuple;"188 " typedef tuple<X> TX;"189 " TX CheckTX;"190 " struct A { typedef int X; };"191 "}");192}193 194TEST(QualTypeNameTest, DoubleUsing) {195 TypeNameVisitor DoubleUsing;196 DoubleUsing.ExpectedQualTypeNames["direct"] = "a::A<0>";197 DoubleUsing.ExpectedQualTypeNames["indirect"] = "b::B";198 DoubleUsing.ExpectedQualTypeNames["double_indirect"] = "b::B";199 DoubleUsing.runOver(R"cpp(200 namespace a {201 template<int> class A {};202 A<0> direct;203 }204 namespace b {205 using B = ::a::A<0>;206 B indirect;207 }208 namespace b {209 using ::b::B;210 B double_indirect;211 }212 )cpp");213}214 215TEST(QualTypeNameTest, GlobalNsPrefix) {216 TypeNameVisitor GlobalNsPrefix;217 GlobalNsPrefix.WithGlobalNsPrefix = true;218 GlobalNsPrefix.ExpectedQualTypeNames["IntVal"] = "int";219 GlobalNsPrefix.ExpectedQualTypeNames["BoolVal"] = "bool";220 GlobalNsPrefix.ExpectedQualTypeNames["XVal"] = "::A::B::X";221 GlobalNsPrefix.ExpectedQualTypeNames["IntAliasVal"] = "::A::B::Alias<int>";222 GlobalNsPrefix.ExpectedQualTypeNames["ZVal"] = "::A::B::Y::Z";223 GlobalNsPrefix.ExpectedQualTypeNames["GlobalZVal"] = "::Z";224 GlobalNsPrefix.ExpectedQualTypeNames["CheckK"] = "D::aStruct";225 GlobalNsPrefix.ExpectedQualTypeNames["YZMPtr"] = "::A::B::X ::A::B::Y::Z::*";226 GlobalNsPrefix.runOver(227 "namespace A {\n"228 " namespace B {\n"229 " int IntVal;\n"230 " bool BoolVal;\n"231 " struct X {};\n"232 " X XVal;\n"233 " template <typename T> class CCC { };\n"234 " template <typename T>\n"235 " using Alias = CCC<T>;\n"236 " Alias<int> IntAliasVal;\n"237 " struct Y { struct Z { X YZIPtr; }; };\n"238 " Y::Z ZVal;\n"239 " X Y::Z::*YZMPtr;\n"240 " }\n"241 "}\n"242 "struct Z {};\n"243 "Z GlobalZVal;\n"244 "namespace {\n"245 " namespace D {\n"246 " namespace {\n"247 " class aStruct {};\n"248 " aStruct CheckK;\n"249 " }\n"250 " }\n"251 "}\n"252 );253}254 255TEST(QualTypeNameTest, InlineNamespace) {256 TypeNameVisitor InlineNamespace;257 InlineNamespace.ExpectedQualTypeNames["c"] = "B::C";258 InlineNamespace.runOver("inline namespace A {\n"259 " namespace B {\n"260 " class C {};\n"261 " }\n"262 "}\n"263 "using namespace A::B;\n"264 "C c;\n",265 TypeNameVisitor::Lang_CXX11);266}267 268TEST(QualTypeNameTest, TemplatedClass) {269 std::unique_ptr<ASTUnit> AST =270 tooling::buildASTFromCode("template <unsigned U1> struct A {\n"271 " template <unsigned U2> struct B {};\n"272 "};\n"273 "template struct A<1>;\n"274 "template struct A<2u>;\n"275 "template struct A<1>::B<3>;\n"276 "template struct A<2u>::B<4u>;\n");277 278 auto &Context = AST->getASTContext();279 auto &Policy = Context.getPrintingPolicy();280 auto getFullyQualifiedName = [&](QualType QT) {281 return TypeName::getFullyQualifiedName(QT, Context, Policy);282 };283 284 auto *A = Context.getTranslationUnitDecl()285 ->lookup(&Context.Idents.get("A"))286 .find_first<ClassTemplateDecl>();287 ASSERT_NE(A, nullptr);288 289 // A has two explicit instantiations: A<1> and A<2u>290 auto ASpec = A->spec_begin();291 ASSERT_NE(ASpec, A->spec_end());292 auto *A1 = *ASpec;293 ASpec++;294 ASSERT_NE(ASpec, A->spec_end());295 auto *A2 = *ASpec;296 297 // Their type names follow the records.298 CanQualType A1RecordTy = Context.getCanonicalTagType(A1);299 EXPECT_EQ(getFullyQualifiedName(A1RecordTy), "A<1>");300 CanQualType A2RecordTy = Context.getCanonicalTagType(A2);301 EXPECT_EQ(getFullyQualifiedName(A2RecordTy), "A<2U>");302 303 // getTemplateSpecializationType() gives types that print the integral304 // argument directly.305 TemplateArgument Args1[] = {306 {Context, llvm::APSInt::getUnsigned(1u), Context.UnsignedIntTy}};307 QualType A1TemplateSpecTy = Context.getTemplateSpecializationType(308 ElaboratedTypeKeyword::None, TemplateName(A), Args1, Args1, A1RecordTy);309 EXPECT_EQ(A1TemplateSpecTy.getAsString(), "A<1>");310 311 TemplateArgument Args2[] = {312 {Context, llvm::APSInt::getUnsigned(2u), Context.UnsignedIntTy}};313 QualType A2TemplateSpecTy = Context.getTemplateSpecializationType(314 ElaboratedTypeKeyword::None, TemplateName(A), Args2, Args2, A2RecordTy);315 EXPECT_EQ(A2TemplateSpecTy.getAsString(), "A<2>");316 317 // Find A<1>::B and its specialization B<3>.318 auto *A1B =319 A1->lookup(&Context.Idents.get("B")).find_first<ClassTemplateDecl>();320 ASSERT_NE(A1B, nullptr);321 auto A1BSpec = A1B->spec_begin();322 ASSERT_NE(A1BSpec, A1B->spec_end());323 auto *A1B3 = *A1BSpec;324 CanQualType A1B3RecordTy = Context.getCanonicalTagType(A1B3);325 EXPECT_EQ(getFullyQualifiedName(A1B3RecordTy), "A<1>::B<3>");326 327 // Construct A<1>::B<3> and check name.328 NestedNameSpecifier A1Nested(A1TemplateSpecTy.getTypePtr());329 TemplateName A1B3Name = Context.getQualifiedTemplateName(330 A1Nested, /*TemplateKeyword=*/false, TemplateName(A1B));331 332 TemplateArgument Args3[] = {333 {Context, llvm::APSInt::getUnsigned(3u), Context.UnsignedIntTy}};334 QualType A1B3TemplateSpecTy = Context.getTemplateSpecializationType(335 ElaboratedTypeKeyword::None, A1B3Name, Args3, Args3, A1B3RecordTy);336 EXPECT_EQ(A1B3TemplateSpecTy.getAsString(), "A<1>::B<3>");337 338 // Find A<2u>::B and its specialization B<4u>.339 auto *A2B =340 A2->lookup(&Context.Idents.get("B")).find_first<ClassTemplateDecl>();341 ASSERT_NE(A2B, nullptr);342 auto A2BSpec = A2B->spec_begin();343 ASSERT_NE(A2BSpec, A2B->spec_end());344 auto *A2B4 = *A2BSpec;345 CanQualType A2B4RecordTy = Context.getCanonicalTagType(A2B4);346 EXPECT_EQ(getFullyQualifiedName(A2B4RecordTy), "A<2U>::B<4U>");347 348 // Construct A<2>::B<4> and check name.349 NestedNameSpecifier A2Nested(A2TemplateSpecTy.getTypePtr());350 TemplateName A2B4Name = Context.getQualifiedTemplateName(351 A2Nested, /*TemplateKeyword=*/false, TemplateName(A2B));352 353 TemplateArgument Args4[] = {354 {Context, llvm::APSInt::getUnsigned(4u), Context.UnsignedIntTy}};355 QualType A2B4TemplateSpecTy = Context.getTemplateSpecializationType(356 ElaboratedTypeKeyword::None, A2B4Name, Args4, Args4, A2B4RecordTy);357 EXPECT_EQ(A2B4TemplateSpecTy.getAsString(), "A<2>::B<4>");358}359 360TEST(QualTypeNameTest, AnonStrucs) {361 TypeNameVisitor AnonStrucs;362 AnonStrucs.ExpectedQualTypeNames["a"] = "short";363 AnonStrucs.ExpectedQualTypeNames["un_in_st_1"] =364 "union (unnamed struct at input.cc:1:1)::(unnamed union at "365 "input.cc:2:27)";366 AnonStrucs.ExpectedQualTypeNames["b"] = "short";367 AnonStrucs.ExpectedQualTypeNames["un_in_st_2"] =368 "union (unnamed struct at input.cc:1:1)::(unnamed union at "369 "input.cc:5:27)";370 AnonStrucs.ExpectedQualTypeNames["anon_st"] =371 "struct (unnamed struct at input.cc:1:1)";372 AnonStrucs.runOver(R"(struct {373 union {374 short a;375 } un_in_st_1;376 union {377 short b;378 } un_in_st_2;379 } anon_st;)");380}381 382TEST(QualTypeNameTest, ConstUsing) {383 TypeNameVisitor ConstUsing;384 ConstUsing.ExpectedQualTypeNames["param1"] = "const A::S &";385 ConstUsing.ExpectedQualTypeNames["param2"] = "const A::S";386 ConstUsing.runOver(R"(namespace A {387 class S {};388 }389 using ::A::S;390 void foo(const S& param1, const S param2);)");391}392 393TEST(QualTypeNameTest, NullableAttributesWithGlobalNs) {394 TypeNameVisitor Visitor;395 Visitor.WithGlobalNsPrefix = true;396 Visitor.ExpectedQualTypeNames["param1"] = "::std::unique_ptr<int> _Nullable";397 Visitor.ExpectedQualTypeNames["param2"] = "::std::unique_ptr<int> _Nonnull";398 Visitor.ExpectedQualTypeNames["param3"] =399 "::std::unique_ptr< ::std::unique_ptr<int> _Nullable> _Nonnull";400 Visitor.ExpectedQualTypeNames["param4"] =401 "::std::unique_ptr<int> _Nullable const *";402 Visitor.ExpectedQualTypeNames["param5"] =403 "::std::unique_ptr<int> _Nullable const *";404 Visitor.ExpectedQualTypeNames["param6"] =405 "::std::unique_ptr<int> _Nullable const *";406 Visitor.runOver(R"(namespace std {407 template<class T> class unique_ptr {};408 }409 void foo(410 std::unique_ptr<int> _Nullable param1,411 _Nonnull std::unique_ptr<int> param2,412 std::unique_ptr<std::unique_ptr<int> _Nullable> _Nonnull param3,413 const std::unique_ptr<int> _Nullable *param4,414 _Nullable std::unique_ptr<int> const *param5,415 std::unique_ptr<int> _Nullable const *param6416 );417 )");418}419} // end anonymous namespace420