nridge updated this revision to Diff 305400.
nridge marked 12 inline comments as done.
nridge added a comment.

Address review comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D91122/new/

https://reviews.llvm.org/D91122

Files:
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
  clang-tools-extra/clangd/unittests/TestTU.cpp

Index: clang-tools-extra/clangd/unittests/TestTU.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TestTU.cpp
+++ clang-tools-extra/clangd/unittests/TestTU.cpp
@@ -156,7 +156,8 @@
 
 std::unique_ptr<SymbolIndex> TestTU::index() const {
   auto AST = build();
-  auto Idx = std::make_unique<FileIndex>(/*UseDex=*/true);
+  auto Idx = std::make_unique<FileIndex>(/*UseDex=*/true,
+                                         /*CollectMainFileRefs=*/true);
   Idx->updatePreamble(testPath(Filename), /*Version=*/"null",
                       AST.getASTContext(), AST.getPreprocessorPtr(),
                       AST.getCanonicalIncludes());
Index: clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
@@ -0,0 +1,272 @@
+//===-- CallHierarchyTests.cpp  ---------------------------*- C++ -*-------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "Annotations.h"
+#include "Compiler.h"
+#include "Matchers.h"
+#include "ParsedAST.h"
+#include "SyncAPI.h"
+#include "TestFS.h"
+#include "TestTU.h"
+#include "TestWorkspace.h"
+#include "XRefs.h"
+#include "index/FileIndex.h"
+#include "index/SymbolCollector.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/Index/IndexingAction.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using ::testing::AllOf;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Matcher;
+using ::testing::UnorderedElementsAre;
+
+// Helpers for matching call hierarchy data structures.
+MATCHER_P(WithName, N, "") { return arg.name == N; }
+MATCHER_P(WithSelectionRange, R, "") { return arg.selectionRange == R; }
+
+template <class ItemMatcher>
+::testing::Matcher<CallHierarchyIncomingCall> From(ItemMatcher M) {
+  return Field(&CallHierarchyIncomingCall::From, M);
+}
+template <class... RangeMatchers>
+::testing::Matcher<CallHierarchyIncomingCall> FromRanges(RangeMatchers... M) {
+  return Field(&CallHierarchyIncomingCall::FromRanges,
+               UnorderedElementsAre(M...));
+}
+
+TEST(CallHierarchy, IncomingOneFile) {
+  Annotations Source(R"cpp(
+    void call^ee(int);
+    void caller1() {
+      $Callee[[callee]](42);
+    }
+    void caller2() {
+      $Caller1A[[caller1]]();
+      $Caller1B[[caller1]]();
+    }
+    void caller3() {
+      $Caller1C[[caller1]]();
+      $Caller2[[caller2]]();
+    }
+  )cpp");
+  TestTU TU = TestTU::withCode(Source.code());
+  auto AST = TU.build();
+  auto Index = TU.index();
+
+  llvm::Optional<std::vector<CallHierarchyItem>> Items =
+      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
+  ASSERT_TRUE(bool(Items));
+  EXPECT_THAT(*Items, ElementsAre(WithName("callee")));
+  auto IncomingLevel1 = incomingCalls((*Items)[0], Index.get());
+  ASSERT_TRUE(bool(IncomingLevel1));
+  EXPECT_THAT(*IncomingLevel1,
+              ElementsAre(AllOf(From(WithName("caller1")),
+                                FromRanges(Source.range("Callee")))));
+
+  auto IncomingLevel2 = incomingCalls((*IncomingLevel1)[0].From, Index.get());
+  ASSERT_TRUE(bool(IncomingLevel2));
+  EXPECT_THAT(
+      *IncomingLevel2,
+      UnorderedElementsAre(
+          AllOf(From(WithName("caller2")),
+                FromRanges(Source.range("Caller1A"), Source.range("Caller1B"))),
+          AllOf(From(WithName("caller3")),
+                FromRanges(Source.range("Caller1C")))));
+
+  auto IncomingLevel3 = incomingCalls((*IncomingLevel2)[0].From, Index.get());
+  ASSERT_TRUE(bool(IncomingLevel3));
+  EXPECT_THAT(*IncomingLevel3,
+              ElementsAre(AllOf(From(WithName("caller3")),
+                                FromRanges(Source.range("Caller2")))));
+
+  auto IncomingLevel4 = incomingCalls((*IncomingLevel3)[0].From, Index.get());
+  ASSERT_TRUE(bool(IncomingLevel4));
+  EXPECT_THAT(*IncomingLevel4, ElementsAre());
+}
+
+TEST(CallHierarchy, MainFileOnlyRef) {
+  // In addition to testing that we store refs to main-file only symbols,
+  // this tests that anonymous namespaces do not interfere with the
+  // symbol re-identification process in callHierarchyItemToSymbo().
+  Annotations Source(R"cpp(
+    void call^ee(int);
+    namespace {
+      void caller1() {
+        $Callee[[callee]](42);
+      }
+    }
+    void caller2() {
+      $Caller1[[caller1]]();
+    }
+  )cpp");
+  TestTU TU = TestTU::withCode(Source.code());
+  auto AST = TU.build();
+  auto Index = TU.index();
+
+  llvm::Optional<std::vector<CallHierarchyItem>> Items =
+      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
+  ASSERT_TRUE(bool(Items));
+  EXPECT_THAT(*Items, ElementsAre(WithName("callee")));
+  auto IncomingLevel1 = incomingCalls((*Items)[0], Index.get());
+  ASSERT_TRUE(bool(IncomingLevel1));
+  EXPECT_THAT(*IncomingLevel1,
+              ElementsAre(AllOf(From(WithName("caller1")),
+                                FromRanges(Source.range("Callee")))));
+
+  auto IncomingLevel2 = incomingCalls((*IncomingLevel1)[0].From, Index.get());
+  ASSERT_TRUE(bool(IncomingLevel2));
+  EXPECT_THAT(*IncomingLevel2,
+              UnorderedElementsAre(AllOf(From(WithName("caller2")),
+                                         FromRanges(Source.range("Caller1")))));
+}
+
+TEST(CallHierarchy, IncomingQualified) {
+  Annotations Source(R"cpp(
+    namespace ns {
+    struct Waldo {
+      void find();
+    };
+    void Waldo::find() {}
+    void caller1(Waldo &W) {
+      W.$Caller1[[f^ind]]();
+    }
+    void caller2(Waldo &W) {
+      W.$Caller2[[find]]();
+    }
+    }
+  )cpp");
+  TestTU TU = TestTU::withCode(Source.code());
+  auto AST = TU.build();
+  auto Index = TU.index();
+
+  llvm::Optional<std::vector<CallHierarchyItem>> Items =
+      prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
+  ASSERT_TRUE(bool(Items));
+  EXPECT_THAT(*Items, ElementsAre(WithName("Waldo::find")));
+  auto Incoming = incomingCalls((*Items)[0], Index.get());
+  ASSERT_TRUE(bool(Incoming));
+  EXPECT_THAT(*Incoming,
+              UnorderedElementsAre(AllOf(From(WithName("caller1")),
+                                         FromRanges(Source.range("Caller1"))),
+                                   AllOf(From(WithName("caller2")),
+                                         FromRanges(Source.range("Caller2")))));
+}
+
+TEST(CallHierarchy, IncomingMultiFile) {
+  // The test uses a .hh suffix for header files to get clang
+  // to parse them in C++ mode. .h files are parsed in C mode
+  // by default, which causes problems because e.g. symbol
+  // USRs are different in C mode (do not include function signatures).
+
+  Annotations CalleeH(R"cpp(
+    void calle^e(int);
+  )cpp");
+  Annotations CalleeC(R"cpp(
+    #include "callee.hh"
+    void calle^e(int) {}
+  )cpp");
+  Annotations Caller1H(R"cpp(
+    void caller1();
+  )cpp");
+  Annotations Caller1C(R"cpp(
+    #include "callee.hh"
+    #include "caller1.hh"
+    void caller1() {
+      [[calle^e]](42);
+    }
+  )cpp");
+  Annotations Caller2H(R"cpp(
+    void caller2();
+  )cpp");
+  Annotations Caller2C(R"cpp(
+    #include "caller1.hh"
+    #include "caller2.hh"
+    void caller2() {
+      $A[[caller1]]();
+      $B[[caller1]]();
+    }
+  )cpp");
+  Annotations Caller3C(R"cpp(
+    #include "caller1.hh"
+    #include "caller2.hh"
+    void caller3() {
+      $Caller1[[caller1]]();
+      $Caller2[[caller2]]();
+    }
+  )cpp");
+
+  TestWorkspace Workspace;
+  Workspace.addSource("callee.hh", CalleeH.code());
+  Workspace.addSource("caller1.hh", Caller1H.code());
+  Workspace.addSource("caller2.hh", Caller2H.code());
+  Workspace.addMainFile("callee.cc", CalleeC.code());
+  Workspace.addMainFile("caller1.cc", Caller1C.code());
+  Workspace.addMainFile("caller2.cc", Caller2C.code());
+  Workspace.addMainFile("caller3.cc", Caller3C.code());
+
+  auto Index = Workspace.index();
+
+  auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
+    llvm::Optional<std::vector<CallHierarchyItem>> Items =
+        prepareCallHierarchy(AST, Pos, TUPath);
+    ASSERT_TRUE(bool(Items));
+    EXPECT_THAT(*Items, ElementsAre(WithName("callee")));
+    auto IncomingLevel1 = incomingCalls((*Items)[0], Index.get());
+    ASSERT_TRUE(bool(IncomingLevel1));
+    EXPECT_THAT(*IncomingLevel1,
+                ElementsAre(AllOf(From(WithName("caller1")),
+                                  FromRanges(Caller1C.range()))));
+
+    auto IncomingLevel2 = incomingCalls((*IncomingLevel1)[0].From, Index.get());
+    ASSERT_TRUE(bool(IncomingLevel2));
+    EXPECT_THAT(*IncomingLevel2,
+                UnorderedElementsAre(
+                    AllOf(From(WithName("caller2")),
+                          FromRanges(Caller2C.range("A"), Caller2C.range("B"))),
+                    AllOf(From(WithName("caller3")),
+                          FromRanges(Caller3C.range("Caller1")))));
+
+    auto IncomingLevel3 = incomingCalls((*IncomingLevel2)[0].From, Index.get());
+    ASSERT_TRUE(bool(IncomingLevel3));
+    EXPECT_THAT(*IncomingLevel3,
+                ElementsAre(AllOf(From(WithName("caller3")),
+                                  FromRanges(Caller3C.range("Caller2")))));
+
+    auto IncomingLevel4 = incomingCalls((*IncomingLevel3)[0].From, Index.get());
+    ASSERT_TRUE(bool(IncomingLevel4));
+    EXPECT_THAT(*IncomingLevel4, ElementsAre());
+  };
+
+  // Check that invoking from a call site works.
+  auto AST = Workspace.openFile("caller1.cc");
+  ASSERT_TRUE(bool(AST));
+  CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1.cc"));
+
+  // Check that invoking from the declaration site works.
+  AST = Workspace.openFile("callee.hh");
+  ASSERT_TRUE(bool(AST));
+  CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee.hh"));
+
+  // Check that invoking from the definition site works.
+  AST = Workspace.openFile("callee.cc");
+  ASSERT_TRUE(bool(AST));
+  CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.cc"));
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/unittests/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/unittests/CMakeLists.txt
+++ clang-tools-extra/clangd/unittests/CMakeLists.txt
@@ -36,6 +36,7 @@
   Annotations.cpp
   ASTTests.cpp
   BackgroundIndexTests.cpp
+  CallHierarchyTests.cpp
   CanonicalIncludesTests.cpp
   ClangdTests.cpp
   ClangdLSPServerTests.cpp
Index: clang-tools-extra/clangd/XRefs.h
===================================================================
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -105,6 +105,16 @@
                           TypeHierarchyDirection Direction,
                           const SymbolIndex *Index);
 
