clang-tools 20.0.0git
CallHierarchyTests.cpp
Go to the documentation of this file.
1//===-- CallHierarchyTests.cpp ---------------------------*- C++ -*-------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#include "Annotations.h"
9#include "ParsedAST.h"
10#include "TestFS.h"
11#include "TestTU.h"
12#include "TestWorkspace.h"
13#include "XRefs.h"
14#include "llvm/Support/Path.h"
15#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17
18namespace clang {
19namespace clangd {
20
21llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
22 const CallHierarchyItem &Item) {
23 return Stream << Item.name << "@" << Item.selectionRange;
24}
25
26llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
28 Stream << "{ from: " << Call.from << ", ranges: [";
29 for (const auto &R : Call.fromRanges) {
30 Stream << R;
31 Stream << ", ";
32 }
33 return Stream << "] }";
34}
35
36namespace {
37
38using ::testing::AllOf;
39using ::testing::ElementsAre;
40using ::testing::Field;
41using ::testing::IsEmpty;
42using ::testing::Matcher;
43using ::testing::UnorderedElementsAre;
44
45// Helpers for matching call hierarchy data structures.
46MATCHER_P(withName, N, "") { return arg.name == N; }
47MATCHER_P(withDetail, N, "") { return arg.detail == N; }
48MATCHER_P(withSelectionRange, R, "") { return arg.selectionRange == R; }
49
50template <class ItemMatcher>
51::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher M) {
53}
54template <class ItemMatcher>
55::testing::Matcher<CallHierarchyOutgoingCall> to(ItemMatcher M) {
57}
58template <class... RangeMatchers>
59::testing::Matcher<CallHierarchyIncomingCall> iFromRanges(RangeMatchers... M) {
61 UnorderedElementsAre(M...));
62}
63template <class... RangeMatchers>
64::testing::Matcher<CallHierarchyOutgoingCall> oFromRanges(RangeMatchers... M) {
66 UnorderedElementsAre(M...));
67}
68
69TEST(CallHierarchy, IncomingOneFileCpp) {
70 Annotations Source(R"cpp(
71 void call^ee(int);
72 void caller1() {
73 $Callee[[callee]](42);
74 }
75 void caller2() {
76 $Caller1A[[caller1]]();
77 $Caller1B[[caller1]]();
78 }
79 void caller3() {
80 $Caller1C[[caller1]]();
81 $Caller2[[caller2]]();
82 }
83 )cpp");
84 TestTU TU = TestTU::withCode(Source.code());
85 auto AST = TU.build();
86 auto Index = TU.index();
87
88 std::vector<CallHierarchyItem> Items =
89 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
90 ASSERT_THAT(Items, ElementsAre(withName("callee")));
91 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
92 ASSERT_THAT(
93 IncomingLevel1,
94 ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))),
95 iFromRanges(Source.range("Callee")))));
96 auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
97 ASSERT_THAT(
98 IncomingLevel2,
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")))));
104
105 auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
106 ASSERT_THAT(
107 IncomingLevel3,
108 ElementsAre(AllOf(from(AllOf(withName("caller3"), withDetail("caller3"))),
109 iFromRanges(Source.range("Caller2")))));
110
111 auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
112 EXPECT_THAT(IncomingLevel4, IsEmpty());
113}
114
115TEST(CallHierarchy, IncomingOneFileObjC) {
116 Annotations Source(R"objc(
117 @implementation MyClass {}
118 +(void)call^ee {}
119 +(void) caller1 {
120 [MyClass $Callee[[callee]]];
121 }
122 +(void) caller2 {
123 [MyClass $Caller1A[[caller1]]];
124 [MyClass $Caller1B[[caller1]]];
125 }
126 +(void) caller3 {
127 [MyClass $Caller1C[[caller1]]];
128 [MyClass $Caller2[[caller2]]];
129 }
130 @end
131 )objc");
132 TestTU TU = TestTU::withCode(Source.code());
133 TU.Filename = "TestTU.m";
134 auto AST = TU.build();
135 auto Index = TU.index();
136 std::vector<CallHierarchyItem> Items =
137 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
138 ASSERT_THAT(Items, ElementsAre(withName("callee")));
139 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
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")))));
153
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")))));
159
160 auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
161 EXPECT_THAT(IncomingLevel4, IsEmpty());
162}
163
164TEST(CallHierarchy, MainFileOnlyRef) {
165 // In addition to testing that we store refs to main-file only symbols,
166 // this tests that anonymous namespaces do not interfere with the
167 // symbol re-identification process in callHierarchyItemToSymbo().
168 Annotations Source(R"cpp(
169 void call^ee(int);
170 namespace {
171 void caller1() {
172 $Callee[[callee]](42);
173 }
174 }
175 void caller2() {
176 $Caller1[[caller1]]();
177 }
178 )cpp");
179 TestTU TU = TestTU::withCode(Source.code());
180 auto AST = TU.build();
181 auto Index = TU.index();
182
183 std::vector<CallHierarchyItem> Items =
184 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
185 ASSERT_THAT(Items, ElementsAre(withName("callee")));
186 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
187 ASSERT_THAT(
188 IncomingLevel1,
189 ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))),
190 iFromRanges(Source.range("Callee")))));
191
192 auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
193 EXPECT_THAT(
194 IncomingLevel2,
195 ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("caller2"))),
196 iFromRanges(Source.range("Caller1")))));
197}
198
199TEST(CallHierarchy, IncomingQualified) {
200 Annotations Source(R"cpp(
201 namespace ns {
202 struct Waldo {
203 void find();
204 };
205 void Waldo::find() {}
206 void caller1(Waldo &W) {
207 W.$Caller1[[f^ind]]();
208 }
209 void caller2(Waldo &W) {
210 W.$Caller2[[find]]();
211 }
212 }
213 )cpp");
214 TestTU TU = TestTU::withCode(Source.code());
215 auto AST = TU.build();
216 auto Index = TU.index();
217
218 std::vector<CallHierarchyItem> Items =
219 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
220 ASSERT_THAT(Items, ElementsAre(withName("Waldo::find")));
221 auto Incoming = incomingCalls(Items[0], Index.get());
222 EXPECT_THAT(
223 Incoming,
224 ElementsAre(
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")))));
229}
230
231TEST(CallHierarchy, OutgoingOneFile) {
232 // Test outgoing call on the main file, with namespaces and methods
233 Annotations Source(R"cpp(
234 void callee(int);
235 namespace ns {
236 struct Foo {
237 void caller1();
238 };
239 void Foo::caller1() {
240 $Callee[[callee]](42);
241 }
242 }
243 namespace {
244 void caller2(ns::Foo& F) {
245 F.$Caller1A[[caller1]]();
246 F.$Caller1B[[caller1]]();
247 }
248 }
249 void call^er3(ns::Foo& F) {
250 F.$Caller1C[[caller1]]();
251 $Caller2[[caller2]](F);
252 }
253 )cpp");
254 TestTU TU = TestTU::withCode(Source.code());
255 auto AST = TU.build();
256 auto Index = TU.index();
257
258 std::vector<CallHierarchyItem> Items =
259 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
260 ASSERT_THAT(Items, ElementsAre(withName("caller3")));
261 auto OugoingLevel1 = outgoingCalls(Items[0], Index.get());
262 ASSERT_THAT(
263 OugoingLevel1,
264 ElementsAre(
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")))));
269
270 auto OutgoingLevel2 = outgoingCalls(OugoingLevel1[1].to, Index.get());
271 ASSERT_THAT(
272 OutgoingLevel2,
273 ElementsAre(AllOf(
274 to(AllOf(withName("caller1"), withDetail("ns::Foo::caller1"))),
275 oFromRanges(Source.range("Caller1A"), Source.range("Caller1B")))));
276
277 auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get());
278 ASSERT_THAT(
279 OutgoingLevel3,
280 ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))),
281 oFromRanges(Source.range("Callee")))));
282
283 auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get());
284 EXPECT_THAT(OutgoingLevel4, IsEmpty());
285}
286
287TEST(CallHierarchy, MultiFileCpp) {
288 // The test uses a .hh suffix for header files to get clang
289 // to parse them in C++ mode. .h files are parsed in C mode
290 // by default, which causes problems because e.g. symbol
291 // USRs are different in C mode (do not include function signatures).
292
293 Annotations CalleeH(R"cpp(
294 void calle^e(int);
295 )cpp");
296 Annotations CalleeC(R"cpp(
297 #include "callee.hh"
298 void calle^e(int) {}
299 )cpp");
300 Annotations Caller1H(R"cpp(
301 namespace nsa {
302 void caller1();
303 }
304 )cpp");
305 Annotations Caller1C(R"cpp(
306 #include "callee.hh"
307 #include "caller1.hh"
308 namespace nsa {
309 void caller1() {
310 [[calle^e]](42);
311 }
312 }
313 )cpp");
314 Annotations Caller2H(R"cpp(
315 namespace nsb {
316 void caller2();
317 }
318 )cpp");
319 Annotations Caller2C(R"cpp(
320 #include "caller1.hh"
321 #include "caller2.hh"
322 namespace nsb {
323 void caller2() {
324 nsa::$A[[caller1]]();
325 nsa::$B[[caller1]]();
326 }
327 }
328 )cpp");
329 Annotations Caller3H(R"cpp(
330 namespace nsa {
331 void call^er3();
332 }
333 )cpp");
334 Annotations Caller3C(R"cpp(
335 #include "caller1.hh"
336 #include "caller2.hh"
337 namespace nsa {
338 void call^er3() {
339 $Caller1[[caller1]]();
340 nsb::$Caller2[[caller2]]();
341 }
342 }
343 )cpp");
344
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());
354
355 auto Index = Workspace.index();
356
357 auto CheckIncomingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
358 std::vector<CallHierarchyItem> Items =
359 prepareCallHierarchy(AST, Pos, TUPath);
360 ASSERT_THAT(Items, ElementsAre(withName("callee")));
361 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
362 ASSERT_THAT(IncomingLevel1,
363 ElementsAre(AllOf(from(AllOf(withName("caller1"),
364 withDetail("nsa::caller1"))),
365 iFromRanges(Caller1C.range()))));
366
367 auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
368 ASSERT_THAT(
369 IncomingLevel2,
370 ElementsAre(
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")))));
375
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")))));
381
382 auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
383 EXPECT_THAT(IncomingLevel4, IsEmpty());
384 };
385
386 auto CheckOutgoingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
387 std::vector<CallHierarchyItem> Items =
388 prepareCallHierarchy(AST, Pos, TUPath);
389 ASSERT_THAT(Items, ElementsAre(withName("caller3")));
390 auto OutgoingLevel1 = outgoingCalls(Items[0], Index.get());
391 ASSERT_THAT(
392 OutgoingLevel1,
393 ElementsAre(
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")))));
398
399 auto OutgoingLevel2 = outgoingCalls(OutgoingLevel1[1].to, Index.get());
400 ASSERT_THAT(OutgoingLevel2,
401 ElementsAre(AllOf(
402 to(AllOf(withName("caller1"), withDetail("nsa::caller1"))),
403 oFromRanges(Caller2C.range("A"), Caller2C.range("B")))));
404
405 auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get());
406 ASSERT_THAT(
407 OutgoingLevel3,
408 ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))),
409 oFromRanges(Caller1C.range()))));
410
411 auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get());
412 EXPECT_THAT(OutgoingLevel4, IsEmpty());
413 };
414
415 // Check that invoking from a call site works.
416 auto AST = Workspace.openFile("caller1.cc");
417 ASSERT_TRUE(bool(AST));
418 CheckIncomingCalls(*AST, Caller1C.point(), testPath("caller1.cc"));
419
420 // Check that invoking from the declaration site works.
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"));
427
428 // Check that invoking from the definition site works.
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"));
435}
436
437TEST(CallHierarchy, IncomingMultiFileObjC) {
438 // The test uses a .mi suffix for header files to get clang
439 // to parse them in ObjC mode. .h files are parsed in C mode
440 // by default, which causes problems because e.g. symbol
441 // USRs are different in C mode (do not include function signatures).
442
443 Annotations CalleeH(R"objc(
444 @interface CalleeClass
445 +(void)call^ee;
446 @end
447 )objc");
448 Annotations CalleeC(R"objc(
449 #import "callee.mi"
450 @implementation CalleeClass {}
451 +(void)call^ee {}
452 @end
453 )objc");
454 Annotations Caller1H(R"objc(
455 @interface Caller1Class
456 +(void)caller1;
457 @end
458 )objc");
459 Annotations Caller1C(R"objc(
460 #import "callee.mi"
461 #import "caller1.mi"
462 @implementation Caller1Class {}
463 +(void)caller1 {
464 [CalleeClass [[calle^e]]];
465 }
466 @end
467 )objc");
468 Annotations Caller2H(R"objc(
469 @interface Caller2Class
470 +(void)caller2;
471 @end
472 )objc");
473 Annotations Caller2C(R"objc(
474 #import "caller1.mi"
475 #import "caller2.mi"
476 @implementation Caller2Class {}
477 +(void)caller2 {
478 [Caller1Class $A[[caller1]]];
479 [Caller1Class $B[[caller1]]];
480 }
481 @end
482 )objc");
483 Annotations Caller3C(R"objc(
484 #import "caller1.mi"
485 #import "caller2.mi"
486 @implementation Caller3Class {}
487 +(void)caller3 {
488 [Caller1Class $Caller1[[caller1]]];
489 [Caller2Class $Caller2[[caller2]]];
490 }
491 @end
492 )objc");
493
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();
503
504 auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
505 std::vector<CallHierarchyItem> Items =
506 prepareCallHierarchy(AST, Pos, TUPath);
507 ASSERT_THAT(Items, ElementsAre(withName("callee")));
508 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
509 ASSERT_THAT(IncomingLevel1,
510 ElementsAre(AllOf(from(withName("caller1")),
511 iFromRanges(Caller1C.range()))));
512
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")))));
520
521 auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
522 ASSERT_THAT(IncomingLevel3,
523 ElementsAre(AllOf(from(withName("caller3")),
524 iFromRanges(Caller3C.range("Caller2")))));
525
526 auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
527 EXPECT_THAT(IncomingLevel4, IsEmpty());
528 };
529
530 // Check that invoking from a call site works.
531 auto AST = Workspace.openFile("caller1.m");
532 ASSERT_TRUE(bool(AST));
533 CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1.m"));
534
535 // Check that invoking from the declaration site works.
536 AST = Workspace.openFile("callee.mi");
537 ASSERT_TRUE(bool(AST));
538 CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee.mi"));
539
540 // Check that invoking from the definition site works.
541 AST = Workspace.openFile("callee.m");
542 ASSERT_TRUE(bool(AST));
543 CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.m"));
544}
545
546TEST(CallHierarchy, CallInLocalVarDecl) {
547 // Tests that local variable declarations are not treated as callers
548 // (they're not indexed, so they can't be represented as call hierarchy
549 // items); instead, the caller should be the containing function.
550 // However, namespace-scope variable declarations should be treated as
551 // callers because those are indexed and there is no enclosing entity
552 // that would be a useful caller.
553 Annotations Source(R"cpp(
554 int call^ee();
555 void caller1() {
556 $call1[[callee]]();
557 }
558 void caller2() {
559 int localVar = $call2[[callee]]();
560 }
561 int caller3 = $call3[[callee]]();
562 )cpp");
563 TestTU TU = TestTU::withCode(Source.code());
564 auto AST = TU.build();
565 auto Index = TU.index();
566
567 std::vector<CallHierarchyItem> Items =
568 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
569 ASSERT_THAT(Items, ElementsAre(withName("callee")));
570
571 auto Incoming = incomingCalls(Items[0], Index.get());
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")))));
578}
579
580TEST(CallHierarchy, HierarchyOnField) {
581 // Tests that the call hierarchy works on fields.
582 Annotations Source(R"cpp(
583 struct Vars {
584 int v^ar1 = 1;
585 };
586 void caller() {
587 Vars values;
588 values.$Callee[[var1]];
589 }
590 )cpp");
591 TestTU TU = TestTU::withCode(Source.code());
592 auto AST = TU.build();
593 auto Index = TU.index();
594
595 std::vector<CallHierarchyItem> Items =
596 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
597 ASSERT_THAT(Items, ElementsAre(withName("var1")));
598 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
599 ASSERT_THAT(IncomingLevel1,
600 ElementsAre(AllOf(from(withName("caller")),
601 iFromRanges(Source.range("Callee")))));
602}
603
604TEST(CallHierarchy, HierarchyOnVar) {
605 // Tests that the call hierarchy works on non-local variables.
606 Annotations Source(R"cpp(
607 int v^ar = 1;
608 void caller() {
609 $Callee[[var]];
610 }
611 )cpp");
612 TestTU TU = TestTU::withCode(Source.code());
613 auto AST = TU.build();
614 auto Index = TU.index();
615
616 std::vector<CallHierarchyItem> Items =
617 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
618 ASSERT_THAT(Items, ElementsAre(withName("var")));
619 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
620 ASSERT_THAT(IncomingLevel1,
621 ElementsAre(AllOf(from(withName("caller")),
622 iFromRanges(Source.range("Callee")))));
623}
624
625TEST(CallHierarchy, CallInDifferentFileThanCaller) {
626 Annotations Header(R"cpp(
627 #define WALDO void caller() {
628 )cpp");
629 Annotations Source(R"cpp(
630 void call^ee();
631 WALDO
632 callee();
633 }
634 )cpp");
635 auto TU = TestTU::withCode(Source.code());
636 TU.HeaderCode = Header.code();
637 auto AST = TU.build();
638 auto Index = TU.index();
639
640 std::vector<CallHierarchyItem> Items =
641 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
642 ASSERT_THAT(Items, ElementsAre(withName("callee")));
643
644 auto Incoming = incomingCalls(Items[0], Index.get());
645
646 // The only call site is in the source file, which is a different file from
647 // the declaration of the function containing the call, which is in the
648 // header. The protocol does not allow us to represent such calls, so we drop
649 // them. (The call hierarchy item itself is kept.)
650 EXPECT_THAT(Incoming,
651 ElementsAre(AllOf(from(withName("caller")), iFromRanges())));
652}
653
654} // namespace
655} // namespace clangd
656} // namespace clang
size_t Pos
const google::protobuf::Message & M
Definition: Server.cpp:356
std::vector< CallHierarchyIncomingCall > incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index)
Definition: XRefs.cpp:2242
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
TEST(BackgroundQueueTest, Priority)
std::vector< CallHierarchyOutgoingCall > outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index)
Definition: XRefs.cpp:2311
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:29
std::vector< CallHierarchyItem > prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath)
Get call hierarchy information at Pos.
Definition: XRefs.cpp:2217
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Represents an incoming call, e.g. a caller of a method or constructor.
Definition: Protocol.h:1612
CallHierarchyItem from
The item that makes the call.
Definition: Protocol.h:1614
std::vector< Range > fromRanges
The range at which the calls appear.
Definition: Protocol.h:1618
Represents programming constructs like functions or constructors in the context of call hierarchy.
Definition: Protocol.h:1572
std::string name
The name of this item.
Definition: Protocol.h:1574
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e....
Definition: Protocol.h:1595
std::vector< Range > fromRanges
The range at which this item is called.
Definition: Protocol.h:1637
CallHierarchyItem to
The item that is called.
Definition: Protocol.h:1633
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36