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