+/// Get call hierarchy information at \p Pos.
+llvm::Optional<std::vector<CallHierarchyItem>>
+prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath);
+
+llvm::Optional<std::vector<CallHierarchyIncomingCall>>
+incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index);
+
+llvm::Optional<std::vector<CallHierarchyOutgoingCall>>
+outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index);
+
 /// Returns all decls that are referenced in the \p FD except local symbols.
 llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
                                                  const FunctionDecl *FD);
Index: clang-tools-extra/clangd/XRefs.cpp
===================================================================
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -47,6 +47,7 @@
 #include "clang/Index/USRGeneration.h"
 #include "clang/Tooling/Syntax/Tokens.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/None.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -1287,9 +1288,9 @@
   return OS;
 }
 
-// FIXME(nridge): Reduce duplication between this function and declToSym().
-static llvm::Optional<TypeHierarchyItem>
-declToTypeHierarchyItem(ASTContext &Ctx, const NamedDecl &ND) {
+template <typename HierarchyItem>
+static llvm::Optional<HierarchyItem> declToHierarchyItem(const NamedDecl &ND) {
+  ASTContext &Ctx = ND.getASTContext();
   auto &SM = Ctx.getSourceManager();
   SourceLocation NameLoc = nameLocation(ND, Ctx.getSourceManager());
   SourceLocation BeginLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getBeginLoc()));
