14#include "llvm/Support/Path.h"
15#include "gmock/gmock.h"
16#include "gtest/gtest.h"
28 Stream <<
"{ from: " <<
Call.from <<
", ranges: [";
29 for (
const auto &R :
Call.fromRanges) {
33 return Stream <<
"] }";
38using ::testing::AllOf;
39using ::testing::ElementsAre;
40using ::testing::Field;
41using ::testing::IsEmpty;
42using ::testing::Matcher;
43using ::testing::UnorderedElementsAre;
46MATCHER_P(withName, N,
"") {
return arg.name == N; }
47MATCHER_P(withDetail, N,
"") {
return arg.detail == N; }
48MATCHER_P(withFile, N,
"") {
return arg.uri.file() == N; }
50template <
typename... Tags>
51::testing::Matcher<CallHierarchyItem> withSymbolTags(Tags... tags) {
56template <
class ItemMatcher>
57::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher M) {
60template <
class ItemMatcher>
61::testing::Matcher<CallHierarchyOutgoingCall> to(ItemMatcher M) {
64template <
class... RangeMatchers>
65::testing::Matcher<CallHierarchyIncomingCall> iFromRanges(RangeMatchers... M) {
67 UnorderedElementsAre(M...));
69template <
class... RangeMatchers>
70::testing::Matcher<CallHierarchyOutgoingCall> oFromRanges(RangeMatchers... M) {
72 UnorderedElementsAre(M...));
75TEST(CallHierarchy, IncomingOneFileCpp) {
79 $Callee[[callee]](42);
82 $Caller1A[[caller1]]();
83 $Caller1B[[caller1]]();
86 $Caller1C[[caller1]]();
87 $Caller2[[caller2]]();
91 auto AST = TU.build();
92 auto Index = TU.index();
94 std::vector<CallHierarchyItem> Items =
96 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
100 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
""))),
101 iFromRanges(Source.range(
"Callee")))));
102 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
105 ElementsAre(AllOf(from(AllOf(withName(
"caller2"), withDetail(
""))),
106 iFromRanges(Source.range(
"Caller1A"),
107 Source.range(
"Caller1B"))),
108 AllOf(from(AllOf(withName(
"caller3"), withDetail(
""))),
109 iFromRanges(Source.range(
"Caller1C")))));
111 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
114 ElementsAre(AllOf(from(AllOf(withName(
"caller3"), withDetail(
""))),
115 iFromRanges(Source.range(
"Caller2")))));
117 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
118 EXPECT_THAT(IncomingLevel4, IsEmpty());
121TEST(CallHierarchy, IncomingOneFileObjC) {
123 @implementation MyClass {}
126 [MyClass $Callee[[callee]]];
129 [MyClass $Caller1A[[caller1]]];
130 [MyClass $Caller1B[[caller1]]];
133 [MyClass $Caller1C[[caller1]]];
134 [MyClass $Caller2[[caller2]]];
139 TU.Filename = "TestTU.m";
140 auto AST = TU.build();
141 auto Index = TU.index();
142 std::vector<CallHierarchyItem> Items =
144 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
148 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
"MyClass"))),
149 iFromRanges(Source.range(
"Callee")))));
150 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
153 ElementsAre(AllOf(from(AllOf(withName(
"caller2"), withDetail(
"MyClass"))),
154 iFromRanges(Source.range(
"Caller1A"),
155 Source.range(
"Caller1B"))),
156 AllOf(from(AllOf(withName(
"caller3"), withDetail(
"MyClass"))),
157 iFromRanges(Source.range(
"Caller1C")))));
159 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
162 ElementsAre(AllOf(from(AllOf(withName(
"caller3"), withDetail(
"MyClass"))),
163 iFromRanges(Source.range(
"Caller2")))));
165 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
166 EXPECT_THAT(IncomingLevel4, IsEmpty());
169TEST(CallHierarchy, IncomingIncludeOverrides) {
173 virtual void Func() = 0;
175 struct Implementation : public Interface {
176 void Func() override {
180 void Test(Interface& cls){
181 cls.$FuncCall[[Func]]();
185 auto AST = TU.build();
186 auto Index = TU.index();
188 std::vector<CallHierarchyItem> Items =
190 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
192 ASSERT_THAT(IncomingLevel1,
194 from(AllOf(withName(
"Func"), withDetail(
"Implementation"))),
195 iFromRanges(Source.range(
"Callee")))));
196 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
197 ASSERT_THAT(IncomingLevel2,
198 ElementsAre(AllOf(from(AllOf(withName(
"Test"), withDetail(
""))),
199 iFromRanges(Source.range(
"FuncCall")))));
201 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
202 EXPECT_THAT(IncomingLevel3, IsEmpty());
205TEST(CallHierarchy, MainFileOnlyRef) {
213 $Callee[[callee]](42);
217 $Caller1[[caller1]]();
221 auto AST = TU.build();
222 auto Index = TU.index();
224 std::vector<CallHierarchyItem> Items =
226 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
230 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
""))),
231 iFromRanges(Source.range(
"Callee")))));
233 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
236 ElementsAre(AllOf(from(AllOf(withName(
"caller2"), withDetail(
""))),
237 iFromRanges(Source.range(
"Caller1")))));
240TEST(CallHierarchy, IncomingQualified) {
246 void Waldo::find() {}
247 void caller1(Waldo &W) {
248 W.$Caller1[[f^ind]]();
250 void caller2(Waldo &W) {
251 W.$Caller2[[find]]();
256 auto AST = TU.build();
257 auto Index = TU.index();
259 std::vector<CallHierarchyItem> Items =
261 ASSERT_THAT(Items, ElementsAre(withName(
"Waldo::find")));
265 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
"ns"))),
266 iFromRanges(Source.range(
"Caller1"))),
267 AllOf(from(AllOf(withName(
"caller2"), withDetail(
"ns"))),
268 iFromRanges(Source.range(
"Caller2")))));
271TEST(CallHierarchy, OutgoingOneFile) {
279 void Foo::caller1() {
280 $Callee[[callee]](42);
284 void caller2(ns::Foo& F) {
285 F.$Caller1A[[caller1]]();
286 F.$Caller1B[[caller1]]();
289 void call^er3(ns::Foo& F) {
290 F.$Caller1C[[caller1]]();
291 $Caller2[[caller2]](F);
295 auto AST = TU.build();
296 auto Index = TU.index();
298 std::vector<CallHierarchyItem> Items =
300 ASSERT_THAT(Items, ElementsAre(withName(
"caller3")));
304 ElementsAre(AllOf(to(AllOf(withName(
"caller1"), withDetail(
"ns::Foo"))),
305 oFromRanges(Source.range(
"Caller1C"))),
306 AllOf(to(AllOf(withName(
"caller2"), withDetail(
""))),
307 oFromRanges(Source.range(
"Caller2")))));
309 auto OutgoingLevel2 =
outgoingCalls(OugoingLevel1[1].to, Index.get());
313 to(AllOf(withName(
"caller1"), withDetail(
"ns::Foo"))),
314 oFromRanges(Source.range(
"Caller1A"), Source.range(
"Caller1B")))));
316 auto OutgoingLevel3 =
outgoingCalls(OutgoingLevel2[0].to, Index.get());
317 ASSERT_THAT(OutgoingLevel3,
318 ElementsAre(AllOf(to(AllOf(withName(
"callee"), withDetail(
""))),
319 oFromRanges(Source.range(
"Callee")))));
321 auto OutgoingLevel4 =
outgoingCalls(OutgoingLevel3[0].to, Index.get());
322 EXPECT_THAT(OutgoingLevel4, IsEmpty());
325TEST(CallHierarchy, MultiFileCpp) {
345 #include "caller1.hh"
358 #include "caller1.hh"
359 #include "caller2.hh"
362 nsa::$A[[caller1]]();
363 nsa::$B[[caller1]]();
373 #include "caller1.hh"
374 #include "caller2.hh"
377 $Caller1[[caller1]]();
378 nsb::$Caller2[[caller2]]();
384 Workspace.addSource("callee.hh", CalleeH.code());
385 Workspace.addSource(
"caller1.hh", Caller1H.code());
386 Workspace.addSource(
"caller2.hh", Caller2H.code());
387 Workspace.addSource(
"caller3.hh", Caller3H.code());
388 Workspace.addMainFile(
"callee.cc", CalleeC.code());
389 Workspace.addMainFile(
"caller1.cc", Caller1C.code());
390 Workspace.addMainFile(
"caller2.cc", Caller2C.code());
391 Workspace.addMainFile(
"caller3.cc", Caller3C.code());
393 auto Index = Workspace.index();
396 std::vector<CallHierarchyItem> Items =
398 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
402 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
"nsa"))),
403 iFromRanges(Caller1C.range()))));
405 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
409 AllOf(from(AllOf(withName(
"caller2"), withDetail(
"nsb"))),
410 iFromRanges(Caller2C.range(
"A"), Caller2C.range(
"B"))),
411 AllOf(from(AllOf(withName(
"caller3"), withDetail(
"nsa"))),
412 iFromRanges(Caller3C.range(
"Caller1")))));
414 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
417 ElementsAre(AllOf(from(AllOf(withName(
"caller3"), withDetail(
"nsa"))),
418 iFromRanges(Caller3C.range(
"Caller2")))));
420 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
421 EXPECT_THAT(IncomingLevel4, IsEmpty());
425 bool IsDeclaration) {
426 std::vector<CallHierarchyItem> Items =
432 withFile(
testPath(IsDeclaration ?
"caller3.hh" :
"caller3.cc")))));
440 AllOf(to(AllOf(withName(
"caller1"), withDetail(
"nsa"))),
441 IsDeclaration ? oFromRanges()
442 : oFromRanges(Caller3C.range(
"Caller1"))),
443 AllOf(to(AllOf(withName(
"caller2"), withDetail(
"nsb"))),
444 IsDeclaration ? oFromRanges()
445 : oFromRanges(Caller3C.range(
"Caller2")))));
447 auto OutgoingLevel2 =
outgoingCalls(OutgoingLevel1[1].to, Index.get());
448 ASSERT_THAT(OutgoingLevel2,
450 to(AllOf(withName(
"caller1"), withDetail(
"nsa"))),
451 oFromRanges(Caller2C.range(
"A"), Caller2C.range(
"B")))));
453 auto OutgoingLevel3 =
outgoingCalls(OutgoingLevel2[0].to, Index.get());
454 ASSERT_THAT(OutgoingLevel3,
455 ElementsAre(AllOf(to(AllOf(withName(
"callee"), withDetail(
""))),
456 oFromRanges(Caller1C.range()))));
458 auto OutgoingLevel4 =
outgoingCalls(OutgoingLevel3[0].to, Index.get());
459 EXPECT_THAT(OutgoingLevel4, IsEmpty());
463 auto AST = Workspace.openFile(
"caller1.cc");
464 ASSERT_TRUE(
bool(
AST));
465 CheckIncomingCalls(*
AST, Caller1C.point(),
testPath(
"caller1.cc"));
468 AST = Workspace.openFile(
"callee.hh");
469 ASSERT_TRUE(
bool(
AST));
470 CheckIncomingCalls(*
AST, CalleeH.point(),
testPath(
"callee.hh"));
471 AST = Workspace.openFile(
"caller3.hh");
472 ASSERT_TRUE(
bool(
AST));
473 CheckOutgoingCalls(*
AST, Caller3H.point(),
testPath(
"caller3.hh"),
true);
476 AST = Workspace.openFile(
"callee.cc");
477 ASSERT_TRUE(
bool(
AST));
478 CheckIncomingCalls(*
AST, CalleeC.point(),
testPath(
"callee.cc"));
479 AST = Workspace.openFile(
"caller3.cc");
480 ASSERT_TRUE(
bool(
AST));
481 CheckOutgoingCalls(*
AST, Caller3C.point(),
testPath(
"caller3.cc"),
false);
484TEST(CallHierarchy, IncomingMultiFileObjC) {
491 @interface CalleeClass
497 @implementation CalleeClass {}
502 @interface Caller1Class
509 @implementation Caller1Class {}
511 [CalleeClass [[calle^e]]];
516 @interface Caller2Class
523 @implementation Caller2Class {}
525 [Caller1Class $A[[caller1]]];
526 [Caller1Class $B[[caller1]]];
533 @implementation Caller3Class {}
535 [Caller1Class $Caller1[[caller1]]];
536 [Caller2Class $Caller2[[caller2]]];
542 Workspace.addSource("callee.mi", CalleeH.code());
543 Workspace.addSource(
"caller1.mi", Caller1H.code());
544 Workspace.addSource(
"caller2.mi", Caller2H.code());
545 Workspace.addMainFile(
"callee.m", CalleeC.code());
546 Workspace.addMainFile(
"caller1.m", Caller1C.code());
547 Workspace.addMainFile(
"caller2.m", Caller2C.code());
548 Workspace.addMainFile(
"caller3.m", Caller3C.code());
549 auto Index = Workspace.index();
552 std::vector<CallHierarchyItem> Items =
554 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
556 ASSERT_THAT(IncomingLevel1,
557 ElementsAre(AllOf(from(withName(
"caller1")),
558 iFromRanges(Caller1C.range()))));
560 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
561 ASSERT_THAT(IncomingLevel2,
562 ElementsAre(AllOf(from(withName(
"caller2")),
563 iFromRanges(Caller2C.range(
"A"),
564 Caller2C.range(
"B"))),
565 AllOf(from(withName(
"caller3")),
566 iFromRanges(Caller3C.range(
"Caller1")))));
568 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
569 ASSERT_THAT(IncomingLevel3,
570 ElementsAre(AllOf(from(withName(
"caller3")),
571 iFromRanges(Caller3C.range(
"Caller2")))));
573 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
574 EXPECT_THAT(IncomingLevel4, IsEmpty());
578 auto AST = Workspace.openFile(
"caller1.m");
579 ASSERT_TRUE(
bool(
AST));
580 CheckCallHierarchy(*
AST, Caller1C.point(),
testPath(
"caller1.m"));
583 AST = Workspace.openFile(
"callee.mi");
584 ASSERT_TRUE(
bool(
AST));
585 CheckCallHierarchy(*
AST, CalleeH.point(),
testPath(
"callee.mi"));
588 AST = Workspace.openFile(
"callee.m");
589 ASSERT_TRUE(
bool(
AST));
590 CheckCallHierarchy(*
AST, CalleeC.point(),
testPath(
"callee.m"));
593TEST(CallHierarchy, CallInLocalVarDecl) {
606 int localVar = $call2[[callee]]();
608 int caller3 = $call3[[callee]]();
611 auto AST = TU.build();
612 auto Index = TU.index();
614 std::vector<CallHierarchyItem> Items =
616 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
619 ASSERT_THAT(Incoming, ElementsAre(AllOf(from(withName(
"caller1")),
620 iFromRanges(Source.range(
"call1"))),
621 AllOf(from(withName(
"caller2")),
622 iFromRanges(Source.range(
"call2"))),
623 AllOf(from(withName(
"caller3")),
624 iFromRanges(Source.range(
"call3")))));
627TEST(CallHierarchy, HierarchyOnField) {
635 values.$Callee[[var1]];
639 auto AST = TU.build();
640 auto Index = TU.index();
642 std::vector<CallHierarchyItem> Items =
644 ASSERT_THAT(Items, ElementsAre(withName(
"var1")));
646 ASSERT_THAT(IncomingLevel1,
647 ElementsAre(AllOf(from(withName(
"caller")),
648 iFromRanges(Source.range(
"Callee")))));
651TEST(CallHierarchy, HierarchyOnVar) {
660 auto AST = TU.build();
661 auto Index = TU.index();
663 std::vector<CallHierarchyItem> Items =
665 ASSERT_THAT(Items, ElementsAre(withName(
"var")));
667 ASSERT_THAT(IncomingLevel1,
668 ElementsAre(AllOf(from(withName(
"caller")),
669 iFromRanges(Source.range(
"Callee")))));
672TEST(CallHierarchy, HierarchyOnEnumConstant) {
675 enum class Coin { heads$Heads^ , tai$Tails^ls };
677 Coin::$CallerH[[heads]];
678 Coin::$CallerT[[tails]];
682 auto AST = TU.build();
683 auto Index = TU.index();
685 std::vector<CallHierarchyItem> Items =
687 ASSERT_THAT(Items, ElementsAre(withName(
"heads")));
689 ASSERT_THAT(IncomingLevel1,
690 ElementsAre(AllOf(from(withName(
"caller")),
691 iFromRanges(Source.range(
"CallerH")))));
694 ASSERT_THAT(Items, ElementsAre(withName(
"tails")));
696 ASSERT_THAT(IncomingLevel1,
697 ElementsAre(AllOf(from(withName(
"caller")),
698 iFromRanges(Source.range(
"CallerT")))));
701TEST(CallHierarchy, CallInDifferentFileThanCaller) {
703 #define WALDO void caller() {
712 TU.HeaderCode = Header.code();
713 auto AST = TU.build();
714 auto Index = TU.index();
716 std::vector<CallHierarchyItem> Items =
718 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
726 EXPECT_THAT(Incoming,
727 ElementsAre(AllOf(from(withName(
"caller")), iFromRanges())));
730TEST(CallHierarchy, IncomingCalls) {
741 auto AST = TU.build();
742 auto Index = TU.index();
744 std::vector<CallHierarchyItem> Items =
746 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
751 UnorderedElementsAre(AllOf(from(
756TEST(CallHierarchy, OutgoingCalls) {
767 auto AST = TU.build();
768 auto Index = TU.index();
770 std::vector<CallHierarchyItem> Items =
772 ASSERT_THAT(Items, ElementsAre(withName(
"caller")));
775 EXPECT_THAT(Outgoing, UnorderedElementsAre(AllOf(
776 to(AllOf(withName(
"callee"),
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Stores and provides access to parsed AST.
void addSource(llvm::StringRef Filename, llvm::StringRef Code)
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
std::vector< CallHierarchyIncomingCall > incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index)
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
std::string testPath(PathRef File, llvm::sys::path::Style Style)
TEST(BackgroundQueueTest, Priority)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
std::vector< CallHierarchyOutgoingCall > outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index)
std::vector< CallHierarchyItem > prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath)
Get call hierarchy information at Pos.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Represents an incoming call, e.g. a caller of a method or constructor.
CallHierarchyItem from
The item that makes the call.
std::vector< Range > fromRanges
The range at which the calls appear.
Represents programming constructs like functions or constructors in the context of call hierarchy.
std::string name
The name of this item.
std::vector< SymbolTag > tags
Tags for this item.
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e....
std::vector< Range > fromRanges
The range at which this item is called.
CallHierarchyItem to
The item that is called.
static TestTU withCode(llvm::StringRef Code)