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