@@ -1313,54 +1314,92 @@
   // correctly.
   SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
 
-  TypeHierarchyItem THI;
-  THI.name = printName(Ctx, ND);
-  THI.kind = SK;
-  THI.deprecated = ND.isDeprecated();
-  THI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()),
-                    sourceLocToPosition(SM, DeclRange->getEnd())};
-  THI.selectionRange = Range{NameBegin, NameEnd};
-  if (!THI.range.contains(THI.selectionRange)) {
+  HierarchyItem HI;
+  HI.name = printName(Ctx, ND);
+  HI.kind = SK;
+  // HI.deprecated = ND.isDeprecated();
+  HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()),
+                   sourceLocToPosition(SM, DeclRange->getEnd())};
+  HI.selectionRange = Range{NameBegin, NameEnd};
+  if (!HI.range.contains(HI.selectionRange)) {
     // 'selectionRange' must be contained in 'range', so in cases where clang
     // reports unrelated ranges we need to reconcile somehow.
-    THI.range = THI.selectionRange;
+    HI.range = HI.selectionRange;
   }
 
-  THI.uri = URIForFile::canonicalize(*FilePath, *TUPath);
+  HI.uri = URIForFile::canonicalize(*FilePath, *TUPath);
 
   // Compute the SymbolID and store it in the 'data' field.
   // This allows typeHierarchy/resolve to be used to
   // resolve children of items returned in a previous request
   // for parents.
   if (auto ID = getSymbolID(&ND))
