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"
26using ::testing::AllOf;
27using ::testing::ElementsAre;
28using ::testing::Field;
29using ::testing::IsEmpty;
30using ::testing::Matcher;
31using ::testing::SizeIs;
32using ::testing::UnorderedElementsAre;
35MATCHER_P(withName, N,
"") {
return arg.name == N; }
37MATCHER_P(selectionRangeIs, R,
"") {
return arg.selectionRange == R; }
38template <
class... ParentMatchers>
39::testing::Matcher<TypeHierarchyItem> parents(ParentMatchers... ParentsM) {
41 HasValue(UnorderedElementsAre(ParentsM...)));
43template <
class... ChildMatchers>
44::testing::Matcher<TypeHierarchyItem> children(ChildMatchers... ChildrenM) {
46 HasValue(UnorderedElementsAre(ChildrenM...)));
49MATCHER(parentsNotResolved,
"") {
return !arg.parents; }
50MATCHER(childrenNotResolved,
"") {
return !arg.children; }
51MATCHER_P(withResolveID, SID,
"") {
return arg.symbolID.str() == SID; }
53 return testing::ExplainMatchResult(
M, arg.data.parents, result_listener);
56TEST(FindRecordTypeAt, TypeOrVariable) {
57 Annotations Source(R
"cpp(
71 auto AST = TU.build();
73 for (Position Pt : Source.points()) {
75 ASSERT_THAT(
Records, SizeIs(1));
77 static_cast<const NamedDecl *
>(
Records.front()));
81TEST(FindRecordTypeAt, Nonexistent) {
82 Annotations Source(R
"cpp(
86 auto AST = TU.build();
88 for (Position Pt : Source.points()) {
90 ASSERT_THAT(
Records, SizeIs(0));
95 Annotations Source(R
"cpp(
108 auto AST = TU.build();
110 for (Position Pt : Source.points()) {
112 ASSERT_THAT(
Records, SizeIs(1));
114 static_cast<const NamedDecl *
>(
Records.front()));
119 Annotations Source(R
"cpp(
131 auto AST = TU.build();
133 for (Position Pt : Source.points()) {
141TEST(TypeParents, SimpleInheritance) {
142 Annotations Source(R
"cpp(
147struct Child1 : Parent {
151struct Child2 : Child1 {
157 auto AST = TU.build();
159 const CXXRecordDecl *
Parent =
161 const CXXRecordDecl *Child1 =
163 const CXXRecordDecl *Child2 =
168 EXPECT_THAT(
typeParents(Child2), ElementsAre(Child1));
171TEST(TypeParents, MultipleInheritance) {
172 Annotations Source(R
"cpp(
181struct Parent3 : Parent2 {
185struct Child : Parent1, Parent3 {
191 auto AST = TU.build();
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"));
203 EXPECT_THAT(
typeParents(Parent3), ElementsAre(Parent2));
204 EXPECT_THAT(
typeParents(Child), ElementsAre(Parent1, Parent3));
207TEST(TypeParents, ClassTemplate) {
208 Annotations Source(R
"cpp(
212struct Child : Parent {};
216 auto AST = TU.build();
218 const CXXRecordDecl *
Parent =
220 const CXXRecordDecl *Child =
221 dyn_cast<ClassTemplateDecl>(&
findDecl(
AST,
"Child"))->getTemplatedDecl();
226MATCHER_P(implicitSpecOf, ClassTemplate,
"") {
227 const ClassTemplateSpecializationDecl *CTS =
228 dyn_cast<ClassTemplateSpecializationDecl>(arg);
230 CTS->getSpecializedTemplate()->getTemplatedDecl() == ClassTemplate &&
231 CTS->getSpecializationKind() == TSK_ImplicitInstantiation;
236const NamedDecl &findDeclWithTemplateArgs(ParsedAST &
AST,
237 llvm::StringRef Query) {
238 return findDecl(
AST, [&Query](
const NamedDecl &ND) {
240 llvm::raw_string_ostream
OS(QName);
241 PrintingPolicy Policy(ND.getASTContext().getLangOpts());
244 ND.getNameForDiagnostic(
OS, Policy,
true);
245 return QName == Query;
249TEST(TypeParents, TemplateSpec1) {
250 Annotations Source(R
"cpp(
255struct Parent<int> {};
257struct Child1 : Parent<float> {};
259struct Child2 : Parent<int> {};
263 auto AST = TU.build();
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 =
271 const CXXRecordDecl *Child2 =
275 EXPECT_THAT(
typeParents(Child2), ElementsAre(ParentSpec));
278TEST(TypeParents, TemplateSpec2) {
279 Annotations Source(R
"cpp(
286struct Child<int> : Parent {};
290 auto AST = TU.build();
292 const CXXRecordDecl *
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>"));
303TEST(TypeParents, DependentBase) {
304 Annotations Source(R
"cpp(
309struct Child1 : Parent<T> {};
312struct Child2 : Parent<T>::Type {};
319 auto AST = TU.build();
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();
338TEST(TypeParents, IncompleteClass) {
339 Annotations Source(R
"cpp(
343 auto AST = TU.build();
345 const CXXRecordDecl *Incomplete =
346 dyn_cast<CXXRecordDecl>(&
findDecl(
AST,
"Incomplete"));
354 Annotations Source(R
"cpp(
355struct $Parent1Def[[Parent1]] {
359struct $Parent2Def[[Parent2]] {
363struct $Parent3Def[[Parent3]] : Parent2 {
367struct Ch^ild : Parent1, Parent3 {
379 auto AST = TU.build();
381 for (Position Pt : Source.points()) {
386 ASSERT_THAT(Result, SizeIs(1));
390 withName(
"Child"), withKind(SymbolKind::Struct),
391 parents(AllOf(withName(
"Parent1"), withKind(SymbolKind::Struct),
392 selectionRangeIs(Source.range(
"Parent1Def")),
394 AllOf(withName(
"Parent3"), withKind(SymbolKind::Struct),
395 selectionRangeIs(Source.range(
"Parent3Def")),
397 withName(
"Parent2"), withKind(SymbolKind::Struct),
398 selectionRangeIs(Source.range(
"Parent2Def")),
403TEST(TypeHierarchy, RecursiveHierarchyUnbounded) {
404 Annotations Source(R
"cpp(
406 struct $SDef[[S]] : S<N + 1> {};
412 TU.ExtraArgs.push_back("-ftemplate-depth=10");
413 auto AST = TU.build();
417 ASSERT_FALSE(
AST.getDiagnostics().empty());
425 ASSERT_THAT(Result, SizeIs(1));
428 AllOf(withName(
"S<0>"), withKind(SymbolKind::Struct),
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")),
437TEST(TypeHierarchy, RecursiveHierarchyBounded) {
438 Annotations Source(R
"cpp(
440 struct $SDef[[S]] : S<N - 1> {};
445 S$SRefConcrete^<2> s;
449 S$SRefDependent^<N> s;
453 auto AST = TU.build();
459 ASSERT_THAT(Result, SizeIs(1));
462 AllOf(withName(
"S<2>"), withKind(SymbolKind::Struct),
464 withName(
"S<1>"), withKind(SymbolKind::Struct),
465 selectionRangeIs(Source.range(
"SDef")),
466 parents(AllOf(withName(
"S<0>"), withKind(SymbolKind::Struct),
470 ASSERT_THAT(Result, SizeIs(1));
473 AllOf(withName(
"S"), withKind(SymbolKind::Struct),
474 parents(AllOf(withName(
"S"), withKind(SymbolKind::Struct),
475 selectionRangeIs(Source.range(
"SDef")), parents()))));
478TEST(TypeHierarchy, DeriveFromImplicitSpec) {
479 Annotations Source(R
"cpp(
480 template <typename T>
483 struct Child1 : Parent<int> {};
485 struct Child2 : Parent<char> {};
491 auto AST = TU.build();
492 auto Index = TU.index();
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()))));
506TEST(TypeHierarchy, DeriveFromPartialSpec) {
507 Annotations Source(R
"cpp(
508 template <typename T> struct Parent {};
509 template <typename T> struct Parent<T*> {};
511 struct Child : Parent<int*> {};
517 auto AST = TU.build();
518 auto Index = TU.index();
523 ASSERT_THAT(Result, SizeIs(1));
524 EXPECT_THAT(Result.front(), AllOf(withName(
"Parent"),
525 withKind(SymbolKind::Struct), children()));
528TEST(TypeHierarchy, DeriveFromTemplate) {
529 Annotations Source(R
"cpp(
530 template <typename T>
533 template <typename T>
534 struct Child : Parent<T> {};
540 auto AST = TU.build();
541 auto Index = TU.index();
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()))));
557 Annotations SourceAnnotations(R
"cpp(
558struct Ch^ild : Parent {
562 Annotations HeaderInPreambleAnnotations(R"cpp(
568 TU.HeaderCode = HeaderInPreambleAnnotations.code().str();
569 auto AST = TU.build();
574 ASSERT_THAT(Result, SizeIs(1));
577 AllOf(withName(
"Child"),
578 parents(AllOf(withName(
"Parent"),
579 selectionRangeIs(HeaderInPreambleAnnotations.range()),
583SymbolID findSymbolIDByName(SymbolIndex *Index, llvm::StringRef
Name,
584 llvm::StringRef TemplateArgs =
"") {
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);
597 EXPECT_TRUE(GotResult);
601std::vector<SymbolID> collectSubtypes(SymbolID Subject, SymbolIndex *Index) {
602 std::vector<SymbolID> Result;
603 RelationsRequest Req;
604 Req.Subjects.insert(Subject);
606 Index->relations(Req,
607 [&Result](
const SymbolID &Subject,
const Symbol &
Object) {
608 Result.push_back(
Object.ID);
613TEST(Subtypes, SimpleInheritance) {
614 Annotations Source(R
"cpp(
616struct Child1a : Parent {};
617struct Child1b : Parent {};
618struct Child2 : Child1a {};
622 auto Index = TU.index();
625 SymbolID Child1a = findSymbolIDByName(Index.get(),
"Child1a");
626 SymbolID Child1b = findSymbolIDByName(Index.get(),
"Child1b");
627 SymbolID Child2 = findSymbolIDByName(Index.get(),
"Child2");
629 EXPECT_THAT(collectSubtypes(
Parent, Index.get()),
630 UnorderedElementsAre(Child1a, Child1b));
631 EXPECT_THAT(collectSubtypes(Child1a, Index.get()), ElementsAre(Child2));
634TEST(Subtypes, MultipleInheritance) {
635 Annotations Source(R
"cpp(
638struct Parent3 : Parent2 {};
639struct Child : Parent1, Parent3 {};
643 auto Index = TU.index();
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");
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));
655TEST(Subtypes, ClassTemplate) {
656 Annotations Source(R
"cpp(
660struct Child : Parent {};
664 auto Index = TU.index();
667 SymbolID Child = findSymbolIDByName(Index.get(),
"Child");
669 EXPECT_THAT(collectSubtypes(
Parent, Index.get()), ElementsAre(Child));
672TEST(Subtypes, TemplateSpec1) {
673 Annotations Source(R
"cpp(
678struct Parent<int> {};
680struct Child1 : Parent<float> {};
682struct Child2 : Parent<int> {};
686 auto Index = TU.index();
689 SymbolID ParentSpec = findSymbolIDByName(Index.get(),
"Parent",
"<int>");
690 SymbolID Child1 = findSymbolIDByName(Index.get(),
"Child1");
691 SymbolID Child2 = findSymbolIDByName(Index.get(),
"Child2");
693 EXPECT_THAT(collectSubtypes(
Parent, Index.get()), ElementsAre(Child1));
694 EXPECT_THAT(collectSubtypes(ParentSpec, Index.get()), ElementsAre(Child2));
697TEST(Subtypes, TemplateSpec2) {
698 Annotations Source(R
"cpp(
705struct Child<int> : Parent {};
709 auto Index = TU.index();
712 SymbolID ChildSpec = findSymbolIDByName(Index.get(),
"Child",
"<int>");
714 EXPECT_THAT(collectSubtypes(
Parent, Index.get()), ElementsAre(ChildSpec));
717TEST(Subtypes, DependentBase) {
718 Annotations Source(R
"cpp(
723struct Child : Parent<T> {};
727 auto Index = TU.index();
730 SymbolID Child = findSymbolIDByName(Index.get(),
"Child");
732 EXPECT_THAT(collectSubtypes(
Parent, Index.get()), ElementsAre(Child));
735TEST(Subtypes, LazyResolution) {
736 Annotations Source(R
"cpp(
738struct Child1 : Parent {};
739struct Child2a : Child1 {};
740struct Child2b : Child1 {};
744 auto AST = TU.build();
745 auto Index = TU.index();
750 ASSERT_THAT(Result, SizeIs(1));
753 AllOf(withName(
"Parent"), withKind(SymbolKind::Struct), parents(),
754 children(AllOf(withName(
"Child1"), withKind(SymbolKind::Struct),
755 parentsNotResolved(), childrenNotResolved()))));
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()))));
771 Annotations Source(R
"cpp(
774struct Child : Parent1, Parent2 {};
778 auto AST = TU.build();
779 auto Index = TU.index();
784 ASSERT_THAT(Result, SizeIs(1));
791 UnorderedElementsAre(
792 AllOf(withName(
"Child"),
793 withResolveParents(
HasValue(UnorderedElementsAre(withResolveID(
798 Annotations Source(R
"cpp(
800struct Chil^d : Parent {};
804 auto AST = TU.build();
805 auto Index = TU.index();
810 ASSERT_THAT(Result, SizeIs(1));
814 AllOf(withName(
"Parent"),
815 withResolveParents(
HasValue(IsEmpty()))))));
llvm::SmallString< 256U > Name
llvm::StringMap< llvm::TimeRecord > Records
const google::protobuf::Message & M
std::vector< TypeHierarchyItem > subTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index)
Returns direct children of a TypeHierarchyItem.
std::optional< std::vector< TypeHierarchyItem > > superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index)
Returns direct parents of a TypeHierarchyItem using SymbolIDs stored inside the item.
SymbolID getSymbolID(const Decl *D)
Gets the symbol ID for a declaration. Returned SymbolID might be null.
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
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.
std::string testPath(PathRef File, llvm::sys::path::Style Style)
TEST(BackgroundQueueTest, Priority)
void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, TypeHierarchyDirection Direction, const SymbolIndex *Index)
std::vector< const CXXRecordDecl * > findRecordTypeAt(ParsedAST &AST, Position Pos)
Find the record types referenced at Pos.
std::vector< const CXXRecordDecl * > typeParents(const CXXRecordDecl *CXXRD)
Given a record type declaration, find its base (parent) types.
std::array< uint8_t, 20 > SymbolID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static TestTU withCode(llvm::StringRef Code)
std::optional< std::vector< TypeHierarchyItem > > children
If this type hierarchy item is resolved, it contains the direct children of the current item.
std::optional< std::vector< TypeHierarchyItem > > parents
This is a clangd exntesion.