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