clang-tools 20.0.0git
TypeHierarchyTests.cpp
Go to the documentation of this file.
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) {
41 HasValue(UnorderedElementsAre(ParentsM...)));
42}
43template <class... ChildMatchers>
44::testing::Matcher<TypeHierarchyItem> children(ChildMatchers... ChildrenM) {
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(Source.code());
71 auto AST = TU.build();
72
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()));
78 }
79}
80
81TEST(FindRecordTypeAt, Nonexistent) {
82 Annotations Source(R"cpp(
83 int *wa^ldo;
84 )cpp");
85 TestTU TU = TestTU::withCode(Source.code());
86 auto AST = TU.build();
87
88 for (Position Pt : Source.points()) {
89 auto Records = findRecordTypeAt(AST, 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(Source.code());
108 auto AST = TU.build();
109
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()));
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(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(Source.code());
157 auto AST = TU.build();
158
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"));
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(Source.code());
191 auto AST = TU.build();
192
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"));
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(Source.code());
216 auto AST = TU.build();
217
218 const CXXRecordDecl *Parent =
219 dyn_cast<CXXRecordDecl>(&findDecl(AST, "Parent"));
220 const CXXRecordDecl *Child =
221 dyn_cast<ClassTemplateDecl>(&findDecl(AST, "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, [&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 return QName == Query;
246 });
247}
248
249TEST(TypeParents, TemplateSpec1) {
250 Annotations Source(R"cpp(
251template <typename T>
252struct Parent {};
253
254template <>
255struct Parent<int> {};
256
257struct Child1 : Parent<float> {};
258
259struct Child2 : Parent<int> {};
260)cpp");
261
262 TestTU TU = TestTU::withCode(Source.code());
263 auto AST = TU.build();
264
265 const CXXRecordDecl *Parent =
266 dyn_cast<ClassTemplateDecl>(&findDecl(AST, "Parent"))->getTemplatedDecl();
267 const CXXRecordDecl *ParentSpec =
268 dyn_cast<CXXRecordDecl>(&findDeclWithTemplateArgs(AST, "Parent<int>"));
269 const CXXRecordDecl *Child1 =
270 dyn_cast<CXXRecordDecl>(&findDecl(AST, "Child1"));
271 const CXXRecordDecl *Child2 =
272 dyn_cast<CXXRecordDecl>(&findDecl(AST, "Child2"));
273
274 EXPECT_THAT(typeParents(Child1), ElementsAre(implicitSpecOf(Parent)));
275 EXPECT_THAT(typeParents(Child2), ElementsAre(ParentSpec));
276}
277
278TEST(TypeParents, TemplateSpec2) {
279 Annotations Source(R"cpp(
280struct Parent {};
281
282template <typename T>
283struct Child {};
284
285template <>
286struct Child<int> : Parent {};
287)cpp");
288
289 TestTU TU = TestTU::withCode(Source.code());
290 auto AST = TU.build();
291
292 const CXXRecordDecl *Parent =
293 dyn_cast<CXXRecordDecl>(&findDecl(AST, "Parent"));
294 const CXXRecordDecl *Child =
295 dyn_cast<ClassTemplateDecl>(&findDecl(AST, "Child"))->getTemplatedDecl();
296 const CXXRecordDecl *ChildSpec =
297 dyn_cast<CXXRecordDecl>(&findDeclWithTemplateArgs(AST, "Child<int>"));
298
299 EXPECT_THAT(typeParents(Child), ElementsAre());
300 EXPECT_THAT(typeParents(ChildSpec), ElementsAre(Parent));
301}
302
303TEST(TypeParents, DependentBase) {
304 Annotations Source(R"cpp(
305template <typename T>
306struct Parent {};
307
308template <typename T>
309struct Child1 : Parent<T> {};
310
311template <typename T>
312struct Child2 : Parent<T>::Type {};
313
314template <typename T>
315struct Child3 : T {};
316)cpp");
317
318 TestTU TU = TestTU::withCode(Source.code());
319 auto AST = TU.build();
320
321 const CXXRecordDecl *Parent =
322 dyn_cast<ClassTemplateDecl>(&findDecl(AST, "Parent"))->getTemplatedDecl();
323 const CXXRecordDecl *Child1 =
324 dyn_cast<ClassTemplateDecl>(&findDecl(AST, "Child1"))->getTemplatedDecl();
325 const CXXRecordDecl *Child2 =
326 dyn_cast<ClassTemplateDecl>(&findDecl(AST, "Child2"))->getTemplatedDecl();
327 const CXXRecordDecl *Child3 =
328 dyn_cast<ClassTemplateDecl>(&findDecl(AST, "Child3"))->getTemplatedDecl();
329
330 // For "Parent<T>", use the primary template as a best-effort guess.
331 EXPECT_THAT(typeParents(Child1), ElementsAre(Parent));
332 // For "Parent<T>::Type", there is nothing we can do.
333 EXPECT_THAT(typeParents(Child2), ElementsAre());
334 // Likewise for "T".
335 EXPECT_THAT(typeParents(Child3), ElementsAre());
336}
337
338TEST(TypeParents, IncompleteClass) {
339 Annotations Source(R"cpp(
340 class Incomplete;
341 )cpp");
342 TestTU TU = TestTU::withCode(Source.code());
343 auto AST = TU.build();
344
345 const CXXRecordDecl *Incomplete =
346 dyn_cast<CXXRecordDecl>(&findDecl(AST, "Incomplete"));
347 EXPECT_THAT(typeParents(Incomplete), IsEmpty());
348}
349
350// Parts of getTypeHierarchy() are tested in more detail by the
351// FindRecordTypeAt.* and TypeParents.* tests above. This test exercises the
352// entire operation.
353TEST(TypeHierarchy, Parents) {
354 Annotations Source(R"cpp(
355struct $Parent1Def[[Parent1]] {
356 int a;
357};
358
359struct $Parent2Def[[Parent2]] {
360 int b;
361};
362
363struct $Parent3Def[[Parent3]] : Parent2 {
364 int c;
365};
366
367struct Ch^ild : Parent1, Parent3 {
368 int d;
369};
370
371int main() {
372 Ch^ild ch^ild;
373
374 ch^ild.a = 1;
375}
376)cpp");
377
378 TestTU TU = TestTU::withCode(Source.code());
379 auto AST = TU.build();
380
381 for (Position Pt : Source.points()) {
382 // Set ResolveLevels to 0 because it's only used for Children;
383 // for Parents, getTypeHierarchy() always returns all levels.
384 auto Result = getTypeHierarchy(AST, Pt, /*ResolveLevels=*/0,
386 ASSERT_THAT(Result, SizeIs(1));
387 EXPECT_THAT(
388 Result.front(),
389 AllOf(
390 withName("Child"), withKind(SymbolKind::Struct),
391 parents(AllOf(withName("Parent1"), withKind(SymbolKind::Struct),
392 selectionRangeIs(Source.range("Parent1Def")),
393 parents()),
394 AllOf(withName("Parent3"), withKind(SymbolKind::Struct),
395 selectionRangeIs(Source.range("Parent3Def")),
396 parents(AllOf(
397 withName("Parent2"), withKind(SymbolKind::Struct),
398 selectionRangeIs(Source.range("Parent2Def")),
399 parents()))))));
400 }
401}
402
403TEST(TypeHierarchy, RecursiveHierarchyUnbounded) {
404 Annotations Source(R"cpp(
405 template <int N>
406 struct $SDef[[S]] : S<N + 1> {};
407
408 S^<0> s; // error-ok
409 )cpp");
410
411 TestTU TU = TestTU::withCode(Source.code());
412 TU.ExtraArgs.push_back("-ftemplate-depth=10");
413 auto AST = TU.build();
414
415 // The compiler should produce a diagnostic for hitting the
416 // template instantiation depth.
417 ASSERT_FALSE(AST.getDiagnostics().empty());
418
419 // Make sure getTypeHierarchy() doesn't get into an infinite recursion.
420 // The parent is reported as "S" because "S<0>" is an invalid instantiation.
421 // We then iterate once more and find "S" again before detecting the
422 // recursion.
423 auto Result = getTypeHierarchy(AST, Source.points()[0], 0,
425 ASSERT_THAT(Result, SizeIs(1));
426 EXPECT_THAT(
427 Result.front(),
428 AllOf(withName("S<0>"), withKind(SymbolKind::Struct),
429 parents(
430 AllOf(withName("S"), withKind(SymbolKind::Struct),
431 selectionRangeIs(Source.range("SDef")),
432 parents(AllOf(withName("S"), withKind(SymbolKind::Struct),
433 selectionRangeIs(Source.range("SDef")),
434 parents()))))));
435}
436
437TEST(TypeHierarchy, RecursiveHierarchyBounded) {
438 Annotations Source(R"cpp(
439 template <int N>
440 struct $SDef[[S]] : S<N - 1> {};
441
442 template <>
443 struct S<0>{};
444
445 S$SRefConcrete^<2> s;
446
447 template <int N>
448 struct Foo {
449 S$SRefDependent^<N> s;
450 };)cpp");
451
452 TestTU TU = TestTU::withCode(Source.code());
453 auto AST = TU.build();
454
455 // Make sure getTypeHierarchy() doesn't get into an infinite recursion
456 // for either a concrete starting point or a dependent starting point.
457 auto Result = getTypeHierarchy(AST, Source.point("SRefConcrete"), 0,
459 ASSERT_THAT(Result, SizeIs(1));
460 EXPECT_THAT(
461 Result.front(),
462 AllOf(withName("S<2>"), withKind(SymbolKind::Struct),
463 parents(AllOf(
464 withName("S<1>"), withKind(SymbolKind::Struct),
465 selectionRangeIs(Source.range("SDef")),
466 parents(AllOf(withName("S<0>"), withKind(SymbolKind::Struct),
467 parents()))))));
468 Result = getTypeHierarchy(AST, Source.point("SRefDependent"), 0,
470 ASSERT_THAT(Result, SizeIs(1));
471 EXPECT_THAT(
472 Result.front(),
473 AllOf(withName("S"), withKind(SymbolKind::Struct),
474 parents(AllOf(withName("S"), withKind(SymbolKind::Struct),
475 selectionRangeIs(Source.range("SDef")), parents()))));
476}
477
478TEST(TypeHierarchy, DeriveFromImplicitSpec) {
479 Annotations Source(R"cpp(
480 template <typename T>
481 struct Parent {};
482
483 struct Child1 : Parent<int> {};
484
485 struct Child2 : Parent<char> {};
486
487 Parent<int> Fo^o;
488 )cpp");
489
490 TestTU TU = TestTU::withCode(Source.code());
491 auto AST = TU.build();
492 auto Index = TU.index();
493
494 auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
496 testPath(TU.Filename));
497 ASSERT_THAT(Result, SizeIs(1));
498 EXPECT_THAT(Result.front(),
499 AllOf(withName("Parent"), withKind(SymbolKind::Struct),
500 children(AllOf(withName("Child1"),
501 withKind(SymbolKind::Struct), children()),
502 AllOf(withName("Child2"),
503 withKind(SymbolKind::Struct), children()))));
504}
505
506TEST(TypeHierarchy, DeriveFromPartialSpec) {
507 Annotations Source(R"cpp(
508 template <typename T> struct Parent {};
509 template <typename T> struct Parent<T*> {};
510
511 struct Child : Parent<int*> {};
512
513 Parent<int> Fo^o;
514 )cpp");
515
516 TestTU TU = TestTU::withCode(Source.code());
517 auto AST = TU.build();
518 auto Index = TU.index();
519
520 auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
522 testPath(TU.Filename));
523 ASSERT_THAT(Result, SizeIs(1));
524 EXPECT_THAT(Result.front(), AllOf(withName("Parent"),
525 withKind(SymbolKind::Struct), children()));
526}
527
528TEST(TypeHierarchy, DeriveFromTemplate) {
529 Annotations Source(R"cpp(
530 template <typename T>
531 struct Parent {};
532
533 template <typename T>
534 struct Child : Parent<T> {};
535
536 Parent<int> Fo^o;
537 )cpp");
538
539 TestTU TU = TestTU::withCode(Source.code());
540 auto AST = TU.build();
541 auto Index = TU.index();
542
543 // FIXME: We'd like this to show the implicit specializations Parent<int>
544 // and Child<int>, but currently libIndex does not expose relationships
545 // between implicit specializations.
546 auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
548 testPath(TU.Filename));
549 ASSERT_THAT(Result, SizeIs(1));
550 EXPECT_THAT(Result.front(),
551 AllOf(withName("Parent"), withKind(SymbolKind::Struct),
552 children(AllOf(withName("Child"),
553 withKind(SymbolKind::Struct), children()))));
554}
555
556TEST(TypeHierarchy, Preamble) {
557 Annotations SourceAnnotations(R"cpp(
558struct Ch^ild : Parent {
559 int b;
560};)cpp");
561
562 Annotations HeaderInPreambleAnnotations(R"cpp(
563struct [[Parent]] {
564 int a;
565};)cpp");
566
567 TestTU TU = TestTU::withCode(SourceAnnotations.code());
568 TU.HeaderCode = HeaderInPreambleAnnotations.code().str();
569 auto AST = TU.build();
570
571 std::vector<TypeHierarchyItem> Result = getTypeHierarchy(
572 AST, SourceAnnotations.point(), 1, TypeHierarchyDirection::Parents);
573
574 ASSERT_THAT(Result, SizeIs(1));
575 EXPECT_THAT(
576 Result.front(),
577 AllOf(withName("Child"),
578 parents(AllOf(withName("Parent"),
579 selectionRangeIs(HeaderInPreambleAnnotations.range()),
580 parents()))));
581}
582
583SymbolID findSymbolIDByName(SymbolIndex *Index, llvm::StringRef Name,
584 llvm::StringRef TemplateArgs = "") {
585 SymbolID Result;
586 FuzzyFindRequest Request;
587 Request.Query = std::string(Name);
588 Request.AnyScope = true;
589 bool GotResult = false;
590 Index->fuzzyFind(Request, [&](const Symbol &S) {
591 if (TemplateArgs == S.TemplateSpecializationArgs) {
592 EXPECT_FALSE(GotResult);
593 Result = S.ID;
594 GotResult = true;
595 }
596 });
597 EXPECT_TRUE(GotResult);
598 return Result;
599}
600
601std::vector<SymbolID> collectSubtypes(SymbolID Subject, SymbolIndex *Index) {
602 std::vector<SymbolID> Result;
603 RelationsRequest Req;
604 Req.Subjects.insert(Subject);
605 Req.Predicate = RelationKind::BaseOf;
606 Index->relations(Req,
607 [&Result](const SymbolID &Subject, const Symbol &Object) {
608 Result.push_back(Object.ID);
609 });
610 return Result;
611}
612
613TEST(Subtypes, SimpleInheritance) {
614 Annotations Source(R"cpp(
615struct Parent {};
616struct Child1a : Parent {};
617struct Child1b : Parent {};
618struct Child2 : Child1a {};
619)cpp");
620
621 TestTU TU = TestTU::withCode(Source.code());
622 auto Index = TU.index();
623
624 SymbolID Parent = findSymbolIDByName(Index.get(), "Parent");
625 SymbolID Child1a = findSymbolIDByName(Index.get(), "Child1a");
626 SymbolID Child1b = findSymbolIDByName(Index.get(), "Child1b");
627 SymbolID Child2 = findSymbolIDByName(Index.get(), "Child2");
628
629 EXPECT_THAT(collectSubtypes(Parent, Index.get()),
630 UnorderedElementsAre(Child1a, Child1b));
631 EXPECT_THAT(collectSubtypes(Child1a, Index.get()), ElementsAre(Child2));
632}
633
634TEST(Subtypes, MultipleInheritance) {
635 Annotations Source(R"cpp(
636struct Parent1 {};
637struct Parent2 {};
638struct Parent3 : Parent2 {};
639struct Child : Parent1, Parent3 {};
640)cpp");
641
642 TestTU TU = TestTU::withCode(Source.code());
643 auto Index = TU.index();
644
645 SymbolID Parent1 = findSymbolIDByName(Index.get(), "Parent1");
646 SymbolID Parent2 = findSymbolIDByName(Index.get(), "Parent2");
647 SymbolID Parent3 = findSymbolIDByName(Index.get(), "Parent3");
648 SymbolID Child = findSymbolIDByName(Index.get(), "Child");
649
650 EXPECT_THAT(collectSubtypes(Parent1, Index.get()), ElementsAre(Child));
651 EXPECT_THAT(collectSubtypes(Parent2, Index.get()), ElementsAre(Parent3));
652 EXPECT_THAT(collectSubtypes(Parent3, Index.get()), ElementsAre(Child));
653}
654
655TEST(Subtypes, ClassTemplate) {
656 Annotations Source(R"cpp(
657struct Parent {};
658
659template <typename T>
660struct Child : Parent {};
661)cpp");
662
663 TestTU TU = TestTU::withCode(Source.code());
664 auto Index = TU.index();
665
666 SymbolID Parent = findSymbolIDByName(Index.get(), "Parent");
667 SymbolID Child = findSymbolIDByName(Index.get(), "Child");
668
669 EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child));
670}
671
672TEST(Subtypes, TemplateSpec1) {
673 Annotations Source(R"cpp(
674template <typename T>
675struct Parent {};
676
677template <>
678struct Parent<int> {};
679
680struct Child1 : Parent<float> {};
681
682struct Child2 : Parent<int> {};
683)cpp");
684
685 TestTU TU = TestTU::withCode(Source.code());
686 auto Index = TU.index();
687
688 SymbolID Parent = findSymbolIDByName(Index.get(), "Parent");
689 SymbolID ParentSpec = findSymbolIDByName(Index.get(), "Parent", "<int>");
690 SymbolID Child1 = findSymbolIDByName(Index.get(), "Child1");
691 SymbolID Child2 = findSymbolIDByName(Index.get(), "Child2");
692
693 EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child1));
694 EXPECT_THAT(collectSubtypes(ParentSpec, Index.get()), ElementsAre(Child2));
695}
696
697TEST(Subtypes, TemplateSpec2) {
698 Annotations Source(R"cpp(
699struct Parent {};
700
701template <typename T>
702struct Child {};
703
704template <>
705struct Child<int> : Parent {};
706)cpp");
707
708 TestTU TU = TestTU::withCode(Source.code());
709 auto Index = TU.index();
710
711 SymbolID Parent = findSymbolIDByName(Index.get(), "Parent");
712 SymbolID ChildSpec = findSymbolIDByName(Index.get(), "Child", "<int>");
713
714 EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(ChildSpec));
715}
716
717TEST(Subtypes, DependentBase) {
718 Annotations Source(R"cpp(
719template <typename T>
720struct Parent {};
721
722template <typename T>
723struct Child : Parent<T> {};
724)cpp");
725
726 TestTU TU = TestTU::withCode(Source.code());
727 auto Index = TU.index();
728
729 SymbolID Parent = findSymbolIDByName(Index.get(), "Parent");
730 SymbolID Child = findSymbolIDByName(Index.get(), "Child");
731
732 EXPECT_THAT(collectSubtypes(Parent, Index.get()), ElementsAre(Child));
733}
734
735TEST(Subtypes, LazyResolution) {
736 Annotations Source(R"cpp(
737struct P^arent {};
738struct Child1 : Parent {};
739struct Child2a : Child1 {};
740struct Child2b : Child1 {};
741)cpp");
742
743 TestTU TU = TestTU::withCode(Source.code());
744 auto AST = TU.build();
745 auto Index = TU.index();
746
747 auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
749 testPath(TU.Filename));
750 ASSERT_THAT(Result, SizeIs(1));
751 EXPECT_THAT(
752 Result.front(),
753 AllOf(withName("Parent"), withKind(SymbolKind::Struct), parents(),
754 children(AllOf(withName("Child1"), withKind(SymbolKind::Struct),
755 parentsNotResolved(), childrenNotResolved()))));
756
757 resolveTypeHierarchy((*Result.front().children)[0], /*ResolveLevels=*/1,
759
760 EXPECT_THAT(
761 (*Result.front().children)[0],
762 AllOf(withName("Child1"), withKind(SymbolKind::Struct),
763 parentsNotResolved(),
764 children(AllOf(withName("Child2a"), withKind(SymbolKind::Struct),
765 parentsNotResolved(), childrenNotResolved()),
766 AllOf(withName("Child2b"), withKind(SymbolKind::Struct),
767 parentsNotResolved(), childrenNotResolved()))));
768}
769
770TEST(Standard, SubTypes) {
771 Annotations Source(R"cpp(
772struct Pare^nt1 {};
773struct Parent2 {};
774struct Child : Parent1, Parent2 {};
775)cpp");
776
777 TestTU TU = TestTU::withCode(Source.code());
778 auto AST = TU.build();
779 auto Index = TU.index();
780
781 auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
783 testPath(TU.Filename));
784 ASSERT_THAT(Result, SizeIs(1));
785 auto Children = subTypes(Result.front(), Index.get());
786
787 // Make sure parents are populated when getting children.
788 // FIXME: This is partial.
789 EXPECT_THAT(
790 Children,
791 UnorderedElementsAre(
792 AllOf(withName("Child"),
793 withResolveParents(HasValue(UnorderedElementsAre(withResolveID(
794 getSymbolID(&findDecl(AST, "Parent1")).str())))))));
795}
796
797TEST(Standard, SuperTypes) {
798 Annotations Source(R"cpp(
799struct Parent {};
800struct Chil^d : Parent {};
801)cpp");
802
803 TestTU TU = TestTU::withCode(Source.code());
804 auto AST = TU.build();
805 auto Index = TU.index();
806
807 auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
809 testPath(TU.Filename));
810 ASSERT_THAT(Result, SizeIs(1));
811 auto Parents = superTypes(Result.front(), Index.get());
812
813 EXPECT_THAT(Parents, HasValue(UnorderedElementsAre(
814 AllOf(withName("Parent"),
815 withResolveParents(HasValue(IsEmpty()))))));
816}
817} // namespace
818} // namespace clangd
819} // namespace clang
BindArgumentKind Kind
llvm::SmallString< 256U > Name
llvm::raw_ostream & OS
const Node * Parent
llvm::StringMap< llvm::TimeRecord > Records
Definition: Query.cpp:97
const google::protobuf::Message & M
Definition: Server.cpp:356
std::vector< TypeHierarchyItem > subTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index)
Returns direct children of a TypeHierarchyItem.
Definition: XRefs.cpp:2193
std::optional< std::vector< TypeHierarchyItem > > superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index)
Returns direct parents of a TypeHierarchyItem using SymbolIDs stored inside the item.
Definition: XRefs.cpp:2172
SymbolID getSymbolID(const Decl *D)
Gets the symbol ID for a declaration. Returned SymbolID might be null.
Definition: AST.cpp:331
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition: TestTU.cpp:219
OptionalMatcher< InnerMatcher > HasValue(const InnerMatcher &inner_matcher)
std::vector< TypeHierarchyItem > getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, TypeHierarchyDirection Direction, const SymbolIndex *Index, PathRef TUPath)
Get type hierarchy information at Pos.
Definition: XRefs.cpp:2128
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
TEST(BackgroundQueueTest, Priority)
void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, TypeHierarchyDirection Direction, const SymbolIndex *Index)
Definition: XRefs.cpp:2202
MATCHER(declared, "")
std::vector< const CXXRecordDecl * > findRecordTypeAt(ParsedAST &AST, Position Pos)
Find the record types referenced at Pos.
Definition: XRefs.cpp:1827
std::vector< const CXXRecordDecl * > typeParents(const CXXRecordDecl *CXXRD)
Given a record type declaration, find its base (parent) types.
Definition: XRefs.cpp:2086
std::array< uint8_t, 20 > SymbolID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36
std::optional< std::vector< TypeHierarchyItem > > children
If this type hierarchy item is resolved, it contains the direct children of the current item.
Definition: Protocol.h:1542
std::optional< std::vector< TypeHierarchyItem > > parents
This is a clangd exntesion.
Definition: Protocol.h:1536