1 //===-- TypeHierarchyTests.cpp ---------------------------*- C++ -*-------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
11 #include "ParsedAST.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"
26 using ::testing::AllOf
;
27 using ::testing::ElementsAre
;
28 using ::testing::Field
;
29 using ::testing::IsEmpty
;
30 using ::testing::Matcher
;
31 using ::testing::SizeIs
;
32 using ::testing::UnorderedElementsAre
;
34 // GMock helpers for matching TypeHierarchyItem.
35 MATCHER_P(withName
, N
, "") { return arg
.name
== N
; }
36 MATCHER_P(withKind
, Kind
, "") { return arg
.kind
== Kind
; }
37 MATCHER_P(selectionRangeIs
, R
, "") { return arg
.selectionRange
== R
; }
38 template <class... ParentMatchers
>
39 ::testing::Matcher
<TypeHierarchyItem
> parents(ParentMatchers
... ParentsM
) {
40 return Field(&TypeHierarchyItem::parents
,
41 HasValue(UnorderedElementsAre(ParentsM
...)));
43 template <class... ChildMatchers
>
44 ::testing::Matcher
<TypeHierarchyItem
> children(ChildMatchers
... ChildrenM
) {
45 return Field(&TypeHierarchyItem::children
,
46 HasValue(UnorderedElementsAre(ChildrenM
...)));
48 // Note: "not resolved" is different from "resolved but empty"!
49 MATCHER(parentsNotResolved
, "") { return !arg
.parents
; }
50 MATCHER(childrenNotResolved
, "") { return !arg
.children
; }
51 MATCHER_P(withResolveID
, SID
, "") { return arg
.symbolID
.str() == SID
; }
52 MATCHER_P(withResolveParents
, M
, "") {
53 return testing::ExplainMatchResult(M
, arg
.data
.parents
, result_listener
);
56 TEST(FindRecordTypeAt
, TypeOrVariable
) {
57 Annotations
Source(R
"cpp(
62 using A^lias = Child2;
70 TestTU TU
= TestTU::withCode(Source
.code());
71 auto AST
= TU
.build();
73 for (Position Pt
: Source
.points()) {
74 auto Records
= findRecordTypeAt(AST
, Pt
);
75 ASSERT_THAT(Records
, SizeIs(1));
76 EXPECT_EQ(&findDecl(AST
, "Child2"),
77 static_cast<const NamedDecl
*>(Records
.front()));
81 TEST(FindRecordTypeAt
, Nonexistent
) {
82 Annotations
Source(R
"cpp(
85 TestTU TU
= TestTU::withCode(Source
.code());
86 auto AST
= TU
.build();
88 for (Position Pt
: Source
.points()) {
89 auto Records
= findRecordTypeAt(AST
, Pt
);
90 ASSERT_THAT(Records
, SizeIs(0));
94 TEST(FindRecordTypeAt
, Method
) {
95 Annotations
Source(R
"cpp(
107 TestTU TU
= TestTU::withCode(Source
.code());
108 auto AST
= TU
.build();
110 for (Position Pt
: Source
.points()) {
111 auto Records
= findRecordTypeAt(AST
, Pt
);
112 ASSERT_THAT(Records
, SizeIs(1));
113 EXPECT_EQ(&findDecl(AST
, "Child2"),
114 static_cast<const NamedDecl
*>(Records
.front()));
118 TEST(FindRecordTypeAt
, Field
) {
119 Annotations
Source(R
"cpp(
130 TestTU TU
= TestTU::withCode(Source
.code());
131 auto AST
= TU
.build();
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));
141 TEST(TypeParents
, SimpleInheritance
) {
142 Annotations
Source(R
"cpp(
147 struct Child1 : Parent {
151 struct Child2 : Child1 {
156 TestTU TU
= TestTU::withCode(Source
.code());
157 auto AST
= TU
.build();
159 const CXXRecordDecl
*Parent
=
160 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Parent"));
161 const CXXRecordDecl
*Child1
=
162 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Child1"));
163 const CXXRecordDecl
*Child2
=
164 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Child2"));
166 EXPECT_THAT(typeParents(Parent
), ElementsAre());
167 EXPECT_THAT(typeParents(Child1
), ElementsAre(Parent
));
168 EXPECT_THAT(typeParents(Child2
), ElementsAre(Child1
));
171 TEST(TypeParents
, MultipleInheritance
) {
172 Annotations
Source(R
"cpp(
181 struct Parent3 : Parent2 {
185 struct Child : Parent1, Parent3 {
190 TestTU TU
= TestTU::withCode(Source
.code());
191 auto AST
= TU
.build();
193 const CXXRecordDecl
*Parent1
=
194 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Parent1"));
195 const CXXRecordDecl
*Parent2
=
196 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Parent2"));
197 const CXXRecordDecl
*Parent3
=
198 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Parent3"));
199 const CXXRecordDecl
*Child
= dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Child"));
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
));
207 TEST(TypeParents
, ClassTemplate
) {
208 Annotations
Source(R
"cpp(
211 template <typename T>
212 struct Child : Parent {};
215 TestTU TU
= TestTU::withCode(Source
.code());
216 auto AST
= TU
.build();
218 const CXXRecordDecl
*Parent
=
219 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Parent"));
220 const CXXRecordDecl
*Child
=
221 dyn_cast
<ClassTemplateDecl
>(&findDecl(AST
, "Child"))->getTemplatedDecl();
223 EXPECT_THAT(typeParents(Child
), ElementsAre(Parent
));
226 MATCHER_P(implicitSpecOf
, ClassTemplate
, "") {
227 const ClassTemplateSpecializationDecl
*CTS
=
228 dyn_cast
<ClassTemplateSpecializationDecl
>(arg
);
230 CTS
->getSpecializedTemplate()->getTemplatedDecl() == ClassTemplate
&&
231 CTS
->getSpecializationKind() == TSK_ImplicitInstantiation
;
234 // This is similar to findDecl(AST, QName), but supports using
235 // a template-id as a query.
236 const NamedDecl
&findDeclWithTemplateArgs(ParsedAST
&AST
,
237 llvm::StringRef Query
) {
238 return findDecl(AST
, [&Query
](const NamedDecl
&ND
) {
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);
246 return QName
== Query
;
250 TEST(TypeParents
, TemplateSpec1
) {
251 Annotations
Source(R
"cpp(
252 template <typename T>
256 struct Parent<int> {};
258 struct Child1 : Parent<float> {};
260 struct Child2 : Parent<int> {};
263 TestTU TU
= TestTU::withCode(Source
.code());
264 auto AST
= TU
.build();
266 const CXXRecordDecl
*Parent
=
267 dyn_cast
<ClassTemplateDecl
>(&findDecl(AST
, "Parent"))->getTemplatedDecl();
268 const CXXRecordDecl
*ParentSpec
=
269 dyn_cast
<CXXRecordDecl
>(&findDeclWithTemplateArgs(AST
, "Parent<int>"));
270 const CXXRecordDecl
*Child1
=
271 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Child1"));
272 const CXXRecordDecl
*Child2
=
273 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Child2"));
275 EXPECT_THAT(typeParents(Child1
), ElementsAre(implicitSpecOf(Parent
)));
276 EXPECT_THAT(typeParents(Child2
), ElementsAre(ParentSpec
));
279 TEST(TypeParents
, TemplateSpec2
) {
280 Annotations
Source(R
"cpp(
283 template <typename T>
287 struct Child<int> : Parent {};
290 TestTU TU
= TestTU::withCode(Source
.code());
291 auto AST
= TU
.build();
293 const CXXRecordDecl
*Parent
=
294 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Parent"));
295 const CXXRecordDecl
*Child
=
296 dyn_cast
<ClassTemplateDecl
>(&findDecl(AST
, "Child"))->getTemplatedDecl();
297 const CXXRecordDecl
*ChildSpec
=
298 dyn_cast
<CXXRecordDecl
>(&findDeclWithTemplateArgs(AST
, "Child<int>"));
300 EXPECT_THAT(typeParents(Child
), ElementsAre());
301 EXPECT_THAT(typeParents(ChildSpec
), ElementsAre(Parent
));
304 TEST(TypeParents
, DependentBase
) {
305 Annotations
Source(R
"cpp(
306 template <typename T>
309 template <typename T>
310 struct Child1 : Parent<T> {};
312 template <typename T>
313 struct Child2 : Parent<T>::Type {};
315 template <typename T>
316 struct Child3 : T {};
319 TestTU TU
= TestTU::withCode(Source
.code());
320 auto AST
= TU
.build();
322 const CXXRecordDecl
*Parent
=
323 dyn_cast
<ClassTemplateDecl
>(&findDecl(AST
, "Parent"))->getTemplatedDecl();
324 const CXXRecordDecl
*Child1
=
325 dyn_cast
<ClassTemplateDecl
>(&findDecl(AST
, "Child1"))->getTemplatedDecl();
326 const CXXRecordDecl
*Child2
=
327 dyn_cast
<ClassTemplateDecl
>(&findDecl(AST
, "Child2"))->getTemplatedDecl();
328 const CXXRecordDecl
*Child3
=
329 dyn_cast
<ClassTemplateDecl
>(&findDecl(AST
, "Child3"))->getTemplatedDecl();
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());
336 EXPECT_THAT(typeParents(Child3
), ElementsAre());
339 TEST(TypeParents
, IncompleteClass
) {
340 Annotations
Source(R
"cpp(
343 TestTU TU
= TestTU::withCode(Source
.code());
344 auto AST
= TU
.build();
346 const CXXRecordDecl
*Incomplete
=
347 dyn_cast
<CXXRecordDecl
>(&findDecl(AST
, "Incomplete"));
348 EXPECT_THAT(typeParents(Incomplete
), IsEmpty());
351 // Parts of getTypeHierarchy() are tested in more detail by the
352 // FindRecordTypeAt.* and TypeParents.* tests above. This test exercises the
354 TEST(TypeHierarchy
, Parents
) {
355 Annotations
Source(R
"cpp(
356 struct $Parent1Def[[Parent1]] {
360 struct $Parent2Def[[Parent2]] {
364 struct $Parent3Def[[Parent3]] : Parent2 {
368 struct Ch^ild : Parent1, Parent3 {
379 TestTU TU
= TestTU::withCode(Source
.code());
380 auto AST
= TU
.build();
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
, Pt
, /*ResolveLevels=*/0,
386 TypeHierarchyDirection::Parents
);
387 ASSERT_THAT(Result
, SizeIs(1));
391 withName("Child"), withKind(SymbolKind::Struct
),
392 parents(AllOf(withName("Parent1"), withKind(SymbolKind::Struct
),
393 selectionRangeIs(Source
.range("Parent1Def")),
395 AllOf(withName("Parent3"), withKind(SymbolKind::Struct
),
396 selectionRangeIs(Source
.range("Parent3Def")),
398 withName("Parent2"), withKind(SymbolKind::Struct
),
399 selectionRangeIs(Source
.range("Parent2Def")),
404 TEST(TypeHierarchy
, RecursiveHierarchyUnbounded
) {
405 Annotations
Source(R
"cpp(
407 struct $SDef[[S]] : S<N + 1> {};
412 TestTU TU
= TestTU::withCode(Source
.code());
413 TU
.ExtraArgs
.push_back("-ftemplate-depth=10");
414 auto AST
= TU
.build();
416 // The compiler should produce a diagnostic for hitting the
417 // template instantiation depth.
418 ASSERT_FALSE(AST
.getDiagnostics().empty());
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
424 auto Result
= getTypeHierarchy(AST
, Source
.points()[0], 0,
425 TypeHierarchyDirection::Parents
);
426 ASSERT_THAT(Result
, SizeIs(1));
429 AllOf(withName("S<0>"), withKind(SymbolKind::Struct
),
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")),
438 TEST(TypeHierarchy
, RecursiveHierarchyBounded
) {
439 Annotations
Source(R
"cpp(
441 struct $SDef[[S]] : S<N - 1> {};
446 S$SRefConcrete^<2> s;
450 S$SRefDependent^<N> s;
453 TestTU TU
= TestTU::withCode(Source
.code());
454 auto AST
= TU
.build();
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
, Source
.point("SRefConcrete"), 0,
459 TypeHierarchyDirection::Parents
);
460 ASSERT_THAT(Result
, SizeIs(1));
463 AllOf(withName("S<2>"), withKind(SymbolKind::Struct
),
465 withName("S<1>"), withKind(SymbolKind::Struct
),
466 selectionRangeIs(Source
.range("SDef")),
467 parents(AllOf(withName("S<0>"), withKind(SymbolKind::Struct
),
469 Result
= getTypeHierarchy(AST
, Source
.point("SRefDependent"), 0,
470 TypeHierarchyDirection::Parents
);
471 ASSERT_THAT(Result
, SizeIs(1));
474 AllOf(withName("S"), withKind(SymbolKind::Struct
),
475 parents(AllOf(withName("S"), withKind(SymbolKind::Struct
),
476 selectionRangeIs(Source
.range("SDef")), parents()))));
479 TEST(TypeHierarchy
, DeriveFromImplicitSpec
) {
480 Annotations
Source(R
"cpp(
481 template <typename T>
484 struct Child1 : Parent<int> {};
486 struct Child2 : Parent<char> {};
491 TestTU TU
= TestTU::withCode(Source
.code());
492 auto AST
= TU
.build();
493 auto Index
= TU
.index();
495 auto Result
= getTypeHierarchy(AST
, Source
.points()[0], 2,
496 TypeHierarchyDirection::Children
, Index
.get(),
497 testPath(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()))));
507 TEST(TypeHierarchy
, DeriveFromPartialSpec
) {
508 Annotations
Source(R
"cpp(
509 template <typename T> struct Parent {};
510 template <typename T> struct Parent<T*> {};
512 struct Child : Parent<int*> {};
517 TestTU TU
= TestTU::withCode(Source
.code());
518 auto AST
= TU
.build();
519 auto Index
= TU
.index();
521 auto Result
= getTypeHierarchy(AST
, Source
.points()[0], 2,
522 TypeHierarchyDirection::Children
, Index
.get(),
523 testPath(TU
.Filename
));
524 ASSERT_THAT(Result
, SizeIs(1));
525 EXPECT_THAT(Result
.front(), AllOf(withName("Parent"),
526 withKind(SymbolKind::Struct
), children()));
529 TEST(TypeHierarchy
, DeriveFromTemplate
) {
530 Annotations
Source(R
"cpp(
531 template <typename T>
534 template <typename T>
535 struct Child : Parent<T> {};
540 TestTU TU
= TestTU::withCode(Source
.code());
541 auto AST
= TU
.build();
542 auto Index
= TU
.index();
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
, Source
.points()[0], 2,
548 TypeHierarchyDirection::Children
, Index
.get(),
549 testPath(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()))));
557 TEST(TypeHierarchy
, Preamble
) {
558 Annotations
SourceAnnotations(R
"cpp(
559 struct Ch^ild : Parent {
563 Annotations
HeaderInPreambleAnnotations(R
"cpp(
568 TestTU TU
= TestTU::withCode(SourceAnnotations
.code());
569 TU
.HeaderCode
= HeaderInPreambleAnnotations
.code().str();
570 auto AST
= TU
.build();
572 std::vector
<TypeHierarchyItem
> Result
= getTypeHierarchy(
573 AST
, SourceAnnotations
.point(), 1, TypeHierarchyDirection::Parents
);
575 ASSERT_THAT(Result
, SizeIs(1));
578 AllOf(withName("Child"),
579 parents(AllOf(withName("Parent"),
580 selectionRangeIs(HeaderInPreambleAnnotations
.range()),
584 SymbolID
findSymbolIDByName(SymbolIndex
*Index
, llvm::StringRef Name
,
585 llvm::StringRef TemplateArgs
= "") {
587 FuzzyFindRequest Request
;
588 Request
.Query
= std::string(Name
);
589 Request
.AnyScope
= true;
590 bool GotResult
= false;
591 Index
->fuzzyFind(Request
, [&](const Symbol
&S
) {
592 if (TemplateArgs
== S
.TemplateSpecializationArgs
) {
593 EXPECT_FALSE(GotResult
);
598 EXPECT_TRUE(GotResult
);
602 std::vector
<SymbolID
> collectSubtypes(SymbolID Subject
, SymbolIndex
*Index
) {
603 std::vector
<SymbolID
> Result
;
604 RelationsRequest Req
;
605 Req
.Subjects
.insert(Subject
);
606 Req
.Predicate
= RelationKind::BaseOf
;
607 Index
->relations(Req
,
608 [&Result
](const SymbolID
&Subject
, const Symbol
&Object
) {
609 Result
.push_back(Object
.ID
);
614 TEST(Subtypes
, SimpleInheritance
) {
615 Annotations
Source(R
"cpp(
617 struct Child1a : Parent {};
618 struct Child1b : Parent {};
619 struct Child2 : Child1a {};
622 TestTU TU
= TestTU::withCode(Source
.code());
623 auto Index
= TU
.index();
625 SymbolID Parent
= findSymbolIDByName(Index
.get(), "Parent");
626 SymbolID Child1a
= findSymbolIDByName(Index
.get(), "Child1a");
627 SymbolID Child1b
= findSymbolIDByName(Index
.get(), "Child1b");
628 SymbolID Child2
= findSymbolIDByName(Index
.get(), "Child2");
630 EXPECT_THAT(collectSubtypes(Parent
, Index
.get()),
631 UnorderedElementsAre(Child1a
, Child1b
));
632 EXPECT_THAT(collectSubtypes(Child1a
, Index
.get()), ElementsAre(Child2
));
635 TEST(Subtypes
, MultipleInheritance
) {
636 Annotations
Source(R
"cpp(
639 struct Parent3 : Parent2 {};
640 struct Child : Parent1, Parent3 {};
643 TestTU TU
= TestTU::withCode(Source
.code());
644 auto Index
= TU
.index();
646 SymbolID Parent1
= findSymbolIDByName(Index
.get(), "Parent1");
647 SymbolID Parent2
= findSymbolIDByName(Index
.get(), "Parent2");
648 SymbolID Parent3
= findSymbolIDByName(Index
.get(), "Parent3");
649 SymbolID Child
= findSymbolIDByName(Index
.get(), "Child");
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
));
656 TEST(Subtypes
, ClassTemplate
) {
657 Annotations
Source(R
"cpp(
660 template <typename T>
661 struct Child : Parent {};
664 TestTU TU
= TestTU::withCode(Source
.code());
665 auto Index
= TU
.index();
667 SymbolID Parent
= findSymbolIDByName(Index
.get(), "Parent");
668 SymbolID Child
= findSymbolIDByName(Index
.get(), "Child");
670 EXPECT_THAT(collectSubtypes(Parent
, Index
.get()), ElementsAre(Child
));
673 TEST(Subtypes
, TemplateSpec1
) {
674 Annotations
Source(R
"cpp(
675 template <typename T>
679 struct Parent<int> {};
681 struct Child1 : Parent<float> {};
683 struct Child2 : Parent<int> {};
686 TestTU TU
= TestTU::withCode(Source
.code());
687 auto Index
= TU
.index();
689 SymbolID Parent
= findSymbolIDByName(Index
.get(), "Parent");
690 SymbolID ParentSpec
= findSymbolIDByName(Index
.get(), "Parent", "<int>");
691 SymbolID Child1
= findSymbolIDByName(Index
.get(), "Child1");
692 SymbolID Child2
= findSymbolIDByName(Index
.get(), "Child2");
694 EXPECT_THAT(collectSubtypes(Parent
, Index
.get()), ElementsAre(Child1
));
695 EXPECT_THAT(collectSubtypes(ParentSpec
, Index
.get()), ElementsAre(Child2
));
698 TEST(Subtypes
, TemplateSpec2
) {
699 Annotations
Source(R
"cpp(
702 template <typename T>
706 struct Child<int> : Parent {};
709 TestTU TU
= TestTU::withCode(Source
.code());
710 auto Index
= TU
.index();
712 SymbolID Parent
= findSymbolIDByName(Index
.get(), "Parent");
713 SymbolID ChildSpec
= findSymbolIDByName(Index
.get(), "Child", "<int>");
715 EXPECT_THAT(collectSubtypes(Parent
, Index
.get()), ElementsAre(ChildSpec
));
718 TEST(Subtypes
, DependentBase
) {
719 Annotations
Source(R
"cpp(
720 template <typename T>
723 template <typename T>
724 struct Child : Parent<T> {};
727 TestTU TU
= TestTU::withCode(Source
.code());
728 auto Index
= TU
.index();
730 SymbolID Parent
= findSymbolIDByName(Index
.get(), "Parent");
731 SymbolID Child
= findSymbolIDByName(Index
.get(), "Child");
733 EXPECT_THAT(collectSubtypes(Parent
, Index
.get()), ElementsAre(Child
));
736 TEST(Subtypes
, LazyResolution
) {
737 Annotations
Source(R
"cpp(
739 struct Child1 : Parent {};
740 struct Child2a : Child1 {};
741 struct Child2b : Child1 {};
744 TestTU TU
= TestTU::withCode(Source
.code());
745 auto AST
= TU
.build();
746 auto Index
= TU
.index();
748 auto Result
= getTypeHierarchy(AST
, Source
.point(), /*ResolveLevels=*/1,
749 TypeHierarchyDirection::Children
, Index
.get(),
750 testPath(TU
.Filename
));
751 ASSERT_THAT(Result
, SizeIs(1));
754 AllOf(withName("Parent"), withKind(SymbolKind::Struct
), parents(),
755 children(AllOf(withName("Child1"), withKind(SymbolKind::Struct
),
756 parentsNotResolved(), childrenNotResolved()))));
758 resolveTypeHierarchy((*Result
.front().children
)[0], /*ResolveLevels=*/1,
759 TypeHierarchyDirection::Children
, Index
.get());
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()))));
771 TEST(Standard
, SubTypes
) {
772 Annotations
Source(R
"cpp(
775 struct Child : Parent1, Parent2 {};
778 TestTU TU
= TestTU::withCode(Source
.code());
779 auto AST
= TU
.build();
780 auto Index
= TU
.index();
782 auto Result
= getTypeHierarchy(AST
, Source
.point(), /*ResolveLevels=*/1,
783 TypeHierarchyDirection::Children
, Index
.get(),
784 testPath(TU
.Filename
));
785 ASSERT_THAT(Result
, SizeIs(1));
786 auto Children
= subTypes(Result
.front(), Index
.get());
788 // Make sure parents are populated when getting children.
789 // FIXME: This is partial.
792 UnorderedElementsAre(
793 AllOf(withName("Child"),
794 withResolveParents(HasValue(UnorderedElementsAre(withResolveID(
795 getSymbolID(&findDecl(AST
, "Parent1")).str())))))));
798 TEST(Standard
, SuperTypes
) {
799 Annotations
Source(R
"cpp(
801 struct Chil^d : Parent {};
804 TestTU TU
= TestTU::withCode(Source
.code());
805 auto AST
= TU
.build();
806 auto Index
= TU
.index();
808 auto Result
= getTypeHierarchy(AST
, Source
.point(), /*ResolveLevels=*/1,
809 TypeHierarchyDirection::Children
, Index
.get(),
810 testPath(TU
.Filename
));
811 ASSERT_THAT(Result
, SizeIs(1));
812 auto Parents
= superTypes(Result
.front(), Index
.get());
814 EXPECT_THAT(Parents
, HasValue(UnorderedElementsAre(
815 AllOf(withName("Parent"),
816 withResolveParents(HasValue(IsEmpty()))))));
819 } // namespace clangd