-    THI.data = ID.str();
+    HI.data = ID.str();
+
+  return HI;
+}
 
-  return THI;
+static llvm::Optional<TypeHierarchyItem>
+declToTypeHierarchyItem(const NamedDecl &ND) {
+  auto Result = declToHierarchyItem<TypeHierarchyItem>(ND);
+  if (Result) {
+    Result->deprecated = ND.isDeprecated();
+  }
+  return Result;
 }
 
-static Optional<TypeHierarchyItem>
-symbolToTypeHierarchyItem(const Symbol &S, const SymbolIndex *Index,
-                          PathRef TUPath) {
+static llvm::Optional<CallHierarchyItem>
+declToCallHierarchyItem(const NamedDecl &ND) {
+  auto Result = declToHierarchyItem<CallHierarchyItem>(ND);
+  if (Result) {
+    if (ND.isDeprecated())
+      Result->tags.push_back(SymbolTag::Deprecated);
+  }
+  return Result;
+}
+
+template <typename HierarchyItem>
+static Optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
+                                                     PathRef TUPath) {
   auto Loc = symbolToLocation(S, TUPath);
   if (!Loc) {
-    log("Type hierarchy: {0}", Loc.takeError());
+    elog("Failed to convert symbol to hierarchy item: {0}", Loc.takeError());
     return llvm::None;
   }
-  TypeHierarchyItem THI;
-  THI.name = std::string(S.Name);
-  THI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind);
-  THI.deprecated = (S.Flags & Symbol::Deprecated);
-  THI.selectionRange = Loc->range;
+  HierarchyItem HI;
+  HI.name = std::string(S.Name);
+  HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind);
+  HI.selectionRange = Loc->range;
   // FIXME: Populate 'range' correctly
   // (https://github.com/clangd/clangd/issues/59).
