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 18 namespace clang { 19 namespace clangd { 20 21 llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream, 22 const CallHierarchyItem &Item) { 23 return Stream << Item.name << "@" << Item.selectionRange; 24 } 25 26 llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream, 27 const CallHierarchyIncomingCall &Call) { 28 Stream << "{ from: " << Call.from << ", ranges: ["; 29 for (const auto &R : Call.fromRanges) { 30 Stream << R; 31 Stream << ", "; 32 } 33 return Stream << "] }"; 34 } 35 36 namespace { 37 38 using ::testing::AllOf; 39 using ::testing::ElementsAre; 40 using ::testing::Field; 41 using ::testing::IsEmpty; 42 using ::testing::Matcher; 43 using ::testing::UnorderedElementsAre; 44 45 // Helpers for matching call hierarchy data structures. 46 MATCHER_P(withName, N, "") { return arg.name == N; } 47 MATCHER_P(withDetail, N, "") { return arg.detail == N; } 48 MATCHER_P(withSelectionRange, R, "") { return arg.selectionRange == R; } 49 50 template <class ItemMatcher> 51 ::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher M) { 52 return Field(&CallHierarchyIncomingCall::from, M); 53 } 54 template <class ItemMatcher> 55 ::testing::Matcher<CallHierarchyOutgoingCall> to(ItemMatcher M) { 56 return Field(&CallHierarchyOutgoingCall::to, M); 57 } 58 template <class... RangeMatchers> 59 ::testing::Matcher<CallHierarchyIncomingCall> iFromRanges(RangeMatchers... M) { 60 return Field(&CallHierarchyIncomingCall::fromRanges, 61 UnorderedElementsAre(M...)); 62 } 63 template <class... RangeMatchers> 64 ::testing::Matcher<CallHierarchyOutgoingCall> oFromRanges(RangeMatchers... M) { 65 return Field(&CallHierarchyOutgoingCall::fromRanges, 66 UnorderedElementsAre(M...)); 67 } 68 69 TEST(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 115 TEST(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 164 TEST(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 199 TEST(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 231 TEST(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 287 TEST(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 437 TEST(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 546 TEST(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 580 TEST(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 604 TEST(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 625 TEST(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 657