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