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, MainFileOnlyRef) {
173 $Callee[[callee]](42);
177 $Caller1[[caller1]]();
181 auto AST = TU.build();
182 auto Index = TU.index();
184 std::vector<CallHierarchyItem> Items =
186 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
190 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
"caller1"))),
191 iFromRanges(Source.range(
"Callee")))));
193 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
196 ElementsAre(AllOf(from(AllOf(withName(
"caller2"), withDetail(
"caller2"))),
197 iFromRanges(Source.range(
"Caller1")))));
200TEST(CallHierarchy, IncomingQualified) {
206 void Waldo::find() {}
207 void caller1(Waldo &W) {
208 W.$Caller1[[f^ind]]();
210 void caller2(Waldo &W) {
211 W.$Caller2[[find]]();
216 auto AST = TU.build();
217 auto Index = TU.index();
219 std::vector<CallHierarchyItem> Items =
221 ASSERT_THAT(Items, ElementsAre(withName(
"Waldo::find")));
226 AllOf(from(AllOf(withName(
"caller1"), withDetail(
"ns::caller1"))),
227 iFromRanges(Source.range(
"Caller1"))),
228 AllOf(from(AllOf(withName(
"caller2"), withDetail(
"ns::caller2"))),
229 iFromRanges(Source.range(
"Caller2")))));
232TEST(CallHierarchy, OutgoingOneFile) {
240 void Foo::caller1() {
241 $Callee[[callee]](42);
245 void caller2(ns::Foo& F) {
246 F.$Caller1A[[caller1]]();
247 F.$Caller1B[[caller1]]();
250 void call^er3(ns::Foo& F) {
251 F.$Caller1C[[caller1]]();
252 $Caller2[[caller2]](F);
256 auto AST = TU.build();
257 auto Index = TU.index();
259 std::vector<CallHierarchyItem> Items =
261 ASSERT_THAT(Items, ElementsAre(withName(
"caller3")));
266 AllOf(to(AllOf(withName(
"caller1"), withDetail(
"ns::Foo::caller1"))),
267 oFromRanges(Source.range(
"Caller1C"))),
268 AllOf(to(AllOf(withName(
"caller2"), withDetail(
"caller2"))),
269 oFromRanges(Source.range(
"Caller2")))));
271 auto OutgoingLevel2 =
outgoingCalls(OugoingLevel1[1].to, Index.get());
275 to(AllOf(withName(
"caller1"), withDetail(
"ns::Foo::caller1"))),
276 oFromRanges(Source.range(
"Caller1A"), Source.range(
"Caller1B")))));
278 auto OutgoingLevel3 =
outgoingCalls(OutgoingLevel2[0].to, Index.get());
281 ElementsAre(AllOf(to(AllOf(withName(
"callee"), withDetail(
"callee"))),
282 oFromRanges(Source.range(
"Callee")))));
284 auto OutgoingLevel4 =
outgoingCalls(OutgoingLevel3[0].to, Index.get());
285 EXPECT_THAT(OutgoingLevel4, IsEmpty());
288TEST(CallHierarchy, MultiFileCpp) {
308 #include "caller1.hh"
321 #include "caller1.hh"
322 #include "caller2.hh"
325 nsa::$A[[caller1]]();
326 nsa::$B[[caller1]]();
336 #include "caller1.hh"
337 #include "caller2.hh"
340 $Caller1[[caller1]]();
341 nsb::$Caller2[[caller2]]();
347 Workspace.addSource("callee.hh", CalleeH.code());
348 Workspace.addSource(
"caller1.hh", Caller1H.code());
349 Workspace.addSource(
"caller2.hh", Caller2H.code());
350 Workspace.addSource(
"caller3.hh", Caller3H.code());
351 Workspace.addMainFile(
"callee.cc", CalleeC.code());
352 Workspace.addMainFile(
"caller1.cc", Caller1C.code());
353 Workspace.addMainFile(
"caller2.cc", Caller2C.code());
354 Workspace.addMainFile(
"caller3.cc", Caller3C.code());
356 auto Index = Workspace.index();
359 std::vector<CallHierarchyItem> Items =
361 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
363 ASSERT_THAT(IncomingLevel1,
364 ElementsAre(AllOf(from(AllOf(withName(
"caller1"),
365 withDetail(
"nsa::caller1"))),
366 iFromRanges(Caller1C.range()))));
368 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
372 AllOf(from(AllOf(withName(
"caller2"), withDetail(
"nsb::caller2"))),
373 iFromRanges(Caller2C.range(
"A"), Caller2C.range(
"B"))),
374 AllOf(from(AllOf(withName(
"caller3"), withDetail(
"nsa::caller3"))),
375 iFromRanges(Caller3C.range(
"Caller1")))));
377 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
378 ASSERT_THAT(IncomingLevel3,
379 ElementsAre(AllOf(from(AllOf(withName(
"caller3"),
380 withDetail(
"nsa::caller3"))),
381 iFromRanges(Caller3C.range(
"Caller2")))));
383 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
384 EXPECT_THAT(IncomingLevel4, IsEmpty());
388 bool IsDeclaration) {
389 std::vector<CallHierarchyItem> Items =
395 withFile(
testPath(IsDeclaration ?
"caller3.hh" :
"caller3.cc")))));
403 AllOf(to(AllOf(withName(
"caller1"), withDetail(
"nsa::caller1"))),
404 IsDeclaration ? oFromRanges()
405 : oFromRanges(Caller3C.range(
"Caller1"))),
406 AllOf(to(AllOf(withName(
"caller2"), withDetail(
"nsb::caller2"))),
407 IsDeclaration ? oFromRanges()
408 : oFromRanges(Caller3C.range(
"Caller2")))));
410 auto OutgoingLevel2 =
outgoingCalls(OutgoingLevel1[1].to, Index.get());
411 ASSERT_THAT(OutgoingLevel2,
413 to(AllOf(withName(
"caller1"), withDetail(
"nsa::caller1"))),
414 oFromRanges(Caller2C.range(
"A"), Caller2C.range(
"B")))));
416 auto OutgoingLevel3 =
outgoingCalls(OutgoingLevel2[0].to, Index.get());
419 ElementsAre(AllOf(to(AllOf(withName(
"callee"), withDetail(
"callee"))),
420 oFromRanges(Caller1C.range()))));
422 auto OutgoingLevel4 =
outgoingCalls(OutgoingLevel3[0].to, Index.get());
423 EXPECT_THAT(OutgoingLevel4, IsEmpty());
427 auto AST = Workspace.openFile(
"caller1.cc");
428 ASSERT_TRUE(
bool(
AST));
429 CheckIncomingCalls(*
AST, Caller1C.point(),
testPath(
"caller1.cc"));
432 AST = Workspace.openFile(
"callee.hh");
433 ASSERT_TRUE(
bool(
AST));
434 CheckIncomingCalls(*
AST, CalleeH.point(),
testPath(
"callee.hh"));
435 AST = Workspace.openFile(
"caller3.hh");
436 ASSERT_TRUE(
bool(
AST));
437 CheckOutgoingCalls(*
AST, Caller3H.point(),
testPath(
"caller3.hh"),
true);
440 AST = Workspace.openFile(
"callee.cc");
441 ASSERT_TRUE(
bool(
AST));
442 CheckIncomingCalls(*
AST, CalleeC.point(),
testPath(
"callee.cc"));
443 AST = Workspace.openFile(
"caller3.cc");
444 ASSERT_TRUE(
bool(
AST));
445 CheckOutgoingCalls(*
AST, Caller3C.point(),
testPath(
"caller3.cc"),
false);
448TEST(CallHierarchy, IncomingMultiFileObjC) {
455 @interface CalleeClass
461 @implementation CalleeClass {}
466 @interface Caller1Class
473 @implementation Caller1Class {}
475 [CalleeClass [[calle^e]]];
480 @interface Caller2Class
487 @implementation Caller2Class {}
489 [Caller1Class $A[[caller1]]];
490 [Caller1Class $B[[caller1]]];
497 @implementation Caller3Class {}
499 [Caller1Class $Caller1[[caller1]]];
500 [Caller2Class $Caller2[[caller2]]];
506 Workspace.addSource("callee.mi", CalleeH.code());
507 Workspace.addSource(
"caller1.mi", Caller1H.code());
508 Workspace.addSource(
"caller2.mi", Caller2H.code());
509 Workspace.addMainFile(
"callee.m", CalleeC.code());
510 Workspace.addMainFile(
"caller1.m", Caller1C.code());
511 Workspace.addMainFile(
"caller2.m", Caller2C.code());
512 Workspace.addMainFile(
"caller3.m", Caller3C.code());
513 auto Index = Workspace.index();
516 std::vector<CallHierarchyItem> Items =
518 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
520 ASSERT_THAT(IncomingLevel1,
521 ElementsAre(AllOf(from(withName(
"caller1")),
522 iFromRanges(Caller1C.range()))));
524 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
525 ASSERT_THAT(IncomingLevel2,
526 ElementsAre(AllOf(from(withName(
"caller2")),
527 iFromRanges(Caller2C.range(
"A"),
528 Caller2C.range(
"B"))),
529 AllOf(from(withName(
"caller3")),
530 iFromRanges(Caller3C.range(
"Caller1")))));
532 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
533 ASSERT_THAT(IncomingLevel3,
534 ElementsAre(AllOf(from(withName(
"caller3")),
535 iFromRanges(Caller3C.range(
"Caller2")))));
537 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
538 EXPECT_THAT(IncomingLevel4, IsEmpty());
542 auto AST = Workspace.openFile(
"caller1.m");
543 ASSERT_TRUE(
bool(
AST));
544 CheckCallHierarchy(*
AST, Caller1C.point(),
testPath(
"caller1.m"));
547 AST = Workspace.openFile(
"callee.mi");
548 ASSERT_TRUE(
bool(
AST));
549 CheckCallHierarchy(*
AST, CalleeH.point(),
testPath(
"callee.mi"));
552 AST = Workspace.openFile(
"callee.m");
553 ASSERT_TRUE(
bool(
AST));
554 CheckCallHierarchy(*
AST, CalleeC.point(),
testPath(
"callee.m"));
557TEST(CallHierarchy, CallInLocalVarDecl) {
570 int localVar = $call2[[callee]]();
572 int caller3 = $call3[[callee]]();
575 auto AST = TU.build();
576 auto Index = TU.index();
578 std::vector<CallHierarchyItem> Items =
580 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
583 ASSERT_THAT(Incoming, ElementsAre(AllOf(from(withName(
"caller1")),
584 iFromRanges(Source.range(
"call1"))),
585 AllOf(from(withName(
"caller2")),
586 iFromRanges(Source.range(
"call2"))),
587 AllOf(from(withName(
"caller3")),
588 iFromRanges(Source.range(
"call3")))));
591TEST(CallHierarchy, HierarchyOnField) {
599 values.$Callee[[var1]];
603 auto AST = TU.build();
604 auto Index = TU.index();
606 std::vector<CallHierarchyItem> Items =
608 ASSERT_THAT(Items, ElementsAre(withName(
"var1")));
610 ASSERT_THAT(IncomingLevel1,
611 ElementsAre(AllOf(from(withName(
"caller")),
612 iFromRanges(Source.range(
"Callee")))));
615TEST(CallHierarchy, HierarchyOnVar) {
624 auto AST = TU.build();
625 auto Index = TU.index();
627 std::vector<CallHierarchyItem> Items =
629 ASSERT_THAT(Items, ElementsAre(withName(
"var")));
631 ASSERT_THAT(IncomingLevel1,
632 ElementsAre(AllOf(from(withName(
"caller")),
633 iFromRanges(Source.range(
"Callee")))));
636TEST(CallHierarchy, HierarchyOnEnumConstant) {
639 enum class Coin { heads$Heads^ , tai$Tails^ls };
641 Coin::$CallerH[[heads]];
642 Coin::$CallerT[[tails]];
646 auto AST = TU.build();
647 auto Index = TU.index();
649 std::vector<CallHierarchyItem> Items =
651 ASSERT_THAT(Items, ElementsAre(withName(
"heads")));
653 ASSERT_THAT(IncomingLevel1,
654 ElementsAre(AllOf(from(withName(
"caller")),
655 iFromRanges(Source.range(
"CallerH")))));
658 ASSERT_THAT(Items, ElementsAre(withName(
"tails")));
660 ASSERT_THAT(IncomingLevel1,
661 ElementsAre(AllOf(from(withName(
"caller")),
662 iFromRanges(Source.range(
"CallerT")))));
665TEST(CallHierarchy, CallInDifferentFileThanCaller) {
667 #define WALDO void caller() {
676 TU.HeaderCode = Header.code();
677 auto AST = TU.build();
678 auto Index = TU.index();
680 std::vector<CallHierarchyItem> Items =
682 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
690 EXPECT_THAT(Incoming,
691 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)