brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.5 KiB · 9859577 Raw
734 lines · cpp
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-exception6//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,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 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(withFile, N, "") { return arg.uri.file() == N; }49MATCHER_P(withSelectionRange, R, "") { return arg.selectionRange == R; }50 51template <class ItemMatcher>52::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher M) {53  return Field(&CallHierarchyIncomingCall::from, M);54}55template <class ItemMatcher>56::testing::Matcher<CallHierarchyOutgoingCall> to(ItemMatcher M) {57  return Field(&CallHierarchyOutgoingCall::to, M);58}59template <class... RangeMatchers>60::testing::Matcher<CallHierarchyIncomingCall> iFromRanges(RangeMatchers... M) {61  return Field(&CallHierarchyIncomingCall::fromRanges,62               UnorderedElementsAre(M...));63}64template <class... RangeMatchers>65::testing::Matcher<CallHierarchyOutgoingCall> oFromRanges(RangeMatchers... M) {66  return Field(&CallHierarchyOutgoingCall::fromRanges,67               UnorderedElementsAre(M...));68}69 70TEST(CallHierarchy, IncomingOneFileCpp) {71  Annotations Source(R"cpp(72    void call^ee(int);73    void caller1() {74      $Callee[[callee]](42);75    }76    void caller2() {77      $Caller1A[[caller1]]();78      $Caller1B[[caller1]]();79    }80    void caller3() {81      $Caller1C[[caller1]]();82      $Caller2[[caller2]]();83    }84  )cpp");85  TestTU TU = TestTU::withCode(Source.code());86  auto AST = TU.build();87  auto Index = TU.index();88 89  std::vector<CallHierarchyItem> Items =90      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));91  ASSERT_THAT(Items, ElementsAre(withName("callee")));92  auto IncomingLevel1 = incomingCalls(Items[0], Index.get());93  ASSERT_THAT(94      IncomingLevel1,95      ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))),96                        iFromRanges(Source.range("Callee")))));97  auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());98  ASSERT_THAT(99      IncomingLevel2,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")))));105 106  auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());107  ASSERT_THAT(108      IncomingLevel3,109      ElementsAre(AllOf(from(AllOf(withName("caller3"), withDetail("caller3"))),110                        iFromRanges(Source.range("Caller2")))));111 112  auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());113  EXPECT_THAT(IncomingLevel4, IsEmpty());114}115 116TEST(CallHierarchy, IncomingOneFileObjC) {117  Annotations Source(R"objc(118    @implementation MyClass {}119      +(void)call^ee {}120      +(void) caller1 {121        [MyClass $Callee[[callee]]];122      }123      +(void) caller2 {124        [MyClass $Caller1A[[caller1]]];125        [MyClass $Caller1B[[caller1]]];126      }127      +(void) caller3 {128        [MyClass $Caller1C[[caller1]]];129        [MyClass $Caller2[[caller2]]];130      }131    @end132  )objc");133  TestTU TU = TestTU::withCode(Source.code());134  TU.Filename = "TestTU.m";135  auto AST = TU.build();136  auto Index = TU.index();137  std::vector<CallHierarchyItem> Items =138      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));139  ASSERT_THAT(Items, ElementsAre(withName("callee")));140  auto IncomingLevel1 = incomingCalls(Items[0], Index.get());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")))));154 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")))));160 161  auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());162  EXPECT_THAT(IncomingLevel4, IsEmpty());163}164 165TEST(CallHierarchy, IncomingIncludeOverrides) {166  Annotations Source(R"cpp(167    void call^ee() {}168    struct Interface {169      virtual void Func() = 0;170    };171    struct Implementation : public Interface {172      void Func() override {173          $Callee[[callee]]();174      }175    };176    void Test(Interface& cls){177      cls.$FuncCall[[Func]]();178    }179  )cpp");180  TestTU TU = TestTU::withCode(Source.code());181  auto AST = TU.build();182  auto Index = TU.index();183 184  std::vector<CallHierarchyItem> Items =185      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));186  ASSERT_THAT(Items, ElementsAre(withName("callee")));187  auto IncomingLevel1 = incomingCalls(Items[0], Index.get());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());193  ASSERT_THAT(194      IncomingLevel2,195      ElementsAre(AllOf(from(AllOf(withName("Test"), withDetail("Test"))),196                        iFromRanges(Source.range("FuncCall")))));197 198  auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());199  EXPECT_THAT(IncomingLevel3, IsEmpty());200}201 202TEST(CallHierarchy, MainFileOnlyRef) {203  // In addition to testing that we store refs to main-file only symbols,204  // this tests that anonymous namespaces do not interfere with the205  // symbol re-identification process in callHierarchyItemToSymbo().206  Annotations Source(R"cpp(207    void call^ee(int);208    namespace {209      void caller1() {210        $Callee[[callee]](42);211      }212    }213    void caller2() {214      $Caller1[[caller1]]();215    }216  )cpp");217  TestTU TU = TestTU::withCode(Source.code());218  auto AST = TU.build();219  auto Index = TU.index();220 221  std::vector<CallHierarchyItem> Items =222      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));223  ASSERT_THAT(Items, ElementsAre(withName("callee")));224  auto IncomingLevel1 = incomingCalls(Items[0], Index.get());225  ASSERT_THAT(226      IncomingLevel1,227      ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))),228                        iFromRanges(Source.range("Callee")))));229 230  auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());231  EXPECT_THAT(232      IncomingLevel2,233      ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("caller2"))),234                        iFromRanges(Source.range("Caller1")))));235}236 237TEST(CallHierarchy, IncomingQualified) {238  Annotations Source(R"cpp(239    namespace ns {240    struct Waldo {241      void find();242    };243    void Waldo::find() {}244    void caller1(Waldo &W) {245      W.$Caller1[[f^ind]]();246    }247    void caller2(Waldo &W) {248      W.$Caller2[[find]]();249    }250    }251  )cpp");252  TestTU TU = TestTU::withCode(Source.code());253  auto AST = TU.build();254  auto Index = TU.index();255 256  std::vector<CallHierarchyItem> Items =257      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));258  ASSERT_THAT(Items, ElementsAre(withName("Waldo::find")));259  auto Incoming = incomingCalls(Items[0], Index.get());260  EXPECT_THAT(261      Incoming,262      ElementsAre(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")))));267}268 269TEST(CallHierarchy, OutgoingOneFile) {270  // Test outgoing call on the main file, with namespaces and methods271  Annotations Source(R"cpp(272    void callee(int);273    namespace ns {274      struct Foo {275        void caller1();276      };277      void Foo::caller1() {278        $Callee[[callee]](42);279      }280    }281    namespace {282      void caller2(ns::Foo& F) {283        F.$Caller1A[[caller1]]();284        F.$Caller1B[[caller1]]();285      }286    }287    void call^er3(ns::Foo& F) {288      F.$Caller1C[[caller1]]();289      $Caller2[[caller2]](F);290    }291  )cpp");292  TestTU TU = TestTU::withCode(Source.code());293  auto AST = TU.build();294  auto Index = TU.index();295 296  std::vector<CallHierarchyItem> Items =297      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));298  ASSERT_THAT(Items, ElementsAre(withName("caller3")));299  auto OugoingLevel1 = outgoingCalls(Items[0], Index.get());300  ASSERT_THAT(301      OugoingLevel1,302      ElementsAre(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")))));307 308  auto OutgoingLevel2 = outgoingCalls(OugoingLevel1[1].to, Index.get());309  ASSERT_THAT(310      OutgoingLevel2,311      ElementsAre(AllOf(312          to(AllOf(withName("caller1"), withDetail("ns::Foo::caller1"))),313          oFromRanges(Source.range("Caller1A"), Source.range("Caller1B")))));314 315  auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get());316  ASSERT_THAT(317      OutgoingLevel3,318      ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))),319                        oFromRanges(Source.range("Callee")))));320 321  auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get());322  EXPECT_THAT(OutgoingLevel4, IsEmpty());323}324 325TEST(CallHierarchy, MultiFileCpp) {326  // The test uses a .hh suffix for header files to get clang327  // to parse them in C++ mode. .h files are parsed in C mode328  // by default, which causes problems because e.g. symbol329  // USRs are different in C mode (do not include function signatures).330 331  Annotations CalleeH(R"cpp(332    void calle^e(int);333  )cpp");334  Annotations CalleeC(R"cpp(335    #include "callee.hh"336    void calle^e(int) {}337  )cpp");338  Annotations Caller1H(R"cpp(339    namespace nsa {340      void caller1();341    }342  )cpp");343  Annotations Caller1C(R"cpp(344    #include "callee.hh"345    #include "caller1.hh"346    namespace nsa {347      void caller1() {348        [[calle^e]](42);349      }350    }351  )cpp");352  Annotations Caller2H(R"cpp(353    namespace nsb {354      void caller2();355    }356  )cpp");357  Annotations Caller2C(R"cpp(358    #include "caller1.hh"359    #include "caller2.hh"360    namespace nsb {361      void caller2() {362        nsa::$A[[caller1]]();363        nsa::$B[[caller1]]();364      }365    }366  )cpp");367  Annotations Caller3H(R"cpp(368    namespace nsa {369      void call^er3();370    }371  )cpp");372  Annotations Caller3C(R"cpp(373    #include "caller1.hh"374    #include "caller2.hh"375    namespace nsa {376      void call^er3() {377        $Caller1[[caller1]]();378        nsb::$Caller2[[caller2]]();379      }380    }381  )cpp");382 383  TestWorkspace Workspace;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());392 393  auto Index = Workspace.index();394 395  auto CheckIncomingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {396    std::vector<CallHierarchyItem> Items =397        prepareCallHierarchy(AST, Pos, TUPath);398    ASSERT_THAT(Items, ElementsAre(withName("callee")));399    auto IncomingLevel1 = incomingCalls(Items[0], Index.get());400    ASSERT_THAT(IncomingLevel1,401                ElementsAre(AllOf(from(AllOf(withName("caller1"),402                                             withDetail("nsa::caller1"))),403                                  iFromRanges(Caller1C.range()))));404 405    auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());406    ASSERT_THAT(407        IncomingLevel2,408        ElementsAre(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")))));413 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")))));419 420    auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());421    EXPECT_THAT(IncomingLevel4, IsEmpty());422  };423 424  auto CheckOutgoingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath,425                                bool IsDeclaration) {426    std::vector<CallHierarchyItem> Items =427        prepareCallHierarchy(AST, Pos, TUPath);428    ASSERT_THAT(429        Items,430        ElementsAre(AllOf(431            withName("caller3"),432            withFile(testPath(IsDeclaration ? "caller3.hh" : "caller3.cc")))));433    auto OutgoingLevel1 = outgoingCalls(Items[0], Index.get());434    ASSERT_THAT(435        OutgoingLevel1,436        // fromRanges are interpreted in the context of Items[0]'s file.437        // If that's the header, we can't get ranges from the implementation438        // file!439        ElementsAre(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")))));446 447    auto OutgoingLevel2 = outgoingCalls(OutgoingLevel1[1].to, Index.get());448    ASSERT_THAT(OutgoingLevel2,449                ElementsAre(AllOf(450                    to(AllOf(withName("caller1"), withDetail("nsa::caller1"))),451                    oFromRanges(Caller2C.range("A"), Caller2C.range("B")))));452 453    auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get());454    ASSERT_THAT(455        OutgoingLevel3,456        ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))),457                          oFromRanges(Caller1C.range()))));458 459    auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get());460    EXPECT_THAT(OutgoingLevel4, IsEmpty());461  };462 463  // Check that invoking from a call site works.464  auto AST = Workspace.openFile("caller1.cc");465  ASSERT_TRUE(bool(AST));466  CheckIncomingCalls(*AST, Caller1C.point(), testPath("caller1.cc"));467 468  // Check that invoking from the declaration site works.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);475 476  // Check that invoking from the definition site works.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);483}484 485TEST(CallHierarchy, IncomingMultiFileObjC) {486  // The test uses a .mi suffix for header files to get clang487  // to parse them in ObjC mode. .h files are parsed in C mode488  // by default, which causes problems because e.g. symbol489  // USRs are different in C mode (do not include function signatures).490 491  Annotations CalleeH(R"objc(492    @interface CalleeClass493      +(void)call^ee;494    @end495  )objc");496  Annotations CalleeC(R"objc(497    #import "callee.mi"498    @implementation CalleeClass {}499      +(void)call^ee {}500    @end501  )objc");502  Annotations Caller1H(R"objc(503    @interface Caller1Class504      +(void)caller1;505    @end506  )objc");507  Annotations Caller1C(R"objc(508    #import "callee.mi"509    #import "caller1.mi"510    @implementation Caller1Class {}511      +(void)caller1 {512        [CalleeClass [[calle^e]]];513      }514    @end515  )objc");516  Annotations Caller2H(R"objc(517    @interface Caller2Class518      +(void)caller2;519    @end520  )objc");521  Annotations Caller2C(R"objc(522    #import "caller1.mi"523    #import "caller2.mi"524    @implementation Caller2Class {}525      +(void)caller2 {526        [Caller1Class $A[[caller1]]];527        [Caller1Class $B[[caller1]]];528      }529    @end530  )objc");531  Annotations Caller3C(R"objc(532    #import "caller1.mi"533    #import "caller2.mi"534    @implementation Caller3Class {}535      +(void)caller3 {536        [Caller1Class $Caller1[[caller1]]];537        [Caller2Class $Caller2[[caller2]]];538      }539    @end540  )objc");541 542  TestWorkspace Workspace;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();551 552  auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {553    std::vector<CallHierarchyItem> Items =554        prepareCallHierarchy(AST, Pos, TUPath);555    ASSERT_THAT(Items, ElementsAre(withName("callee")));556    auto IncomingLevel1 = incomingCalls(Items[0], Index.get());557    ASSERT_THAT(IncomingLevel1,558                ElementsAre(AllOf(from(withName("caller1")),559                                  iFromRanges(Caller1C.range()))));560 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")))));568 569    auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());570    ASSERT_THAT(IncomingLevel3,571                ElementsAre(AllOf(from(withName("caller3")),572                                  iFromRanges(Caller3C.range("Caller2")))));573 574    auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());575    EXPECT_THAT(IncomingLevel4, IsEmpty());576  };577 578  // Check that invoking from a call site works.579  auto AST = Workspace.openFile("caller1.m");580  ASSERT_TRUE(bool(AST));581  CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1.m"));582 583  // Check that invoking from the declaration site works.584  AST = Workspace.openFile("callee.mi");585  ASSERT_TRUE(bool(AST));586  CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee.mi"));587 588  // Check that invoking from the definition site works.589  AST = Workspace.openFile("callee.m");590  ASSERT_TRUE(bool(AST));591  CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.m"));592}593 594TEST(CallHierarchy, CallInLocalVarDecl) {595  // Tests that local variable declarations are not treated as callers596  // (they're not indexed, so they can't be represented as call hierarchy597  // items); instead, the caller should be the containing function.598  // However, namespace-scope variable declarations should be treated as599  // callers because those are indexed and there is no enclosing entity600  // that would be a useful caller.601  Annotations Source(R"cpp(602    int call^ee();603    void caller1() {604      $call1[[callee]]();605    }606    void caller2() {607      int localVar = $call2[[callee]]();608    }609    int caller3 = $call3[[callee]]();610  )cpp");611  TestTU TU = TestTU::withCode(Source.code());612  auto AST = TU.build();613  auto Index = TU.index();614 615  std::vector<CallHierarchyItem> Items =616      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));617  ASSERT_THAT(Items, ElementsAre(withName("callee")));618 619  auto Incoming = incomingCalls(Items[0], Index.get());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")))));626}627 628TEST(CallHierarchy, HierarchyOnField) {629  // Tests that the call hierarchy works on fields.630  Annotations Source(R"cpp(631    struct Vars {632      int v^ar1 = 1;633    };634    void caller() {635      Vars values;636      values.$Callee[[var1]];637    }638  )cpp");639  TestTU TU = TestTU::withCode(Source.code());640  auto AST = TU.build();641  auto Index = TU.index();642 643  std::vector<CallHierarchyItem> Items =644      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));645  ASSERT_THAT(Items, ElementsAre(withName("var1")));646  auto IncomingLevel1 = incomingCalls(Items[0], Index.get());647  ASSERT_THAT(IncomingLevel1,648              ElementsAre(AllOf(from(withName("caller")),649                                iFromRanges(Source.range("Callee")))));650}651 652TEST(CallHierarchy, HierarchyOnVar) {653  // Tests that the call hierarchy works on non-local variables.654  Annotations Source(R"cpp(655    int v^ar = 1;656    void caller() {657      $Callee[[var]];658    }659  )cpp");660  TestTU TU = TestTU::withCode(Source.code());661  auto AST = TU.build();662  auto Index = TU.index();663 664  std::vector<CallHierarchyItem> Items =665      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));666  ASSERT_THAT(Items, ElementsAre(withName("var")));667  auto IncomingLevel1 = incomingCalls(Items[0], Index.get());668  ASSERT_THAT(IncomingLevel1,669              ElementsAre(AllOf(from(withName("caller")),670                                iFromRanges(Source.range("Callee")))));671}672 673TEST(CallHierarchy, HierarchyOnEnumConstant) {674  // Tests that the call hierarchy works on enum constants.675  Annotations Source(R"cpp(676    enum class Coin { heads$Heads^ , tai$Tails^ls };677    void caller() {678      Coin::$CallerH[[heads]];679      Coin::$CallerT[[tails]];680    }681  )cpp");682  TestTU TU = TestTU::withCode(Source.code());683  auto AST = TU.build();684  auto Index = TU.index();685 686  std::vector<CallHierarchyItem> Items =687      prepareCallHierarchy(AST, Source.point("Heads"), testPath(TU.Filename));688  ASSERT_THAT(Items, ElementsAre(withName("heads")));689  auto IncomingLevel1 = incomingCalls(Items[0], Index.get());690  ASSERT_THAT(IncomingLevel1,691              ElementsAre(AllOf(from(withName("caller")),692                                iFromRanges(Source.range("CallerH")))));693  Items =694      prepareCallHierarchy(AST, Source.point("Tails"), testPath(TU.Filename));695  ASSERT_THAT(Items, ElementsAre(withName("tails")));696  IncomingLevel1 = incomingCalls(Items[0], Index.get());697  ASSERT_THAT(IncomingLevel1,698              ElementsAre(AllOf(from(withName("caller")),699                                iFromRanges(Source.range("CallerT")))));700}701 702TEST(CallHierarchy, CallInDifferentFileThanCaller) {703  Annotations Header(R"cpp(704    #define WALDO void caller() {705  )cpp");706  Annotations Source(R"cpp(707    void call^ee();708    WALDO709      callee();710    }711  )cpp");712  auto TU = TestTU::withCode(Source.code());713  TU.HeaderCode = Header.code();714  auto AST = TU.build();715  auto Index = TU.index();716 717  std::vector<CallHierarchyItem> Items =718      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));719  ASSERT_THAT(Items, ElementsAre(withName("callee")));720 721  auto Incoming = incomingCalls(Items[0], Index.get());722 723  // The only call site is in the source file, which is a different file from724  // the declaration of the function containing the call, which is in the725  // header. The protocol does not allow us to represent such calls, so we drop726  // them. (The call hierarchy item itself is kept.)727  EXPECT_THAT(Incoming,728              ElementsAre(AllOf(from(withName("caller")), iFromRanges())));729}730 731} // namespace732} // namespace clangd733} // namespace clang734