1//===-- TypeHierarchyTests.cpp ---------------------------*- C++ -*-------===//
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#include "AST.h"
9#include "Annotations.h"
10#include "Matchers.h"
11#include "ParsedAST.h"
12#include "TestFS.h"
13#include "TestTU.h"
14#include "XRefs.h"
15#include "clang/AST/DeclCXX.h"
16#include "clang/AST/DeclTemplate.h"
17#include "llvm/Support/Path.h"
18#include "gmock/gmock.h"
19#include "gtest/gtest.h"
20#include <vector>
21
22namespace clang {
23namespace clangd {
24namespace {
25
26using ::testing::AllOf;
27using ::testing::ElementsAre;
28using ::testing::Field;
29using ::testing::IsEmpty;
30using ::testing::Matcher;
31using ::testing::SizeIs;
32using ::testing::UnorderedElementsAre;
33
34// GMock helpers for matching TypeHierarchyItem.
35MATCHER_P(withName, N, "") { return arg.name == N; }
36MATCHER_P(withKind, Kind, "") { return arg.kind == Kind; }
37MATCHER_P(selectionRangeIs, R, "") { return arg.selectionRange == R; }
38template <class... ParentMatchers>
39::testing::Matcher<TypeHierarchyItem> parents(ParentMatchers... ParentsM) {
40 return Field(&TypeHierarchyItem::parents,
41 HasValue(UnorderedElementsAre(ParentsM...)));
42}
43template <class... ChildMatchers>
44::testing::Matcher<TypeHierarchyItem> children(ChildMatchers... ChildrenM) {
45 return Field(&TypeHierarchyItem::children,
46 HasValue(UnorderedElementsAre(ChildrenM...)));
47}
48// Note: "not resolved" is different from "resolved but empty"!
49MATCHER(parentsNotResolved, "") { return !arg.parents; }
50MATCHER(childrenNotResolved, "") { return !arg.children; }
51MATCHER_P(withResolveID, SID, "") { return arg.symbolID.str() == SID; }
52MATCHER_P(withResolveParents, M, "") {
53 return testing::ExplainMatchResult(M, arg.data.parents, result_listener);
54}
55
56TEST(FindRecordTypeAt, TypeOrVariable) {
57 Annotations Source(R"cpp(
58struct Ch^ild2 {
59 int c;
60};
61
62using A^lias = Child2;
63
64int main() {
65 Ch^ild2 ch^ild2;
66 ch^ild2.c = 1;
67}
68)cpp");
69
70 TestTU TU = TestTU::withCode(Code: Source.code());
71 auto AST = TU.build();
72
73 for (Position Pt : Source.points()) {
74 auto Records = findRecordTypeAt(AST, Pos: Pt);
75 ASSERT_THAT(Records, SizeIs(1));
76 EXPECT_EQ(&findDecl(AST, "Child2"),
77 static_cast<const NamedDecl *>(Records.front()));
78 }
79}
80
81TEST(FindRecordTypeAt, Nonexistent) {
82 Annotations Source(R"cpp(
83 int *wa^ldo;
84 )cpp");
85 TestTU TU = TestTU::withCode(Code: Source.code());
86 auto AST = TU.build();
87
88 for (Position Pt : Source.points()) {
89 auto Records = findRecordTypeAt(AST, Pos: Pt);
90 ASSERT_THAT(Records, SizeIs(0));
91 }
92}
93
94TEST(FindRecordTypeAt, Method) {
95 Annotations Source(R"cpp(
96struct Child2 {
97 void met^hod ();
98 void met^hod (int x);
99};
100
101int main() {
102 Child2 child2;
103 child2.met^hod(5);
104}
105)cpp");
106
107 TestTU TU = TestTU::withCode(Code: Source.code());
108 auto AST = TU.build();
109
110 for (Position Pt : Source.points()) {
111 auto Records = findRecordTypeAt(AST, Pos: Pt);
112 ASSERT_THAT(Records, SizeIs(1));
113 EXPECT_EQ(&findDecl(AST, "Child2"),
114 static_cast<const NamedDecl *>(Records.front()));
115 }
116}
117
118TEST(FindRecordTypeAt, Field) {
119 Annotations Source(R"cpp(
120struct Child2 {
121 int fi^eld;
122};
123
124int main() {
125 Child2 child2;
126 child2.fi^eld = 5;
127}
128)cpp");
129
130 TestTU TU = TestTU::withCode(Code: Source.code());
131 auto AST = TU.build();
132
133 for (Position Pt : Source.points()) {
134 // A field does not unambiguously specify a record type
135 // (possible associated record types could be the field's type,
136 // or the type of the record that the field is a member of).
137 EXPECT_THAT(findRecordTypeAt(AST, Pt), SizeIs(0));
138 }
139}
140
141TEST(TypeParents, SimpleInheritance) {
142 Annotations Source(R"cpp(
143struct Parent {
144 int a;
145};
146
147struct Child1 : Parent {
148 int b;
149};
150
151struct Child2 : Child1 {
152 int c;
153};
154)cpp");
155
156 TestTU TU = TestTU::withCode(Code: Source.code());
157 auto AST = TU.build();
158
159 const CXXRecordDecl *Parent =
160 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Parent"));
161 const CXXRecordDecl *Child1 =
162 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Child1"));
163 const CXXRecordDecl *Child2 =
164 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Child2"));
165
166 EXPECT_THAT(typeParents(Parent), ElementsAre());
167 EXPECT_THAT(typeParents(Child1), ElementsAre(Parent));
168 EXPECT_THAT(typeParents(Child2), ElementsAre(Child1));
169}
170
171TEST(TypeParents, MultipleInheritance) {
172 Annotations Source(R"cpp(
173struct Parent1 {
174 int a;
175};
176
177struct Parent2 {
178 int b;
179};
180
181struct Parent3 : Parent2 {
182 int c;
183};
184
185struct Child : Parent1, Parent3 {
186 int d;
187};
188)cpp");
189
190 TestTU TU = TestTU::withCode(Code: Source.code());
191 auto AST = TU.build();
192
193 const CXXRecordDecl *Parent1 =
194 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Parent1"));
195 const CXXRecordDecl *Parent2 =
196 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Parent2"));
197 const CXXRecordDecl *Parent3 =
198 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Parent3"));
199 const CXXRecordDecl *Child = dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Child"));
200
201 EXPECT_THAT(typeParents(Parent1), ElementsAre());
202 EXPECT_THAT(typeParents(Parent2), ElementsAre());
203 EXPECT_THAT(typeParents(Parent3), ElementsAre(Parent2));
204 EXPECT_THAT(typeParents(Child), ElementsAre(Parent1, Parent3));
205}
206
207TEST(TypeParents, ClassTemplate) {
208 Annotations Source(R"cpp(
209struct Parent {};
210
211template <typename T>
212struct Child : Parent {};
213)cpp");
214
215 TestTU TU = TestTU::withCode(Code: Source.code());
216 auto AST = TU.build();
217
218 const CXXRecordDecl *Parent =
219 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Parent"));
220 const CXXRecordDecl *Child =
221 dyn_cast<ClassTemplateDecl>(Val: &findDecl(AST, QName: "Child"))->getTemplatedDecl();
222
223 EXPECT_THAT(typeParents(Child), ElementsAre(Parent));
224}
225
226MATCHER_P(implicitSpecOf, ClassTemplate, "") {
227 const ClassTemplateSpecializationDecl *CTS =
228 dyn_cast<ClassTemplateSpecializationDecl>(arg);
229 return CTS &&
230 CTS->getSpecializedTemplate()->getTemplatedDecl() == ClassTemplate &&
231 CTS->getSpecializationKind() == TSK_ImplicitInstantiation;
232}
233
234// This is similar to findDecl(AST, QName), but supports using
235// a template-id as a query.
236const NamedDecl &findDeclWithTemplateArgs(ParsedAST &AST,
237 llvm::StringRef Query) {
238 return findDecl(AST, Filter: [&Query](const NamedDecl &ND) {
239 std::string QName;
240 llvm::raw_string_ostream OS(QName);
241 PrintingPolicy Policy(ND.getASTContext().getLangOpts());
242 // Use getNameForDiagnostic() which includes the template
243 // arguments in the printed name.
244 ND.getNameForDiagnostic(OS, Policy, /*Qualified=*/true);
245 OS.flush();
246 return QName == Query;
247 });
248}
249
250TEST(TypeParents, TemplateSpec1) {
251 Annotations Source(R"cpp(
252template <typename T>
253struct Parent {};
254
255template <>
256struct Parent<int> {};
257
258struct Child1 : Parent<float> {};
259
260struct Child2 : Parent<int> {};
261)cpp");
262
263 TestTU TU = TestTU::withCode(Code: Source.code());
264 auto AST = TU.build();
265
266 const CXXRecordDecl *Parent =
267 dyn_cast<ClassTemplateDecl>(Val: &findDecl(AST, QName: "Parent"))->getTemplatedDecl();
268 const CXXRecordDecl *ParentSpec =
269 dyn_cast<CXXRecordDecl>(Val: &findDeclWithTemplateArgs(AST, Query: "Parent<int>"));
270 const CXXRecordDecl *Child1 =
271 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Child1"));
272 const CXXRecordDecl *Child2 =
273 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Child2"));
274
275 EXPECT_THAT(typeParents(Child1), ElementsAre(implicitSpecOf(Parent)));
276 EXPECT_THAT(typeParents(Child2), ElementsAre(ParentSpec));
277}
278
279TEST(TypeParents, TemplateSpec2) {
280 Annotations Source(R"cpp(
281struct Parent {};
282
283template <typename T>
284struct Child {};
285
286template <>
287struct Child<int> : Parent {};
288)cpp");
289
290 TestTU TU = TestTU::withCode(Code: Source.code());
291 auto AST = TU.build();
292
293 const CXXRecordDecl *Parent =
294 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Parent"));
295 const CXXRecordDecl *Child =
296 dyn_cast<ClassTemplateDecl>(Val: &findDecl(AST, QName: "Child"))->getTemplatedDecl();
297 const CXXRecordDecl *ChildSpec =
298 dyn_cast<CXXRecordDecl>(Val: &findDeclWithTemplateArgs(AST, Query: "Child<int>"));
299
300 EXPECT_THAT(typeParents(Child), ElementsAre());
301 EXPECT_THAT(typeParents(ChildSpec), ElementsAre(Parent));
302}
303
304TEST(TypeParents, DependentBase) {
305 Annotations Source(R"cpp(
306template <typename T>
307struct Parent {};
308
309template <typename T>
310struct Child1 : Parent<T> {};
311
312template <typename T>
313struct Child2 : Parent<T>::Type {};
314
315template <typename T>
316struct Child3 : T {};
317)cpp");
318
319 TestTU TU = TestTU::withCode(Code: Source.code());
320 auto AST = TU.build();
321
322 const CXXRecordDecl *Parent =
323 dyn_cast<ClassTemplateDecl>(Val: &findDecl(AST, QName: "Parent"))->getTemplatedDecl();
324 const CXXRecordDecl *Child1 =
325 dyn_cast<ClassTemplateDecl>(Val: &findDecl(AST, QName: "Child1"))->getTemplatedDecl();
326 const CXXRecordDecl *Child2 =
327 dyn_cast<ClassTemplateDecl>(Val: &findDecl(AST, QName: "Child2"))->getTemplatedDecl();
328 const CXXRecordDecl *Child3 =
329 dyn_cast<ClassTemplateDecl>(Val: &findDecl(AST, QName: "Child3"))->getTemplatedDecl();
330
331 // For "Parent<T>", use the primary template as a best-effort guess.
332 EXPECT_THAT(typeParents(Child1), ElementsAre(Parent));
333 // For "Parent<T>::Type", there is nothing we can do.
334 EXPECT_THAT(typeParents(Child2), ElementsAre());
335 // Likewise for "T".
336 EXPECT_THAT(typeParents(Child3), ElementsAre());
337}
338
339TEST(TypeParents, IncompleteClass) {
340 Annotations Source(R"cpp(
341 class Incomplete;
342 )cpp");
343 TestTU TU = TestTU::withCode(Code: Source.code());
344 auto AST = TU.build();
345
346 const CXXRecordDecl *Incomplete =
347 dyn_cast<CXXRecordDecl>(Val: &findDecl(AST, QName: "Incomplete"));
348 EXPECT_THAT(typeParents(Incomplete), IsEmpty());
349}
350
351// Parts of getTypeHierarchy() are tested in more detail by the
352// FindRecordTypeAt.* and TypeParents.* tests above. This test exercises the
353// entire operation.
354TEST(TypeHierarchy, Parents) {
355 Annotations Source(R"cpp(
356struct $Parent1Def[[Parent1]] {
357 int a;
358};
359
360struct $Parent2Def[[Parent2]] {
361 int b;
362};
363
364struct $Parent3Def[[Parent3]] : Parent2 {
365 int c;
366};
367
368struct Ch^ild : Parent1, Parent3 {
369 int d;
370};
371
372int main() {
373 Ch^ild ch^ild;
374
375 ch^ild.a = 1;
376}
377)cpp");
378
379 TestTU TU = TestTU::withCode(Code: Source.code());
380 auto AST = TU.build();
381
382 for (Position Pt : Source.points()) {
383 // Set ResolveLevels to 0 because it's only used for Children;
384 // for Parents, getTypeHierarchy() always returns all levels.
385 auto Result = getTypeHierarchy(AST, Pos: Pt, /*ResolveLevels=*/Resolve: 0,
386 Direction: TypeHierarchyDirection::Parents);
387 ASSERT_THAT(Result, SizeIs(1));
388 EXPECT_THAT(
389 Result.front(),
390 AllOf(
391 withName("Child"), withKind(SymbolKind::Struct),
392 parents(AllOf(withName("Parent1"), withKind(SymbolKind::Struct),
393 selectionRangeIs(Source.range("Parent1Def")),
394 parents()),
395 AllOf(withName("Parent3"), withKind(SymbolKind::Struct),
396 selectionRangeIs(Source.range("Parent3Def")),
397 parents(AllOf(
398 withName("Parent2"), withKind(SymbolKind::Struct),
399 selectionRangeIs(Source.range("Parent2Def")),
400 parents()))))));
401 }
402}
403
404TEST(TypeHierarchy, RecursiveHierarchyUnbounded) {
405 Annotations Source(R"cpp(
406 template <int N>
407 struct $SDef[[S]] : S<N + 1> {};
408
409 S^<0> s; // error-ok
410 )cpp");
411
412 TestTU TU = TestTU::withCode(Code: Source.code());
413 TU.ExtraArgs.push_back(x: "-ftemplate-depth=10");
414 auto AST = TU.build();
415
416 // The compiler should produce a diagnostic for hitting the
417 // template instantiation depth.
418 ASSERT_FALSE(AST.getDiagnostics().empty());
419
420 // Make sure getTypeHierarchy() doesn't get into an infinite recursion.
421 // The parent is reported as "S" because "S<0>" is an invalid instantiation.
422 // We then iterate once more and find "S" again before detecting the
423 // recursion.
424 auto Result = getTypeHierarchy(AST, Pos: Source.points()[0], Resolve: 0,
425 Direction: TypeHierarchyDirection::Parents);
426 ASSERT_THAT(Result, SizeIs(1));
427 EXPECT_THAT(
428 Result.front(),
429 AllOf(withName("S<0>"), withKind(SymbolKind::Struct),
430 parents(
431 AllOf(withName("S"), withKind(SymbolKind::Struct),
432 selectionRangeIs(Source.range("SDef")),
433 parents(AllOf(withName("S"), withKind(SymbolKind::Struct),
434 selectionRangeIs(Source.range("SDef")),
435 parents()))))));
436}
437
438TEST(TypeHierarchy, RecursiveHierarchyBounded) {
439 Annotations Source(R"cpp(
440 template <int N>
441 struct $SDef[[S]] : S<N - 1> {};
442
443 template <>
444 struct S<0>{};
445
446 S$SRefConcrete^<2> s;
447
448 template <int N>
449 struct Foo {
450 S$SRefDependent^<N> s;
451 };)cpp");
452
453 TestTU TU = TestTU::withCode(Code: Source.code());
454 auto AST = TU.build();
455
456 // Make sure getTypeHierarchy() doesn't get into an infinite recursion
457 // for either a concrete starting point or a dependent starting point.
458 auto Result = getTypeHierarchy(AST, Pos: Source.point(Name: "SRefConcrete"), Resolve: 0,
459 Direction: TypeHierarchyDirection::Parents);
460 ASSERT_THAT(Result, SizeIs(1));
461 EXPECT_THAT(
462 Result.front(),
463 AllOf(withName("S<2>"), withKind(SymbolKind::Struct),
464 parents(AllOf(
465 withName("S<1>"), withKind(SymbolKind::Struct),
466 selectionRangeIs(Source.range("SDef")),
467 parents(AllOf(withName("S<0>"), withKind(SymbolKind::Struct),
468 parents()))))));
469 Result = getTypeHierarchy(AST, Pos: Source.point(Name: "SRefDependent"), Resolve: 0,
470 Direction: TypeHierarchyDirection::Parents);
471 ASSERT_THAT(Result, SizeIs(1));
472 EXPECT_THAT(
473 Result.front(),
474 AllOf(withName("S"), withKind(SymbolKind::Struct),
475 parents(AllOf(withName("S"), withKind(SymbolKind::Struct),
476 selectionRangeIs(Source.range("SDef")), parents()))));
477}
478
479TEST(TypeHierarchy, DeriveFromImplicitSpec) {
480 Annotations Source(R"cpp(
481 template <typename T>
482 struct Parent {};
483
484 struct Child1 : Parent<int> {};
485
486 struct Child2 : Parent<char> {};
487
488 Parent<int> Fo^o;
489 )cpp");
490
491 TestTU TU = TestTU::withCode(Code: Source.code());
492 auto AST = TU.build();
493 auto Index = TU.index();
494
495 auto Result = getTypeHierarchy(AST, Pos: Source.points()[0], Resolve: 2,
496 Direction: TypeHierarchyDirection::Children, Index: Index.get(),
497 TUPath: testPath(File: TU.Filename));
498 ASSERT_THAT(Result, SizeIs(1));
499 EXPECT_THAT(Result.front(),
500 AllOf(withName("Parent"), withKind(SymbolKind::Struct),
501 children(AllOf(withName("Child1"),
502 withKind(SymbolKind::Struct), children()),
503 AllOf(withName("Child2"),
504 withKind(SymbolKind::Struct), children()))));
505}
506
507TEST(TypeHierarchy, DeriveFromPartialSpec) {
508 Annotations Source(R"cpp(
509 template <typename T> struct Parent {};
510 template <typename T> struct Parent<T*> {};
511
512 struct Child : Parent<int*> {};
513
514 Parent<int> Fo^o;
515 )cpp");
516
517 TestTU TU = TestTU::withCode(Code: Source.code());
518 auto AST = TU.build();
519 auto Index = TU.index();
520
521 auto Result = getTypeHierarchy(AST, Pos: Source.points()[0], Resolve: 2,
522 Direction: TypeHierarchyDirection::Children, Index: Index.get(),
523 TUPath: testPath(File: TU.Filename));
524 ASSERT_THAT(Result, SizeIs(1));
525 EXPECT_THAT(Result.front(), AllOf(withName("Parent"),
526 withKind(SymbolKind::Struct), children()));
527}
528
529TEST(TypeHierarchy, DeriveFromTemplate) {
530 Annotations Source(R"cpp(
531 template <typename T>
532 struct Parent {};
533
534 template <typename T>
535 struct Child : Parent<T> {};
536
537 Parent<int> Fo^o;
538 )cpp");
539
540 TestTU TU = TestTU::withCode(Code: Source.code());
541 auto AST = TU.build();
542 auto Index = TU.index();
543
544 // FIXME: We'd like this to show the implicit specializations Parent<int>
545 // and Child<int>, but currently libIndex does not expose relationships
546 // between implicit specializations.
547 auto Result = getTypeHierarchy(AST, Pos: Source.points()[0], Resolve: 2,
548 Direction: TypeHierarchyDirection::Children, Index: Index.get(),
549 TUPath: testPath(File: TU.Filename));
550 ASSERT_THAT(Result, SizeIs(1));
551 EXPECT_THAT(Result.front(),
552 AllOf(withName("Parent"), withKind(SymbolKind::Struct),
553 children(AllOf(withName("Child"),
554 withKind(SymbolKind::Struct), children()))));
555}
556
557TEST(TypeHierarchy, Preamble) {
558 Annotations SourceAnnotations(R"cpp(
559struct Ch^ild : Parent {
560 int b;
561};)cpp");
562
563 Annotations HeaderInPreambleAnnotations(R"cpp(
564struct [[Parent]] {
565 int a;
566};)cpp");
567
568 TestTU TU = TestTU::withCode(Code: SourceAnnotations.code());
569 TU.HeaderCode = HeaderInPreambleAnnotations.code().str();
570 auto AST = TU.build();
571
572 std::vector<TypeHierarchyItem> Result = getTypeHierarchy(
573 AST, Pos: SourceAnnotations.point(), Resolve: 1, Direction: TypeHierarchyDirection::Parents);
574
575 ASSERT_THAT(Result, SizeIs(1));
576 EXPECT_THAT(
577 Result.front(),
578 AllOf(withName("Child"),
579 parents(AllOf(withName("Parent"),
580 selectionRangeIs(HeaderInPreambleAnnotations.range()),
581 parents()))));
582}
583
584SymbolID findSymbolIDByName(SymbolIndex *Index, llvm::StringRef Name,
585 llvm::StringRef TemplateArgs = "") {
586 SymbolID Result;
587 FuzzyFindRequest Request;
588 Request.Query = std::string(Name);
589 Request.AnyScope = true;
590 bool GotResult = false;
591 Index->fuzzyFind(Req: Request, Callback: [&](const Symbol &S) {
592 if (TemplateArgs == S.TemplateSpecializationArgs) {
593 EXPECT_FALSE(GotResult);
594 Result = S.ID;
595 GotResult = true;
596 }
597 });
598 EXPECT_TRUE(GotResult);
599 return Result;
600}
601
602std::vector<SymbolID> collectSubtypes(SymbolID Subject, SymbolIndex *Index) {
603 std::vector<SymbolID> Result;
604 RelationsRequest Req;
605 Req.Subjects.insert(V: Subject);
606 Req.Predicate = RelationKind::BaseOf;
607 Index->relations(Req,
608 Callback: [&Result](const SymbolID &Subject, const Symbol &Object) {
609 Result.push_back(x: Object.ID);
610 });
611 return Result;
612}
613
614TEST(Subtypes, SimpleInheritance) {
615 Annotations Source(R"cpp(
616struct Parent {};
617struct Child1a : Parent {};
618struct Child1b : Parent {};
619struct Child2 : Child1a {};
620)cpp");
621
622 TestTU TU = TestTU::withCode(Code: Source.code());
623 auto Index = TU.index();
624
625 SymbolID Parent = findSymbolIDByName(Index: Index.get(), Name: "Parent");
626 SymbolID Child1a = findSymbolIDByName(Index: Index.get(), Name: "Child1a");
627 SymbolID Child1b = findSymbolIDByName(Index: Index.get(), Name: "Child1b");
628 SymbolID Child2 = findSymbolIDByName(Index: Index.get(), Name: "Child2");
629
630 EXPECT_THAT(collectSubtypes(Parent, Index.get()),
631 UnorderedElementsAre(Child1a, Child1b));
632 EXPECT_THAT(collectSubtypes(Child1a, Index.get()), ElementsAre(Child2));
633}
634
635TEST(Subtypes, MultipleInheritance) {
636 Annotations Source(R"cpp(
637struct Parent1 {};
638struct Parent2 {};
639struct Parent3 : Parent2 {};
640struct Child : Parent1, Parent3 {};
641)cpp");
642
643 TestTU TU = TestTU::withCode(Code: Source.code());
644 auto Index = TU.index();
645
646 SymbolID Parent1 = findSymbolIDByName(Index: Index.get(), Name: "Parent1");
647 SymbolID Parent2 = findSymbolIDByName(Index: Index.get(), Name: "Parent2");
648 SymbolID Parent3 = findSymbolIDByName(Index: Index.get(), Name: "Parent3");
649 SymbolID Child = findSymbolIDByName(Index: Index.get(), Name: "Child");
650
651 EXPECT_THAT(collectSubtypes(Parent1, Index.get()), ElementsAre(Child));
652 EXPECT_THAT(collectSubtypes(Parent2, Index.get()), ElementsAre(Parent3));
653 EXPECT_THAT(collectSubtypes(Parent3, Index.get()), ElementsAre(Child));
654}
655
656TEST(Subtypes, ClassTemplate) {
657 Annotations Source(R"cpp(
658struct Parent {};
659
660template <typename T>
661struct Child : Parent {};
662)cpp");
663
664 TestTU TU = TestTU::withCode(Code: Source.code());
665 auto Index = TU.index();
666
667 SymbolID Parent = findSymbolIDByName(Index: Index.get(), Name: "Parent");
668 SymbolID Child = findSymbolIDByName(Index: Index.get(), Name: "Child");
669
670 EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child));
671}
672
673TEST(Subtypes, TemplateSpec1) {
674 Annotations Source(R"cpp(
675template <typename T>
676struct Parent {};
677
678template <>
679struct Parent<int> {};
680
681struct Child1 : Parent<float> {};
682
683struct Child2 : Parent<int> {};
684)cpp");
685
686 TestTU TU = TestTU::withCode(Code: Source.code());
687 auto Index = TU.index();
688
689 SymbolID Parent = findSymbolIDByName(Index: Index.get(), Name: "Parent");
690 SymbolID ParentSpec = findSymbolIDByName(Index: Index.get(), Name: "Parent", TemplateArgs: "<int>");
691 SymbolID Child1 = findSymbolIDByName(Index: Index.get(), Name: "Child1");
692 SymbolID Child2 = findSymbolIDByName(Index: Index.get(), Name: "Child2");
693
694 EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child1));
695 EXPECT_THAT(collectSubtypes(ParentSpec, Index.get()), ElementsAre(Child2));
696}
697
698TEST(Subtypes, TemplateSpec2) {
699 Annotations Source(R"cpp(
700struct Parent {};
701
702template <typename T>
703struct Child {};
704
705template <>
706struct Child<int> : Parent {};
707)cpp");
708
709 TestTU TU = TestTU::withCode(Code: Source.code());
710 auto Index = TU.index();
711
712 SymbolID Parent = findSymbolIDByName(Index: Index.get(), Name: "Parent");
713 SymbolID ChildSpec = findSymbolIDByName(Index: Index.get(), Name: "Child", TemplateArgs: "<int>");
714
715 EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(ChildSpec));
716}
717
718TEST(Subtypes, DependentBase) {
719 Annotations Source(R"cpp(
720template <typename T>
721struct Parent {};
722
723template <typename T>
724struct Child : Parent<T> {};
725)cpp");
726
727 TestTU TU = TestTU::withCode(Code: Source.code());
728 auto Index = TU.index();
729
730 SymbolID Parent = findSymbolIDByName(Index: Index.get(), Name: "Parent");
731 SymbolID Child = findSymbolIDByName(Index: Index.get(), Name: "Child");
732
733 EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child));
734}
735
736TEST(Subtypes, LazyResolution) {
737 Annotations Source(R"cpp(
738struct P^arent {};
739struct Child1 : Parent {};
740struct Child2a : Child1 {};
741struct Child2b : Child1 {};
742)cpp");
743
744 TestTU TU = TestTU::withCode(Code: Source.code());
745 auto AST = TU.build();
746 auto Index = TU.index();
747
748 auto Result = getTypeHierarchy(AST, Pos: Source.point(), /*ResolveLevels=*/Resolve: 1,
749 Direction: TypeHierarchyDirection::Children, Index: Index.get(),
750 TUPath: testPath(File: TU.Filename));
751 ASSERT_THAT(Result, SizeIs(1));
752 EXPECT_THAT(
753 Result.front(),
754 AllOf(withName("Parent"), withKind(SymbolKind::Struct), parents(),
755 children(AllOf(withName("Child1"), withKind(SymbolKind::Struct),
756 parentsNotResolved(), childrenNotResolved()))));
757
758 resolveTypeHierarchy(Item&: (*Result.front().children)[0], /*ResolveLevels=*/1,
759 Direction: TypeHierarchyDirection::Children, Index: Index.get());
760
761 EXPECT_THAT(
762 (*Result.front().children)[0],
763 AllOf(withName("Child1"), withKind(SymbolKind::Struct),
764 parentsNotResolved(),
765 children(AllOf(withName("Child2a"), withKind(SymbolKind::Struct),
766 parentsNotResolved(), childrenNotResolved()),
767 AllOf(withName("Child2b"), withKind(SymbolKind::Struct),
768 parentsNotResolved(), childrenNotResolved()))));
769}
770
771TEST(Standard, SubTypes) {
772 Annotations Source(R"cpp(
773struct Pare^nt1 {};
774struct Parent2 {};
775struct Child : Parent1, Parent2 {};
776)cpp");
777
778 TestTU TU = TestTU::withCode(Code: Source.code());
779 auto AST = TU.build();
780 auto Index = TU.index();
781
782 auto Result = getTypeHierarchy(AST, Pos: Source.point(), /*ResolveLevels=*/Resolve: 1,
783 Direction: TypeHierarchyDirection::Children, Index: Index.get(),
784 TUPath: testPath(File: TU.Filename));
785 ASSERT_THAT(Result, SizeIs(1));
786 auto Children = subTypes(Item: Result.front(), Index: Index.get());
787
788 // Make sure parents are populated when getting children.
789 // FIXME: This is partial.
790 EXPECT_THAT(
791 Children,
792 UnorderedElementsAre(
793 AllOf(withName("Child"),
794 withResolveParents(HasValue(UnorderedElementsAre(withResolveID(
795 getSymbolID(&findDecl(AST, "Parent1")).str())))))));
796}
797
798TEST(Standard, SuperTypes) {
799 Annotations Source(R"cpp(
800struct Parent {};
801struct Chil^d : Parent {};
802)cpp");
803
804 TestTU TU = TestTU::withCode(Code: Source.code());
805 auto AST = TU.build();
806 auto Index = TU.index();
807
808 auto Result = getTypeHierarchy(AST, Pos: Source.point(), /*ResolveLevels=*/Resolve: 1,
809 Direction: TypeHierarchyDirection::Children, Index: Index.get(),
810 TUPath: testPath(File: TU.Filename));
811 ASSERT_THAT(Result, SizeIs(1));
812 auto Parents = superTypes(Item: Result.front(), Index: Index.get());
813
814 EXPECT_THAT(Parents, HasValue(UnorderedElementsAre(
815 AllOf(withName("Parent"),
816 withResolveParents(HasValue(IsEmpty()))))));
817}
818} // namespace
819} // namespace clangd
820} // namespace clang
821

source code of clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp