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; }
49MATCHER_P(withSelectionRange, R,
"") {
return arg.selectionRange == R; }
51template <
class ItemMatcher>
52::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher M) {
55template <
class ItemMatcher>
56::testing::Matcher<CallHierarchyOutgoingCall> to(ItemMatcher M) {
59template <
class... RangeMatchers>
60::testing::Matcher<CallHierarchyIncomingCall> iFromRanges(RangeMatchers... M) {
62 UnorderedElementsAre(M...));
64template <
class... RangeMatchers>
65::testing::Matcher<CallHierarchyOutgoingCall> oFromRanges(RangeMatchers... M) {
67 UnorderedElementsAre(M...));
70TEST(CallHierarchy, IncomingOneFileCpp) {
74 $Callee[[callee]](42);
77 $Caller1A[[caller1]]();
78 $Caller1B[[caller1]]();
81 $Caller1C[[caller1]]();
82 $Caller2[[caller2]]();
86 auto AST = TU.build();
87 auto Index = TU.index();
89 std::vector<CallHierarchyItem> Items =
91 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
95 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
"caller1"))),
96 iFromRanges(Source.range(
"Callee")))));
97 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
100 ElementsAre(AllOf(from(AllOf(withName(
"caller2"), withDetail(
"caller2"))),
101 iFromRanges(Source.range(
"Caller1A"),
102 Source.range(
"Caller1B"))),
103 AllOf(from(AllOf(withName(
"caller3"), withDetail(
"caller3"))),
104 iFromRanges(Source.range(
"Caller1C")))));
106 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
109 ElementsAre(AllOf(from(AllOf(withName(
"caller3"), withDetail(
"caller3"))),
110 iFromRanges(Source.range(
"Caller2")))));
112 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
113 EXPECT_THAT(IncomingLevel4, IsEmpty());
116TEST(CallHierarchy, IncomingOneFileObjC) {
118 @implementation MyClass {}
121 [MyClass $Callee[[callee]]];
124 [MyClass $Caller1A[[caller1]]];
125 [MyClass $Caller1B[[caller1]]];
128 [MyClass $Caller1C[[caller1]]];
129 [MyClass $Caller2[[caller2]]];
134 TU.Filename = "TestTU.m";
135 auto AST = TU.build();
136 auto Index = TU.index();
137 std::vector<CallHierarchyItem> Items =
139 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
141 ASSERT_THAT(IncomingLevel1,
142 ElementsAre(AllOf(from(AllOf(withName(
"caller1"),
143 withDetail(
"MyClass::caller1"))),
144 iFromRanges(Source.range(
"Callee")))));
145 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
146 ASSERT_THAT(IncomingLevel2,
147 ElementsAre(AllOf(from(AllOf(withName(
"caller2"),
148 withDetail(
"MyClass::caller2"))),
149 iFromRanges(Source.range(
"Caller1A"),
150 Source.range(
"Caller1B"))),
151 AllOf(from(AllOf(withName(
"caller3"),
152 withDetail(
"MyClass::caller3"))),
153 iFromRanges(Source.range(
"Caller1C")))));
155 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
156 ASSERT_THAT(IncomingLevel3,
157 ElementsAre(AllOf(from(AllOf(withName(
"caller3"),
158 withDetail(
"MyClass::caller3"))),
159 iFromRanges(Source.range(
"Caller2")))));
161 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
162 EXPECT_THAT(IncomingLevel4, IsEmpty());
165TEST(CallHierarchy, IncomingIncludeOverrides) {
169 virtual void Func() = 0;
171 struct Implementation : public Interface {
172 void Func() override {
176 void Test(Interface& cls){
177 cls.$FuncCall[[Func]]();
181 auto AST = TU.build();
182 auto Index = TU.index();
184 std::vector<CallHierarchyItem> Items =
186 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
188 ASSERT_THAT(IncomingLevel1,
189 ElementsAre(AllOf(from(AllOf(withName(
"Func"),
190 withDetail(
"Implementation::Func"))),
191 iFromRanges(Source.range(
"Callee")))));
192 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
195 ElementsAre(AllOf(from(AllOf(withName(
"Test"), withDetail(
"Test"))),
196 iFromRanges(Source.range(
"FuncCall")))));
198 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
199 EXPECT_THAT(IncomingLevel3, IsEmpty());
202TEST(CallHierarchy, MainFileOnlyRef) {
210 $Callee[[callee]](42);
214 $Caller1[[caller1]]();
218 auto AST = TU.build();
219 auto Index = TU.index();
221 std::vector<CallHierarchyItem> Items =
223 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
227 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
"caller1"))),
228 iFromRanges(Source.range(
"Callee")))));
230 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
233 ElementsAre(AllOf(from(AllOf(withName(
"caller2"), withDetail(
"caller2"))),
234 iFromRanges(Source.range(
"Caller1")))));
237TEST(CallHierarchy, IncomingQualified) {
243 void Waldo::find() {}
244 void caller1(Waldo &W) {
245 W.$Caller1[[f^ind]]();
247 void caller2(Waldo &W) {
248 W.$Caller2[[find]]();
253 auto AST = TU.build();
254 auto Index = TU.index();
256 std::vector<CallHierarchyItem> Items =
258 ASSERT_THAT(Items, ElementsAre(withName(
"Waldo::find")));
263 AllOf(from(AllOf(withName(
"caller1"), withDetail(
"ns::caller1"))),
264 iFromRanges(Source.range(
"Caller1"))),
265 AllOf(from(AllOf(withName(
"caller2"), withDetail(
"ns::caller2"))),
266 iFromRanges(Source.range(
"Caller2")))));
269TEST(CallHierarchy, OutgoingOneFile) {
277 void Foo::caller1() {
278 $Callee[[callee]](42);
282 void caller2(ns::Foo& F) {
283 F.$Caller1A[[caller1]]();
284 F.$Caller1B[[caller1]]();
287 void call^er3(ns::Foo& F) {
288 F.$Caller1C[[caller1]]();
289 $Caller2[[caller2]](F);
293 auto AST = TU.build();
294 auto Index = TU.index();
296 std::vector<CallHierarchyItem> Items =
298 ASSERT_THAT(Items, ElementsAre(withName(
"caller3")));
303 AllOf(to(AllOf(withName(
"caller1"), withDetail(
"ns::Foo::caller1"))),
304 oFromRanges(Source.range(
"Caller1C"))),
305 AllOf(to(AllOf(withName(
"caller2"), withDetail(
"caller2"))),
306 oFromRanges(Source.range(
"Caller2")))));
308 auto OutgoingLevel2 =
outgoingCalls(OugoingLevel1[1].to, Index.get());
312 to(AllOf(withName(
"caller1"), withDetail(
"ns::Foo::caller1"))),
313 oFromRanges(Source.range(
"Caller1A"), Source.range(
"Caller1B")))));
315 auto OutgoingLevel3 =
outgoingCalls(OutgoingLevel2[0].to, Index.get());
318 ElementsAre(AllOf(to(AllOf(withName(
"callee"), withDetail(
"callee"))),
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")));
400 ASSERT_THAT(IncomingLevel1,
401 ElementsAre(AllOf(from(AllOf(withName(
"caller1"),
402 withDetail(
"nsa::caller1"))),
403 iFromRanges(Caller1C.range()))));
405 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
409 AllOf(from(AllOf(withName(
"caller2"), withDetail(
"nsb::caller2"))),
410 iFromRanges(Caller2C.range(
"A"), Caller2C.range(
"B"))),
411 AllOf(from(AllOf(withName(
"caller3"), withDetail(
"nsa::caller3"))),
412 iFromRanges(Caller3C.range(
"Caller1")))));
414 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
415 ASSERT_THAT(IncomingLevel3,
416 ElementsAre(AllOf(from(AllOf(withName(
"caller3"),
417 withDetail(
"nsa::caller3"))),
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::caller1"))),
441 IsDeclaration ? oFromRanges()
442 : oFromRanges(Caller3C.range(
"Caller1"))),
443 AllOf(to(AllOf(withName(
"caller2"), withDetail(
"nsb::caller2"))),
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::caller1"))),
451 oFromRanges(Caller2C.range(
"A"), Caller2C.range(
"B")))));
453 auto OutgoingLevel3 =
outgoingCalls(OutgoingLevel2[0].to, Index.get());
456 ElementsAre(AllOf(to(AllOf(withName(
"callee"), withDetail(
"callee"))),
457 oFromRanges(Caller1C.range()))));
459 auto OutgoingLevel4 =
outgoingCalls(OutgoingLevel3[0].to, Index.get());
460 EXPECT_THAT(OutgoingLevel4, IsEmpty());
464 auto AST = Workspace.openFile(
"caller1.cc");
465 ASSERT_TRUE(
bool(
AST));
466 CheckIncomingCalls(*
AST, Caller1C.point(),
testPath(
"caller1.cc"));
469 AST = Workspace.openFile(
"callee.hh");
470 ASSERT_TRUE(
bool(
AST));
471 CheckIncomingCalls(*
AST, CalleeH.point(),
testPath(
"callee.hh"));
472 AST = Workspace.openFile(
"caller3.hh");
473 ASSERT_TRUE(
bool(
AST));
474 CheckOutgoingCalls(*
AST, Caller3H.point(),
testPath(
"caller3.hh"),
true);
477 AST = Workspace.openFile(
"callee.cc");
478 ASSERT_TRUE(
bool(
AST));
479 CheckIncomingCalls(*
AST, CalleeC.point(),
testPath(
"callee.cc"));
480 AST = Workspace.openFile(
"caller3.cc");
481 ASSERT_TRUE(
bool(
AST));
482 CheckOutgoingCalls(*
AST, Caller3C.point(),
testPath(
"caller3.cc"),
false);
485TEST(CallHierarchy, IncomingMultiFileObjC) {
492 @interface CalleeClass
498 @implementation CalleeClass {}
503 @interface Caller1Class
510 @implementation Caller1Class {}
512 [CalleeClass [[calle^e]]];
517 @interface Caller2Class
524 @implementation Caller2Class {}
526 [Caller1Class $A[[caller1]]];
527 [Caller1Class $B[[caller1]]];
534 @implementation Caller3Class {}
536 [Caller1Class $Caller1[[caller1]]];
537 [Caller2Class $Caller2[[caller2]]];
543 Workspace.addSource("callee.mi", CalleeH.code());
544 Workspace.addSource(
"caller1.mi", Caller1H.code());
545 Workspace.addSource(
"caller2.mi", Caller2H.code());
546 Workspace.addMainFile(
"callee.m", CalleeC.code());
547 Workspace.addMainFile(
"caller1.m", Caller1C.code());
548 Workspace.addMainFile(
"caller2.m", Caller2C.code());
549 Workspace.addMainFile(
"caller3.m", Caller3C.code());
550 auto Index = Workspace.index();
553 std::vector<CallHierarchyItem> Items =
555 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
557 ASSERT_THAT(IncomingLevel1,
558 ElementsAre(AllOf(from(withName(
"caller1")),
559 iFromRanges(Caller1C.range()))));
561 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
562 ASSERT_THAT(IncomingLevel2,
563 ElementsAre(AllOf(from(withName(
"caller2")),
564 iFromRanges(Caller2C.range(
"A"),
565 Caller2C.range(
"B"))),
566 AllOf(from(withName(
"caller3")),
567 iFromRanges(Caller3C.range(
"Caller1")))));
569 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
570 ASSERT_THAT(IncomingLevel3,
571 ElementsAre(AllOf(from(withName(
"caller3")),
572 iFromRanges(Caller3C.range(
"Caller2")))));
574 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
575 EXPECT_THAT(IncomingLevel4, IsEmpty());
579 auto AST = Workspace.openFile(
"caller1.m");
580 ASSERT_TRUE(
bool(
AST));
581 CheckCallHierarchy(*
AST, Caller1C.point(),
testPath(
"caller1.m"));
584 AST = Workspace.openFile(
"callee.mi");
585 ASSERT_TRUE(
bool(
AST));
586 CheckCallHierarchy(*
AST, CalleeH.point(),
testPath(
"callee.mi"));
589 AST = Workspace.openFile(
"callee.m");
590 ASSERT_TRUE(
bool(
AST));
591 CheckCallHierarchy(*
AST, CalleeC.point(),
testPath(
"callee.m"));
594TEST(CallHierarchy, CallInLocalVarDecl) {
607 int localVar = $call2[[callee]]();
609 int caller3 = $call3[[callee]]();
612 auto AST = TU.build();
613 auto Index = TU.index();
615 std::vector<CallHierarchyItem> Items =
617 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
620 ASSERT_THAT(Incoming, ElementsAre(AllOf(from(withName(
"caller1")),
621 iFromRanges(Source.range(
"call1"))),
622 AllOf(from(withName(
"caller2")),
623 iFromRanges(Source.range(
"call2"))),
624 AllOf(from(withName(
"caller3")),
625 iFromRanges(Source.range(
"call3")))));
628TEST(CallHierarchy, HierarchyOnField) {
636 values.$Callee[[var1]];
640 auto AST = TU.build();
641 auto Index = TU.index();
643 std::vector<CallHierarchyItem> Items =
645 ASSERT_THAT(Items, ElementsAre(withName(
"var1")));
647 ASSERT_THAT(IncomingLevel1,
648 ElementsAre(AllOf(from(withName(
"caller")),
649 iFromRanges(Source.range(
"Callee")))));
652TEST(CallHierarchy, HierarchyOnVar) {
661 auto AST = TU.build();
662 auto Index = TU.index();
664 std::vector<CallHierarchyItem> Items =
666 ASSERT_THAT(Items, ElementsAre(withName(
"var")));
668 ASSERT_THAT(IncomingLevel1,
669 ElementsAre(AllOf(from(withName(
"caller")),
670 iFromRanges(Source.range(
"Callee")))));
673TEST(CallHierarchy, HierarchyOnEnumConstant) {
676 enum class Coin { heads$Heads^ , tai$Tails^ls };
678 Coin::$CallerH[[heads]];
679 Coin::$CallerT[[tails]];
683 auto AST = TU.build();
684 auto Index = TU.index();
686 std::vector<CallHierarchyItem> Items =
688 ASSERT_THAT(Items, ElementsAre(withName(
"heads")));
690 ASSERT_THAT(IncomingLevel1,
691 ElementsAre(AllOf(from(withName(
"caller")),
692 iFromRanges(Source.range(
"CallerH")))));
695 ASSERT_THAT(Items, ElementsAre(withName(
"tails")));
697 ASSERT_THAT(IncomingLevel1,
698 ElementsAre(AllOf(from(withName(
"caller")),
699 iFromRanges(Source.range(
"CallerT")))));
702TEST(CallHierarchy, CallInDifferentFileThanCaller) {
704 #define WALDO void caller() {
713 TU.HeaderCode = Header.code();
714 auto AST = TU.build();
715 auto Index = TU.index();
717 std::vector<CallHierarchyItem> Items =
719 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
727 EXPECT_THAT(Incoming,
728 ElementsAre(AllOf(from(withName(
"caller")), iFromRanges())));
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.
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)