-  THI.range = THI.selectionRange;
-  THI.uri = Loc->uri;
+  HI.range = HI.selectionRange;
+  HI.uri = Loc->uri;
   // Store the SymbolID in the 'data' field. The client will
-  // send this back in typeHierarchy/resolve, allowing us to
-  // continue resolving additional levels of the type hierarchy.
-  THI.data = S.ID.str();
+  // send this back in requests to resolve additional levels
+  // of the hierarchy.
+  HI.data = S.ID.str();
+
+  return std::move(HI);
+}
 
-  return std::move(THI);
+static Optional<TypeHierarchyItem> symbolToTypeHierarchyItem(const Symbol &S,
+                                                             PathRef TUPath) {
+  auto Result = symbolToHierarchyItem<TypeHierarchyItem>(S, TUPath);
+  if (Result) {
+    Result->deprecated = (S.Flags & Symbol::Deprecated);
+  }
+  return Result;
+}
+
+static Optional<CallHierarchyItem> symbolToCallHierarchyItem(const Symbol &S,
+                                                             PathRef TUPath) {
+  auto Result = symbolToHierarchyItem<CallHierarchyItem>(S, TUPath);
+  if (Result) {
+    if (S.Flags & Symbol::Deprecated) {
+      Result->tags.push_back(SymbolTag::Deprecated);
+    }
+  }
+  return Result;
 }
 
 static void fillSubTypes(const SymbolID &ID,
@@ -1371,7 +1410,7 @@
   Req.Predicate = RelationKind::BaseOf;
   Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
     if (Optional<TypeHierarchyItem> ChildSym =
-            symbolToTypeHierarchyItem(Object, Index, TUPath)) {
+            symbolToTypeHierarchyItem(Object, TUPath)) {
       if (Levels > 1) {
         ChildSym->children.emplace();
         fillSubTypes(Object.ID, *ChildSym->children, Index, Levels - 1, TUPath);
@@ -1400,7 +1439,7 @@
 
   for (const CXXRecordDecl *ParentDecl : typeParents(&CXXRD)) {
     if (Optional<TypeHierarchyItem> ParentSym =
-            declToTypeHierarchyItem(ASTCtx, *ParentDecl)) {
+            declToTypeHierarchyItem(*ParentDecl)) {
       ParentSym->parents.emplace();
       fillSuperTypes(*ParentDecl, ASTCtx, *ParentSym->parents, RPSet);
       SuperTypes.emplace_back(std::move(*ParentSym));
@@ -1522,8 +1561,7 @@
       CXXRD = CTSD->getTemplateInstantiationPattern();
   }
 
-  Optional<TypeHierarchyItem> Result =
-      declToTypeHierarchyItem(AST.getASTContext(), *CXXRD);
+  Optional<TypeHierarchyItem> Result = declToTypeHierarchyItem(*CXXRD);
   if (!Result)
     return Result;
 
@@ -1565,6 +1603,91 @@
   }
 }
 
+llvm::Optional<std::vector<CallHierarchyItem>>
+prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath) {
+  const auto &SM = AST.getSourceManager();
+  auto Loc = sourceLocationInMainFile(SM, Pos);
+  if (!Loc) {
+    elog("prepareCallHierarchy failed to convert position to source location: "
+         "{0}",
+         Loc.takeError());
+    return llvm::None;
+  }
+  std::vector<CallHierarchyItem> Result;
+  for (const NamedDecl *Decl : getDeclAtPosition(AST, *Loc, {})) {
+    if (!Decl->isFunctionOrFunctionTemplate())
+      continue;
+    if (auto CHI = declToCallHierarchyItem(*Decl))
+      Result.push_back(*std::move(CHI));
+  }
+  return Result;
+}
+
+llvm::Optional<std::vector<CallHierarchyIncomingCall>>
+incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
+  if (!Index || Item.data.empty())
+    return llvm::None;
+  Expected<SymbolID> ID = SymbolID::fromStr(Item.data);
+  if (!ID) {
+    elog("incomingCalls failed to find symbol: {0}", ID.takeError());
+    return llvm::None;
+  }
+  // In this function, we find incoming calls based on the index only.
+  // In principle, the AST could have more up-to-date information about
+  // occurrences within the current file. However, going from a SymbolID
+  // to an AST node isn't cheap, particularly when the declaration isn't
+  // in the main file.
+  // FIXME: Consider also using AST information when feasible.
+  RefsRequest Request;
+  Request.IDs.insert(*ID);
+  // We could restrict more specifically to calls by introducing a new RefKind,
+  // but non-call references (such as address-of-function) can still be
+  // interesting as they can indicate indirect calls.
+  Request.Filter = RefKind::Reference;
+  // Initially store the results in a map keyed by SymbolID.
+  // This allows us to group different calls with the same caller
+  // into the same CallHierarchyIncomingCall.
+  llvm::DenseMap<SymbolID, CallHierarchyIncomingCall> ResultMap;
+  // We can populate the keys of ResultMap, and the FromRanges fields of
+  // its values, based on a refs request only. As we do so, we also accumulate
+  // the container IDs into a lookup request.
+  LookupRequest ContainerLookup;
+  Index->refs(Request, [&](const Ref &R) {
+    auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
+    if (!Loc) {
+      elog("incomingCalls failed to convert location: {0}", Loc.takeError());
+      return;
+    }
+    auto It = ResultMap.find(R.Container);
+    if (It == ResultMap.end())
+      It = ResultMap.insert({R.Container, CallHierarchyIncomingCall{}}).first;
+    It->second.FromRanges.push_back(Loc->range);
+
+    ContainerLookup.IDs.insert(R.Container);
+  });
+  // Perform the lookup request and use its result to fill the From fields
+  // of the values in ResultMap.
+  Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
+    auto It = ResultMap.find(Caller.ID);
+    if (It != ResultMap.end()) {
+      if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file()))
+        It->second.From = std::move(*CHI);
+    }
+  });
+  // Finally, flatten the results into a vector.
+  std::vector<CallHierarchyIncomingCall> Results;
+  for (auto &&Entry : ResultMap) {
+    Results.push_back(std::move(Entry.second));
+  }
+  return Results;
+}
+
+llvm::Optional<std::vector<CallHierarchyOutgoingCall>>
+outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
+  // TODO: Implement.
+  return llvm::None;
+}
+
 llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
                                                  const FunctionDecl *FD) {
   if (!FD->hasBody())
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to