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(withSelectionRange, R,
"") {
return arg.selectionRange == R; }
50template <
class ItemMatcher>
51::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher
M) {
54template <
class ItemMatcher>
55::testing::Matcher<CallHierarchyOutgoingCall> to(ItemMatcher
M) {
58template <
class... RangeMatchers>
59::testing::Matcher<CallHierarchyIncomingCall> iFromRanges(RangeMatchers...
M) {
61 UnorderedElementsAre(
M...));
63template <
class... RangeMatchers>
64::testing::Matcher<CallHierarchyOutgoingCall> oFromRanges(RangeMatchers...
M) {
66 UnorderedElementsAre(
M...));
69TEST(CallHierarchy, IncomingOneFileCpp) {
70 Annotations Source(R
"cpp(
73 $Callee[[callee]](42);
76 $Caller1A[[caller1]]();
77 $Caller1B[[caller1]]();
80 $Caller1C[[caller1]]();
81 $Caller2[[caller2]]();
85 auto AST = TU.build();
86 auto Index = TU.index();
88 std::vector<CallHierarchyItem> Items =
90 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
94 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
"caller1"))),
95 iFromRanges(Source.range(
"Callee")))));
96 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
99 ElementsAre(AllOf(from(AllOf(withName(
"caller2"), withDetail(
"caller2"))),
100 iFromRanges(Source.range(
"Caller1A"),
101 Source.range(
"Caller1B"))),
102 AllOf(from(AllOf(withName(
"caller3"), withDetail(
"caller3"))),
103 iFromRanges(Source.range(
"Caller1C")))));
105 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
108 ElementsAre(AllOf(from(AllOf(withName(
"caller3"), withDetail(
"caller3"))),
109 iFromRanges(Source.range(
"Caller2")))));
111 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
112 EXPECT_THAT(IncomingLevel4, IsEmpty());
115TEST(CallHierarchy, IncomingOneFileObjC) {
116 Annotations Source(R
"objc(
117 @implementation MyClass {}
120 [MyClass $Callee[[callee]]];
123 [MyClass $Caller1A[[caller1]]];
124 [MyClass $Caller1B[[caller1]]];
127 [MyClass $Caller1C[[caller1]]];
128 [MyClass $Caller2[[caller2]]];
133 TU.Filename = "TestTU.m";
134 auto AST = TU.build();
135 auto Index = TU.index();
136 std::vector<CallHierarchyItem> Items =
138 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
140 ASSERT_THAT(IncomingLevel1,
141 ElementsAre(AllOf(from(AllOf(withName(
"caller1"),
142 withDetail(
"MyClass::caller1"))),
143 iFromRanges(Source.range(
"Callee")))));
144 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
145 ASSERT_THAT(IncomingLevel2,
146 ElementsAre(AllOf(from(AllOf(withName(
"caller2"),
147 withDetail(
"MyClass::caller2"))),
148 iFromRanges(Source.range(
"Caller1A"),
149 Source.range(
"Caller1B"))),
150 AllOf(from(AllOf(withName(
"caller3"),
151 withDetail(
"MyClass::caller3"))),
152 iFromRanges(Source.range(
"Caller1C")))));
154 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
155 ASSERT_THAT(IncomingLevel3,
156 ElementsAre(AllOf(from(AllOf(withName(
"caller3"),
157 withDetail(
"MyClass::caller3"))),
158 iFromRanges(Source.range(
"Caller2")))));
160 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
161 EXPECT_THAT(IncomingLevel4, IsEmpty());
164TEST(CallHierarchy, MainFileOnlyRef) {
168 Annotations Source(R
"cpp(
172 $Callee[[callee]](42);
176 $Caller1[[caller1]]();
180 auto AST = TU.build();
181 auto Index = TU.index();
183 std::vector<CallHierarchyItem> Items =
185 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
189 ElementsAre(AllOf(from(AllOf(withName(
"caller1"), withDetail(
"caller1"))),
190 iFromRanges(Source.range(
"Callee")))));
192 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
195 ElementsAre(AllOf(from(AllOf(withName(
"caller2"), withDetail(
"caller2"))),
196 iFromRanges(Source.range(
"Caller1")))));
199TEST(CallHierarchy, IncomingQualified) {
200 Annotations Source(R
"cpp(
205 void Waldo::find() {}
206 void caller1(Waldo &W) {
207 W.$Caller1[[f^ind]]();
209 void caller2(Waldo &W) {
210 W.$Caller2[[find]]();
215 auto AST = TU.build();
216 auto Index = TU.index();
218 std::vector<CallHierarchyItem> Items =
220 ASSERT_THAT(Items, ElementsAre(withName(
"Waldo::find")));
225 AllOf(from(AllOf(withName(
"caller1"), withDetail(
"ns::caller1"))),
226 iFromRanges(Source.range(
"Caller1"))),
227 AllOf(from(AllOf(withName(
"caller2"), withDetail(
"ns::caller2"))),
228 iFromRanges(Source.range(
"Caller2")))));
231TEST(CallHierarchy, OutgoingOneFile) {
233 Annotations Source(R
"cpp(
239 void Foo::caller1() {
240 $Callee[[callee]](42);
244 void caller2(ns::Foo& F) {
245 F.$Caller1A[[caller1]]();
246 F.$Caller1B[[caller1]]();
249 void call^er3(ns::Foo& F) {
250 F.$Caller1C[[caller1]]();
251 $Caller2[[caller2]](F);
255 auto AST = TU.build();
256 auto Index = TU.index();
258 std::vector<CallHierarchyItem> Items =
260 ASSERT_THAT(Items, ElementsAre(withName(
"caller3")));
265 AllOf(to(AllOf(withName(
"caller1"), withDetail(
"ns::Foo::caller1"))),
266 oFromRanges(Source.range(
"Caller1C"))),
267 AllOf(to(AllOf(withName(
"caller2"), withDetail(
"caller2"))),
268 oFromRanges(Source.range(
"Caller2")))));
270 auto OutgoingLevel2 =
outgoingCalls(OugoingLevel1[1].to, Index.get());
274 to(AllOf(withName(
"caller1"), withDetail(
"ns::Foo::caller1"))),
275 oFromRanges(Source.range(
"Caller1A"), Source.range(
"Caller1B")))));
277 auto OutgoingLevel3 =
outgoingCalls(OutgoingLevel2[0].to, Index.get());
280 ElementsAre(AllOf(to(AllOf(withName(
"callee"), withDetail(
"callee"))),
281 oFromRanges(Source.range(
"Callee")))));
283 auto OutgoingLevel4 =
outgoingCalls(OutgoingLevel3[0].to, Index.get());
284 EXPECT_THAT(OutgoingLevel4, IsEmpty());
287TEST(CallHierarchy, MultiFileCpp) {
293 Annotations CalleeH(R
"cpp(
296 Annotations CalleeC(R"cpp(
300 Annotations Caller1H(R"cpp(
305 Annotations Caller1C(R"cpp(
307 #include "caller1.hh"
314 Annotations Caller2H(R"cpp(
319 Annotations Caller2C(R"cpp(
320 #include "caller1.hh"
321 #include "caller2.hh"
324 nsa::$A[[caller1]]();
325 nsa::$B[[caller1]]();
329 Annotations Caller3H(R"cpp(
334 Annotations Caller3C(R"cpp(
335 #include "caller1.hh"
336 #include "caller2.hh"
339 $Caller1[[caller1]]();
340 nsb::$Caller2[[caller2]]();
345 TestWorkspace Workspace;
346 Workspace.addSource("callee.hh", CalleeH.code());
347 Workspace.addSource(
"caller1.hh", Caller1H.code());
348 Workspace.addSource(
"caller2.hh", Caller2H.code());
349 Workspace.addSource(
"caller3.hh", Caller3H.code());
350 Workspace.addMainFile(
"callee.cc", CalleeC.code());
351 Workspace.addMainFile(
"caller1.cc", Caller1C.code());
352 Workspace.addMainFile(
"caller2.cc", Caller2C.code());
353 Workspace.addMainFile(
"caller3.cc", Caller3C.code());
355 auto Index = Workspace.index();
357 auto CheckIncomingCalls = [&](ParsedAST &
AST, Position
Pos,
PathRef TUPath) {
358 std::vector<CallHierarchyItem> Items =
360 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
362 ASSERT_THAT(IncomingLevel1,
363 ElementsAre(AllOf(from(AllOf(withName(
"caller1"),
364 withDetail(
"nsa::caller1"))),
365 iFromRanges(Caller1C.range()))));
367 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
371 AllOf(from(AllOf(withName(
"caller2"), withDetail(
"nsb::caller2"))),
372 iFromRanges(Caller2C.range(
"A"), Caller2C.range(
"B"))),
373 AllOf(from(AllOf(withName(
"caller3"), withDetail(
"nsa::caller3"))),
374 iFromRanges(Caller3C.range(
"Caller1")))));
376 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
377 ASSERT_THAT(IncomingLevel3,
378 ElementsAre(AllOf(from(AllOf(withName(
"caller3"),
379 withDetail(
"nsa::caller3"))),
380 iFromRanges(Caller3C.range(
"Caller2")))));
382 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
383 EXPECT_THAT(IncomingLevel4, IsEmpty());
386 auto CheckOutgoingCalls = [&](ParsedAST &
AST, Position
Pos,
PathRef TUPath) {
387 std::vector<CallHierarchyItem> Items =
389 ASSERT_THAT(Items, ElementsAre(withName(
"caller3")));
394 AllOf(to(AllOf(withName(
"caller1"), withDetail(
"nsa::caller1"))),
395 oFromRanges(Caller3C.range(
"Caller1"))),
396 AllOf(to(AllOf(withName(
"caller2"), withDetail(
"nsb::caller2"))),
397 oFromRanges(Caller3C.range(
"Caller2")))));
399 auto OutgoingLevel2 =
outgoingCalls(OutgoingLevel1[1].to, Index.get());
400 ASSERT_THAT(OutgoingLevel2,
402 to(AllOf(withName(
"caller1"), withDetail(
"nsa::caller1"))),
403 oFromRanges(Caller2C.range(
"A"), Caller2C.range(
"B")))));
405 auto OutgoingLevel3 =
outgoingCalls(OutgoingLevel2[0].to, Index.get());
408 ElementsAre(AllOf(to(AllOf(withName(
"callee"), withDetail(
"callee"))),
409 oFromRanges(Caller1C.range()))));
411 auto OutgoingLevel4 =
outgoingCalls(OutgoingLevel3[0].to, Index.get());
412 EXPECT_THAT(OutgoingLevel4, IsEmpty());
416 auto AST = Workspace.openFile(
"caller1.cc");
417 ASSERT_TRUE(
bool(
AST));
418 CheckIncomingCalls(*
AST, Caller1C.point(),
testPath(
"caller1.cc"));
421 AST = Workspace.openFile(
"callee.hh");
422 ASSERT_TRUE(
bool(
AST));
423 CheckIncomingCalls(*
AST, CalleeH.point(),
testPath(
"callee.hh"));
424 AST = Workspace.openFile(
"caller3.hh");
425 ASSERT_TRUE(
bool(
AST));
426 CheckOutgoingCalls(*
AST, Caller3H.point(),
testPath(
"caller3.hh"));
429 AST = Workspace.openFile(
"callee.cc");
430 ASSERT_TRUE(
bool(
AST));
431 CheckIncomingCalls(*
AST, CalleeC.point(),
testPath(
"callee.cc"));
432 AST = Workspace.openFile(
"caller3.cc");
433 ASSERT_TRUE(
bool(
AST));
434 CheckOutgoingCalls(*
AST, Caller3C.point(),
testPath(
"caller3.cc"));
437TEST(CallHierarchy, IncomingMultiFileObjC) {
443 Annotations CalleeH(R
"objc(
444 @interface CalleeClass
448 Annotations CalleeC(R"objc(
450 @implementation CalleeClass {}
454 Annotations Caller1H(R"objc(
455 @interface Caller1Class
459 Annotations Caller1C(R"objc(
462 @implementation Caller1Class {}
464 [CalleeClass [[calle^e]]];
468 Annotations Caller2H(R"objc(
469 @interface Caller2Class
473 Annotations Caller2C(R"objc(
476 @implementation Caller2Class {}
478 [Caller1Class $A[[caller1]]];
479 [Caller1Class $B[[caller1]]];
483 Annotations Caller3C(R"objc(
486 @implementation Caller3Class {}
488 [Caller1Class $Caller1[[caller1]]];
489 [Caller2Class $Caller2[[caller2]]];
494 TestWorkspace Workspace;
495 Workspace.addSource("callee.mi", CalleeH.code());
496 Workspace.addSource(
"caller1.mi", Caller1H.code());
497 Workspace.addSource(
"caller2.mi", Caller2H.code());
498 Workspace.addMainFile(
"callee.m", CalleeC.code());
499 Workspace.addMainFile(
"caller1.m", Caller1C.code());
500 Workspace.addMainFile(
"caller2.m", Caller2C.code());
501 Workspace.addMainFile(
"caller3.m", Caller3C.code());
502 auto Index = Workspace.index();
504 auto CheckCallHierarchy = [&](ParsedAST &
AST, Position
Pos,
PathRef TUPath) {
505 std::vector<CallHierarchyItem> Items =
507 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
509 ASSERT_THAT(IncomingLevel1,
510 ElementsAre(AllOf(from(withName(
"caller1")),
511 iFromRanges(Caller1C.range()))));
513 auto IncomingLevel2 =
incomingCalls(IncomingLevel1[0].from, Index.get());
514 ASSERT_THAT(IncomingLevel2,
515 ElementsAre(AllOf(from(withName(
"caller2")),
516 iFromRanges(Caller2C.range(
"A"),
517 Caller2C.range(
"B"))),
518 AllOf(from(withName(
"caller3")),
519 iFromRanges(Caller3C.range(
"Caller1")))));
521 auto IncomingLevel3 =
incomingCalls(IncomingLevel2[0].from, Index.get());
522 ASSERT_THAT(IncomingLevel3,
523 ElementsAre(AllOf(from(withName(
"caller3")),
524 iFromRanges(Caller3C.range(
"Caller2")))));
526 auto IncomingLevel4 =
incomingCalls(IncomingLevel3[0].from, Index.get());
527 EXPECT_THAT(IncomingLevel4, IsEmpty());
531 auto AST = Workspace.openFile(
"caller1.m");
532 ASSERT_TRUE(
bool(
AST));
533 CheckCallHierarchy(*
AST, Caller1C.point(),
testPath(
"caller1.m"));
536 AST = Workspace.openFile(
"callee.mi");
537 ASSERT_TRUE(
bool(
AST));
538 CheckCallHierarchy(*
AST, CalleeH.point(),
testPath(
"callee.mi"));
541 AST = Workspace.openFile(
"callee.m");
542 ASSERT_TRUE(
bool(
AST));
543 CheckCallHierarchy(*
AST, CalleeC.point(),
testPath(
"callee.m"));
546TEST(CallHierarchy, CallInLocalVarDecl) {
553 Annotations Source(R
"cpp(
559 int localVar = $call2[[callee]]();
561 int caller3 = $call3[[callee]]();
564 auto AST = TU.build();
565 auto Index = TU.index();
567 std::vector<CallHierarchyItem> Items =
569 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
572 ASSERT_THAT(Incoming, ElementsAre(AllOf(from(withName(
"caller1")),
573 iFromRanges(Source.range(
"call1"))),
574 AllOf(from(withName(
"caller2")),
575 iFromRanges(Source.range(
"call2"))),
576 AllOf(from(withName(
"caller3")),
577 iFromRanges(Source.range(
"call3")))));
580TEST(CallHierarchy, HierarchyOnField) {
582 Annotations Source(R
"cpp(
588 values.$Callee[[var1]];
592 auto AST = TU.build();
593 auto Index = TU.index();
595 std::vector<CallHierarchyItem> Items =
597 ASSERT_THAT(Items, ElementsAre(withName(
"var1")));
599 ASSERT_THAT(IncomingLevel1,
600 ElementsAre(AllOf(from(withName(
"caller")),
601 iFromRanges(Source.range(
"Callee")))));
604TEST(CallHierarchy, HierarchyOnVar) {
606 Annotations Source(R
"cpp(
613 auto AST = TU.build();
614 auto Index = TU.index();
616 std::vector<CallHierarchyItem> Items =
618 ASSERT_THAT(Items, ElementsAre(withName(
"var")));
620 ASSERT_THAT(IncomingLevel1,
621 ElementsAre(AllOf(from(withName(
"caller")),
622 iFromRanges(Source.range(
"Callee")))));
625TEST(CallHierarchy, CallInDifferentFileThanCaller) {
626 Annotations Header(R
"cpp(
627 #define WALDO void caller() {
629 Annotations Source(R"cpp(
636 TU.HeaderCode = Header.code();
637 auto AST = TU.build();
638 auto Index = TU.index();
640 std::vector<CallHierarchyItem> Items =
642 ASSERT_THAT(Items, ElementsAre(withName(
"callee")));
650 EXPECT_THAT(Incoming,
651 ElementsAre(AllOf(from(withName(
"caller")), iFromRanges())));
const google::protobuf::Message & M
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)
std::vector< CallHierarchyOutgoingCall > outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
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)