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-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "clang/AST/QualTypeNames.h" |
10 | #include "TestVisitor.h" |
11 | using namespace clang; |
12 | |
13 | namespace { |
14 | struct 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(Key: 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 | QT: VD->getType(), Ctx: *Context, Policy, WithGlobalNsPrefix); |
30 | if (ExpectedName != ActualName) { |
31 | // A custom message makes it much easier to see what declaration |
32 | // failed compared to EXPECT_EQ. |
33 | ADD_FAILURE() << "Typename::getFullyQualifiedName failed for " |
34 | << VD->getQualifiedNameAsString() << std::endl |
35 | << " Actual: " << ActualName << std::endl |
36 | << " Expected: " << ExpectedName; |
37 | } |
38 | } |
39 | return true; |
40 | } |
41 | }; |
42 | |
43 | // named namespaces inside anonymous namespaces |
44 | |
45 | TEST(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 unique |
51 | // within the entire test--regardless of their own scope--makes it |
52 | // easier to diagnose test failures. |
53 | |
54 | // Simple namespace qualifier |
55 | Visitor.ExpectedQualTypeNames["CheckA" ] = "A::B::Class0" ; |
56 | // Lookup up the enclosing scopes, then down another one. (These |
57 | // appear as elaborated type in the AST. In that case--even if |
58 | // policy.SuppressScope = 0--qual_type.getAsString(policy) only |
59 | // 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 fully |
75 | // expanded. |
76 | Visitor.ExpectedQualTypeNames["CheckG" ] = "A::B::C::MyInt" ; |
77 | // Type inside function |
78 | Visitor.ExpectedQualTypeNames["CheckH" ] = "struct X" ; |
79 | // Anonymous Namespaces |
80 | Visitor.ExpectedQualTypeNames["CheckI" ] = "aClass" ; |
81 | // Keyword inclusion with namespaces |
82 | Visitor.ExpectedQualTypeNames["CheckJ" ] = "struct A::aStruct" ; |
83 | // Anonymous Namespaces nested in named namespaces and vice-versa. |
84 | Visitor.ExpectedQualTypeNames["CheckK" ] = "D::aStruct" ; |
85 | // Namespace alias |
86 | 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 | |
175 | TEST(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 | |
194 | TEST(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 | |
215 | TEST(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 | |
255 | TEST(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 | |
268 | TEST(QualTypeNameTest, TemplatedClass) { |
269 | std::unique_ptr<ASTUnit> AST = |
270 | tooling::buildASTFromCode(Code: "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, Ctx: Context, Policy); |
282 | }; |
283 | |
284 | auto *A = Context.getTranslationUnitDecl() |
285 | ->lookup(&Context.Idents.get(Name: "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 | QualType A1RecordTy = Context.getRecordType(Decl: A1); |
299 | EXPECT_EQ(getFullyQualifiedName(A1RecordTy), "A<1>" ); |
300 | QualType A2RecordTy = Context.getRecordType(Decl: A2); |
301 | EXPECT_EQ(getFullyQualifiedName(A2RecordTy), "A<2U>" ); |
302 | |
303 | // getTemplateSpecializationType() gives types that print the integral |
304 | // argument directly. |
305 | TemplateArgument Args1[] = { |
306 | {Context, llvm::APSInt::getUnsigned(X: 1u), Context.UnsignedIntTy}}; |
307 | QualType A1TemplateSpecTy = Context.getTemplateSpecializationType( |
308 | TemplateName(A), Args1, Args1, A1RecordTy); |
309 | EXPECT_EQ(A1TemplateSpecTy.getAsString(), "A<1>" ); |
310 | |
311 | TemplateArgument Args2[] = { |
312 | {Context, llvm::APSInt::getUnsigned(X: 2u), Context.UnsignedIntTy}}; |
313 | QualType A2TemplateSpecTy = Context.getTemplateSpecializationType( |
314 | 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(Name: "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 | QualType A1B3RecordTy = Context.getRecordType(Decl: A1B3); |
325 | EXPECT_EQ(getFullyQualifiedName(A1B3RecordTy), "A<1>::B<3>" ); |
326 | |
327 | // Construct A<1>::B<3> and check name. |
328 | TemplateArgument Args3[] = { |
329 | {Context, llvm::APSInt::getUnsigned(X: 3u), Context.UnsignedIntTy}}; |
330 | QualType A1B3TemplateSpecTy = Context.getTemplateSpecializationType( |
331 | TemplateName(A1B), Args3, Args3, A1B3RecordTy); |
332 | EXPECT_EQ(A1B3TemplateSpecTy.getAsString(), "B<3>" ); |
333 | |
334 | NestedNameSpecifier *A1Nested = NestedNameSpecifier::Create( |
335 | Context, Prefix: nullptr, T: A1TemplateSpecTy.getTypePtr()); |
336 | QualType A1B3ElaboratedTy = Context.getElaboratedType( |
337 | Keyword: ElaboratedTypeKeyword::None, NNS: A1Nested, NamedType: A1B3TemplateSpecTy); |
338 | EXPECT_EQ(A1B3ElaboratedTy.getAsString(), "A<1>::B<3>" ); |
339 | |
340 | // Find A<2u>::B and its specialization B<4u>. |
341 | auto *A2B = |
342 | A2->lookup(&Context.Idents.get(Name: "B" )).find_first<ClassTemplateDecl>(); |
343 | ASSERT_NE(A2B, nullptr); |
344 | auto A2BSpec = A2B->spec_begin(); |
345 | ASSERT_NE(A2BSpec, A2B->spec_end()); |
346 | auto *A2B4 = *A2BSpec; |
347 | QualType A2B4RecordTy = Context.getRecordType(Decl: A2B4); |
348 | EXPECT_EQ(getFullyQualifiedName(A2B4RecordTy), "A<2U>::B<4U>" ); |
349 | |
350 | // Construct A<2>::B<4> and check name. |
351 | TemplateArgument Args4[] = { |
352 | {Context, llvm::APSInt::getUnsigned(X: 4u), Context.UnsignedIntTy}}; |
353 | QualType A2B4TemplateSpecTy = Context.getTemplateSpecializationType( |
354 | TemplateName(A2B), Args4, Args4, A2B4RecordTy); |
355 | EXPECT_EQ(A2B4TemplateSpecTy.getAsString(), "B<4>" ); |
356 | |
357 | NestedNameSpecifier *A2Nested = NestedNameSpecifier::Create( |
358 | Context, Prefix: nullptr, T: A2TemplateSpecTy.getTypePtr()); |
359 | QualType A2B4ElaboratedTy = Context.getElaboratedType( |
360 | Keyword: ElaboratedTypeKeyword::None, NNS: A2Nested, NamedType: A2B4TemplateSpecTy); |
361 | EXPECT_EQ(A2B4ElaboratedTy.getAsString(), "A<2>::B<4>" ); |
362 | } |
363 | |
364 | TEST(QualTypeNameTest, AnonStrucs) { |
365 | TypeNameVisitor AnonStrucs; |
366 | AnonStrucs.ExpectedQualTypeNames["a" ] = "short" ; |
367 | AnonStrucs.ExpectedQualTypeNames["un_in_st_1" ] = |
368 | "union (unnamed struct at input.cc:1:1)::(unnamed union at " |
369 | "input.cc:2:27)" ; |
370 | AnonStrucs.ExpectedQualTypeNames["b" ] = "short" ; |
371 | AnonStrucs.ExpectedQualTypeNames["un_in_st_2" ] = |
372 | "union (unnamed struct at input.cc:1:1)::(unnamed union at " |
373 | "input.cc:5:27)" ; |
374 | AnonStrucs.ExpectedQualTypeNames["anon_st" ] = |
375 | "struct (unnamed struct at input.cc:1:1)" ; |
376 | AnonStrucs.runOver(R"(struct { |
377 | union { |
378 | short a; |
379 | } un_in_st_1; |
380 | union { |
381 | short b; |
382 | } un_in_st_2; |
383 | } anon_st;)" ); |
384 | } |
385 | |
386 | TEST(QualTypeNameTest, ConstUsing) { |
387 | TypeNameVisitor ConstUsing; |
388 | ConstUsing.ExpectedQualTypeNames["param1" ] = "const A::S &" ; |
389 | ConstUsing.ExpectedQualTypeNames["param2" ] = "const A::S" ; |
390 | ConstUsing.runOver(R"(namespace A { |
391 | class S {}; |
392 | } |
393 | using ::A::S; |
394 | void foo(const S& param1, const S param2);)" ); |
395 | } |
396 | |
397 | TEST(QualTypeNameTest, NullableAttributesWithGlobalNs) { |
398 | TypeNameVisitor Visitor; |
399 | Visitor.WithGlobalNsPrefix = true; |
400 | Visitor.ExpectedQualTypeNames["param1" ] = "::std::unique_ptr<int> _Nullable" ; |
401 | Visitor.ExpectedQualTypeNames["param2" ] = "::std::unique_ptr<int> _Nonnull" ; |
402 | Visitor.ExpectedQualTypeNames["param3" ] = |
403 | "::std::unique_ptr< ::std::unique_ptr<int> _Nullable> _Nonnull" ; |
404 | Visitor.ExpectedQualTypeNames["param4" ] = |
405 | "::std::unique_ptr<int> _Nullable const *" ; |
406 | Visitor.ExpectedQualTypeNames["param5" ] = |
407 | "::std::unique_ptr<int> _Nullable const *" ; |
408 | Visitor.ExpectedQualTypeNames["param6" ] = |
409 | "::std::unique_ptr<int> _Nullable const *" ; |
410 | Visitor.runOver(R"(namespace std { |
411 | template<class T> class unique_ptr {}; |
412 | } |
413 | void foo( |
414 | std::unique_ptr<int> _Nullable param1, |
415 | _Nonnull std::unique_ptr<int> param2, |
416 | std::unique_ptr<std::unique_ptr<int> _Nullable> _Nonnull param3, |
417 | const std::unique_ptr<int> _Nullable *param4, |
418 | _Nullable std::unique_ptr<int> const *param5, |
419 | std::unique_ptr<int> _Nullable const *param6 |
420 | ); |
421 | )" ); |
422 | } |
423 | } // end anonymous namespace |
424 | |