nridge updated this revision to Diff 557829.
nridge added a comment.
Implement the "simple lookup optimization"
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D93829/new/
https://reviews.llvm.org/D93829
Files:
clang-tools-extra/clangd/ClangdLSPServer.cpp
clang-tools-extra/clangd/ClangdLSPServer.h
clang-tools-extra/clangd/ClangdServer.cpp
clang-tools-extra/clangd/ClangdServer.h
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/XRefs.h
clang-tools-extra/clangd/index/Index.cpp
clang-tools-extra/clangd/index/Index.h
clang-tools-extra/clangd/index/MemIndex.cpp
clang-tools-extra/clangd/index/MemIndex.h
clang-tools-extra/clangd/index/Merge.cpp
clang-tools-extra/clangd/index/Merge.h
clang-tools-extra/clangd/index/ProjectAware.cpp
clang-tools-extra/clangd/index/Ref.h
clang-tools-extra/clangd/index/SymbolCollector.cpp
clang-tools-extra/clangd/index/SymbolCollector.h
clang-tools-extra/clangd/index/dex/Dex.cpp
clang-tools-extra/clangd/index/dex/Dex.h
clang-tools-extra/clangd/test/type-hierarchy-ext.test
clang-tools-extra/clangd/test/type-hierarchy.test
clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang-tools-extra/clangd/unittests/RenameTests.cpp
Index: clang-tools-extra/clangd/unittests/RenameTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -1417,6 +1417,12 @@
return true; // has more references
}
+ bool refersTo(const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback)
+ const override {
+ return false;
+ }
+
bool fuzzyFind(
const FuzzyFindRequest &Req,
llvm::function_ref<void(const Symbol &)> Callback) const override {
@@ -1468,6 +1474,12 @@
return false;
}
+ bool refersTo(const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback)
+ const override {
+ return false;
+ }
+
bool fuzzyFind(const FuzzyFindRequest &,
llvm::function_ref<void(const Symbol &)>) const override {
return false;
Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1644,6 +1644,12 @@
return false;
}
+ bool
+ refersTo(const RefsRequest &,
+ llvm::function_ref<void(const RefersToResult &)>) const override {
+ return false;
+ }
+
void relations(const RelationsRequest &,
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override {}
Index: clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
+++ clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
@@ -44,17 +44,27 @@
// Helpers for matching call hierarchy data structures.
MATCHER_P(withName, N, "") { return arg.name == N; }
+MATCHER_P(withDetail, N, "") { return arg.detail == 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 ItemMatcher>
+::testing::Matcher<CallHierarchyOutgoingCall> to(ItemMatcher M) {
+ return Field(&CallHierarchyOutgoingCall::to, M);
+}
template <class... RangeMatchers>
-::testing::Matcher<CallHierarchyIncomingCall> fromRanges(RangeMatchers... M) {
+::testing::Matcher<CallHierarchyIncomingCall> iFromRanges(RangeMatchers... M) {
return Field(&CallHierarchyIncomingCall::fromRanges,
UnorderedElementsAre(M...));
}
+template <class... RangeMatchers>
+::testing::Matcher<CallHierarchyOutgoingCall> oFromRanges(RangeMatchers... M) {
+ return Field(&CallHierarchyOutgoingCall::fromRanges,
+ UnorderedElementsAre(M...));
+}
TEST(CallHierarchy, IncomingOneFileCpp) {
Annotations Source(R"cpp(
@@ -79,21 +89,24 @@
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
ASSERT_THAT(Items, ElementsAre(withName("callee")));
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
- ASSERT_THAT(IncomingLevel1,
- ElementsAre(AllOf(from(withName("caller1")),
- fromRanges(Source.range("Callee")))));
+ ASSERT_THAT(
+ IncomingLevel1,
+ ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))),
+ iFromRanges(Source.range("Callee")))));
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
- ASSERT_THAT(IncomingLevel2,
- ElementsAre(AllOf(from(withName("caller2")),
- fromRanges(Source.range("Caller1A"),
- Source.range("Caller1B"))),
- AllOf(from(withName("caller3")),
- fromRanges(Source.range("Caller1C")))));
+ ASSERT_THAT(
+ IncomingLevel2,
+ ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("caller2"))),
+ iFromRanges(Source.range("Caller1A"),
+ Source.range("Caller1B"))),
+ AllOf(from(AllOf(withName("caller3"), withDetail("caller3"))),
+ iFromRanges(Source.range("Caller1C")))));
auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
- ASSERT_THAT(IncomingLevel3,
- ElementsAre(AllOf(from(withName("caller3")),
- fromRanges(Source.range("Caller2")))));
+ ASSERT_THAT(
+ IncomingLevel3,
+ ElementsAre(AllOf(from(AllOf(withName("caller3"), withDetail("caller3"))),
+ iFromRanges(Source.range("Caller2")))));
auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
EXPECT_THAT(IncomingLevel4, IsEmpty());
@@ -125,20 +138,24 @@
ASSERT_THAT(Items, ElementsAre(withName("callee")));
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
ASSERT_THAT(IncomingLevel1,
- ElementsAre(AllOf(from(withName("caller1")),
- fromRanges(Source.range("Callee")))));
+ ElementsAre(AllOf(from(AllOf(withName("caller1"),
+ withDetail("MyClass::caller1"))),
+ iFromRanges(Source.range("Callee")))));
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
ASSERT_THAT(IncomingLevel2,
- ElementsAre(AllOf(from(withName("caller2")),
- fromRanges(Source.range("Caller1A"),
- Source.range("Caller1B"))),
- AllOf(from(withName("caller3")),
- fromRanges(Source.range("Caller1C")))));
+ ElementsAre(AllOf(from(AllOf(withName("caller2"),
+ withDetail("MyClass::caller2"))),
+ iFromRanges(Source.range("Caller1A"),
+ Source.range("Caller1B"))),
+ AllOf(from(AllOf(withName("caller3"),
+ withDetail("MyClass::caller3"))),
+ iFromRanges(Source.range("Caller1C")))));
auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
ASSERT_THAT(IncomingLevel3,
- ElementsAre(AllOf(from(withName("caller3")),
- fromRanges(Source.range("Caller2")))));
+ ElementsAre(AllOf(from(AllOf(withName("caller3"),
+ withDetail("MyClass::caller3"))),
+ iFromRanges(Source.range("Caller2")))));
auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
EXPECT_THAT(IncomingLevel4, IsEmpty());
@@ -167,14 +184,16 @@
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
ASSERT_THAT(Items, ElementsAre(withName("callee")));
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
- ASSERT_THAT(IncomingLevel1,
- ElementsAre(AllOf(from(withName("caller1")),
- fromRanges(Source.range("Callee")))));
+ ASSERT_THAT(
+ IncomingLevel1,
+ ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))),
+ iFromRanges(Source.range("Callee")))));
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
- EXPECT_THAT(IncomingLevel2,
- ElementsAre(AllOf(from(withName("caller2")),
- fromRanges(Source.range("Caller1")))));
+ EXPECT_THAT(
+ IncomingLevel2,
+ ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("caller2"))),
+ iFromRanges(Source.range("Caller1")))));
}
TEST(CallHierarchy, IncomingQualified) {
@@ -200,14 +219,72 @@
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
ASSERT_THAT(Items, ElementsAre(withName("Waldo::find")));
auto Incoming = incomingCalls(Items[0], Index.get());
- EXPECT_THAT(Incoming,
- ElementsAre(AllOf(from(withName("caller1")),
- fromRanges(Source.range("Caller1"))),
- AllOf(from(withName("caller2")),
- fromRanges(Source.range("Caller2")))));
+ EXPECT_THAT(
+ Incoming,
+ ElementsAre(
+ AllOf(from(AllOf(withName("caller1"), withDetail("ns::caller1"))),
+ iFromRanges(Source.range("Caller1"))),
+ AllOf(from(AllOf(withName("caller2"), withDetail("ns::caller2"))),
+ iFromRanges(Source.range("Caller2")))));
+}
+
+TEST(CallHierarchy, OutgoingOneFile) {
+ // Test outgoing call on the main file, with namespaces and methods
+ Annotations Source(R"cpp(
+ void callee(int);
+ namespace ns {
+ struct Foo {
+ void caller1();
+ };
+ void Foo::caller1() {
+ $Callee[[callee]](42);
+ }
+ }
+ namespace {
+ void caller2(ns::Foo& F) {
+ F.$Caller1A[[caller1]]();
+ F.$Caller1B[[caller1]]();
+ }
+ }
+ void call^er3(ns::Foo& F) {
+ F.$Caller1C[[caller1]]();
+ $Caller2[[caller2]](F);
+ }
+ )cpp");
+ TestTU TU = TestTU::withCode(Source.code());
+ auto AST = TU.build();
+ auto Index = TU.index();
+
+ std::vector<CallHierarchyItem> Items =
+ prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
+ ASSERT_THAT(Items, ElementsAre(withName("caller3")));
+ auto OugoingLevel1 = outgoingCalls(Items[0], Index.get());
+ ASSERT_THAT(
+ OugoingLevel1,
+ ElementsAre(
+ AllOf(to(AllOf(withName("caller1"), withDetail("ns::Foo::caller1"))),
+ oFromRanges(Source.range("Caller1C"))),
+ AllOf(to(AllOf(withName("caller2"), withDetail("caller2"))),
+ oFromRanges(Source.range("Caller2")))));
+
+ auto OutgoingLevel2 = outgoingCalls(OugoingLevel1[1].to, Index.get());
+ ASSERT_THAT(
+ OutgoingLevel2,
+ ElementsAre(AllOf(
+ to(AllOf(withName("caller1"), withDetail("ns::Foo::caller1"))),
+ oFromRanges(Source.range("Caller1A"), Source.range("Caller1B")))));
+
+ auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get());
+ ASSERT_THAT(
+ OutgoingLevel3,
+ ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))),
+ oFromRanges(Source.range("Callee")))));
+
+ auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get());
+ EXPECT_THAT(OutgoingLevel4, IsEmpty());
}
-TEST(CallHierarchy, IncomingMultiFileCpp) {
+TEST(CallHierarchy, MultiFileCpp) {
// 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
@@ -221,32 +298,47 @@
void calle^e(int) {}
)cpp");
Annotations Caller1H(R"cpp(
- void caller1();
+ namespace nsa {
+ void caller1();
+ }
)cpp");
Annotations Caller1C(R"cpp(
#include "callee.hh"
#include "caller1.hh"
- void caller1() {
- [[calle^e]](42);
+ namespace nsa {
+ void caller1() {
+ [[calle^e]](42);
+ }
}
)cpp");
Annotations Caller2H(R"cpp(
- void caller2();
+ namespace nsb {
+ void caller2();
+ }
)cpp");
Annotations Caller2C(R"cpp(
#include "caller1.hh"
#include "caller2.hh"
- void caller2() {
- $A[[caller1]]();
- $B[[caller1]]();
+ namespace nsb {
+ void caller2() {
+ nsa::$A[[caller1]]();
+ nsa::$B[[caller1]]();
+ }
+ }
+ )cpp");
+ Annotations Caller3H(R"cpp(
+ namespace nsa {
+ void call^er3();
}
)cpp");
Annotations Caller3C(R"cpp(
#include "caller1.hh"
#include "caller2.hh"
- void caller3() {
- $Caller1[[caller1]]();
- $Caller2[[caller2]]();
+ namespace nsa {
+ void call^er3() {
+ $Caller1[[caller1]]();
+ nsb::$Caller2[[caller2]]();
+ }
}
)cpp");
@@ -254,6 +346,7 @@
Workspace.addSource("callee.hh", CalleeH.code());
Workspace.addSource("caller1.hh", Caller1H.code());
Workspace.addSource("caller2.hh", Caller2H.code());
+ Workspace.addSource("caller3.hh", Caller3H.code());
Workspace.addMainFile("callee.cc", CalleeC.code());
Workspace.addMainFile("caller1.cc", Caller1C.code());
Workspace.addMainFile("caller2.cc", Caller2C.code());
@@ -261,46 +354,84 @@
auto Index = Workspace.index();
- auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
+ auto CheckIncomingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
std::vector<CallHierarchyItem> Items =
prepareCallHierarchy(AST, Pos, TUPath);
ASSERT_THAT(Items, ElementsAre(withName("callee")));
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
ASSERT_THAT(IncomingLevel1,
- ElementsAre(AllOf(from(withName("caller1")),
- fromRanges(Caller1C.range()))));
+ ElementsAre(AllOf(from(AllOf(withName("caller1"),
+ withDetail("nsa::caller1"))),
+ iFromRanges(Caller1C.range()))));
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
ASSERT_THAT(
IncomingLevel2,
- ElementsAre(AllOf(from(withName("caller2")),
- fromRanges(Caller2C.range("A"), Caller2C.range("B"))),
- AllOf(from(withName("caller3")),
- fromRanges(Caller3C.range("Caller1")))));
+ ElementsAre(
+ AllOf(from(AllOf(withName("caller2"), withDetail("nsb::caller2"))),
+ iFromRanges(Caller2C.range("A"), Caller2C.range("B"))),
+ AllOf(from(AllOf(withName("caller3"), withDetail("nsa::caller3"))),
+ iFromRanges(Caller3C.range("Caller1")))));
auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
ASSERT_THAT(IncomingLevel3,
- ElementsAre(AllOf(from(withName("caller3")),
- fromRanges(Caller3C.range("Caller2")))));
+ ElementsAre(AllOf(from(AllOf(withName("caller3"),
+ withDetail("nsa::caller3"))),
+ iFromRanges(Caller3C.range("Caller2")))));
auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
EXPECT_THAT(IncomingLevel4, IsEmpty());
};
+ auto CheckOutgoingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
+ std::vector<CallHierarchyItem> Items =
+ prepareCallHierarchy(AST, Pos, TUPath);
+ ASSERT_THAT(Items, ElementsAre(withName("caller3")));
+ auto OutgoingLevel1 = outgoingCalls(Items[0], Index.get());
+ ASSERT_THAT(
+ OutgoingLevel1,
+ ElementsAre(
+ AllOf(to(AllOf(withName("caller1"), withDetail("nsa::caller1"))),
+ oFromRanges(Caller3C.range("Caller1"))),
+ AllOf(to(AllOf(withName("caller2"), withDetail("nsb::caller2"))),
+ oFromRanges(Caller3C.range("Caller2")))));
+
+ auto OutgoingLevel2 = outgoingCalls(OutgoingLevel1[1].to, Index.get());
+ ASSERT_THAT(OutgoingLevel2,
+ ElementsAre(AllOf(
+ to(AllOf(withName("caller1"), withDetail("nsa::caller1"))),
+ oFromRanges(Caller2C.range("A"), Caller2C.range("B")))));
+
+ auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get());
+ ASSERT_THAT(
+ OutgoingLevel3,
+ ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))),
+ oFromRanges(Caller1C.range()))));
+
+ auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get());
+ EXPECT_THAT(OutgoingLevel4, IsEmpty());
+ };
+
// 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"));
+ CheckIncomingCalls(*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"));
+ CheckIncomingCalls(*AST, CalleeH.point(), testPath("callee.hh"));
+ AST = Workspace.openFile("caller3.hh");
+ ASSERT_TRUE(bool(AST));
+ CheckOutgoingCalls(*AST, Caller3H.point(), testPath("caller3.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"));
+ CheckIncomingCalls(*AST, CalleeC.point(), testPath("callee.cc"));
+ AST = Workspace.openFile("caller3.cc");
+ ASSERT_TRUE(bool(AST));
+ CheckOutgoingCalls(*AST, Caller3C.point(), testPath("caller3.cc"));
}
TEST(CallHierarchy, IncomingMultiFileObjC) {
@@ -377,20 +508,20 @@
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
ASSERT_THAT(IncomingLevel1,
ElementsAre(AllOf(from(withName("caller1")),
- fromRanges(Caller1C.range()))));
+ iFromRanges(Caller1C.range()))));
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
- ASSERT_THAT(
- IncomingLevel2,
- ElementsAre(AllOf(from(withName("caller2")),
- fromRanges(Caller2C.range("A"), Caller2C.range("B"))),
- AllOf(from(withName("caller3")),
- fromRanges(Caller3C.range("Caller1")))));
+ ASSERT_THAT(IncomingLevel2,
+ ElementsAre(AllOf(from(withName("caller2")),
+ iFromRanges(Caller2C.range("A"),
+ Caller2C.range("B"))),
+ AllOf(from(withName("caller3")),
+ iFromRanges(Caller3C.range("Caller1")))));
auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
ASSERT_THAT(IncomingLevel3,
ElementsAre(AllOf(from(withName("caller3")),
- fromRanges(Caller3C.range("Caller2")))));
+ iFromRanges(Caller3C.range("Caller2")))));
auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
EXPECT_THAT(IncomingLevel4, IsEmpty());
@@ -438,12 +569,12 @@
ASSERT_THAT(Items, ElementsAre(withName("callee")));
auto Incoming = incomingCalls(Items[0], Index.get());
- ASSERT_THAT(
- Incoming,
- ElementsAre(
- AllOf(from(withName("caller1")), fromRanges(Source.range("call1"))),
- AllOf(from(withName("caller2")), fromRanges(Source.range("call2"))),
- AllOf(from(withName("caller3")), fromRanges(Source.range("call3")))));
+ ASSERT_THAT(Incoming, ElementsAre(AllOf(from(withName("caller1")),
+ iFromRanges(Source.range("call1"))),
+ AllOf(from(withName("caller2")),
+ iFromRanges(Source.range("call2"))),
+ AllOf(from(withName("caller3")),
+ iFromRanges(Source.range("call3")))));
}
} // namespace
Index: clang-tools-extra/clangd/test/type-hierarchy.test
===================================================================
--- clang-tools-extra/clangd/test/type-hierarchy.test
+++ clang-tools-extra/clangd/test/type-hierarchy.test
@@ -62,6 +62,7 @@
# CHECK-NEXT: ],
# CHECK-NEXT: "symbolID": "ECDC0C46D75120F4"
# CHECK-NEXT: },
+# CHECK-NEXT: "detail": "Child1",
# CHECK-NEXT: "kind": 23,
# CHECK-NEXT: "name": "Child1",
# CHECK-NEXT: "range": {
@@ -88,56 +89,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: ]
---
-{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/subtypes","params":{"item":{"uri":"test:///main.cpp","data":{"parents":[{"parents":[{"parents":[],"symbolID":"FE546E7B648D69A7"}],"symbolID":"ECDC0C46D75120F4"}],"symbolID":"8A991335E4E67D08"},"name":"Child2","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}}}}
-# CHECK: "id": 2
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "data": {
-# CHECK-NEXT: "parents": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "parents": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "parents": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "parents": [],
-# CHECK-NEXT: "symbolID": "FE546E7B648D69A7"
-# CHECK-NEXT: }
-# CHECK-NEXT: ],
-# CHECK-NEXT: "symbolID": "ECDC0C46D75120F4"
-# CHECK-NEXT: }
-# CHECK-NEXT: ],
-# CHECK-NEXT: "symbolID": "8A991335E4E67D08"
-# CHECK-NEXT: }
-# CHECK-NEXT: ],
-# CHECK-NEXT: "symbolID": "A6576FE083F2949A"
-# CHECK-NEXT: },
-# CHECK-NEXT: "kind": 23,
-# CHECK-NEXT: "name": "Child3",
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 13,
-# CHECK-NEXT: "line": 3
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 7,
-# CHECK-NEXT: "line": 3
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "selectionRange": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 13,
-# CHECK-NEXT: "line": 3
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 7,
-# CHECK-NEXT: "line": 3
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
----
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
---
{"jsonrpc":"2.0","method":"exit"}
Index: clang-tools-extra/clangd/test/type-hierarchy-ext.test
===================================================================
--- clang-tools-extra/clangd/test/type-hierarchy-ext.test
+++ clang-tools-extra/clangd/test/type-hierarchy-ext.test
@@ -12,6 +12,7 @@
# CHECK-NEXT: "data": {
# CHECK-NEXT: "symbolID": "A6576FE083F2949A"
# CHECK-NEXT: },
+# CHECK-NEXT: "detail": "Child3",
# CHECK-NEXT: "kind": 23,
# CHECK-NEXT: "name": "Child3",
# CHECK-NEXT: "range": {
@@ -153,6 +154,7 @@
# CHECK-NEXT: "data": {
# CHECK-NEXT: "symbolID": "5705B382DFC77CBC"
# CHECK-NEXT: },
+# CHECK-NEXT: "detail": "Child4",
# CHECK-NEXT: "kind": 23,
# CHECK-NEXT: "name": "Child4",
# CHECK-NEXT: "range": {
Index: clang-tools-extra/clangd/index/dex/Dex.h
===================================================================
--- clang-tools-extra/clangd/index/dex/Dex.h
+++ clang-tools-extra/clangd/index/dex/Dex.h
@@ -85,6 +85,10 @@
bool refs(const RefsRequest &Req,
llvm::function_ref<void(const Ref &)> Callback) const override;
+ bool refersTo(
+ const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback) const override;
+
void relations(const RelationsRequest &Req,
llvm::function_ref<void(const SymbolID &, const Symbol &)>
Callback) const override;
@@ -95,7 +99,22 @@
size_t estimateMemoryUsage() const override;
private:
+ class RevRef {
+ const Ref *Reference;
+ SymbolID Target;
+
+ public:
+ RevRef(const Ref &Reference, SymbolID Target)
+ : Reference(&Reference), Target(Target) {}
+ const Ref &ref() const { return *Reference; }
+ RefersToResult refersToResult() const {
+ return {ref().Location, ref().Kind, Target};
+ }
+ };
+
void buildIndex();
+ llvm::iterator_range<std::vector<RevRef>::const_iterator>
+ lookupRevRefs(const SymbolID &Container) const;
std::unique_ptr<Iterator> iterator(const Token &Tok) const;
std::unique_ptr<Iterator>
createFileProximityIterator(llvm::ArrayRef<std::string> ProximityPaths) const;
@@ -116,6 +135,7 @@
llvm::DenseMap<Token, PostingList> InvertedIndex;
dex::Corpus Corpus;
llvm::DenseMap<SymbolID, llvm::ArrayRef<Ref>> Refs;
+ std::vector<RevRef> RevRefs; // sorted by container ID
static_assert(sizeof(RelationKind) == sizeof(uint8_t),
"RelationKind should be of same size as a uint8_t");
llvm::DenseMap<std::pair<SymbolID, uint8_t>, std::vector<SymbolID>> Relations;
Index: clang-tools-extra/clangd/index/dex/Dex.cpp
===================================================================
--- clang-tools-extra/clangd/index/dex/Dex.cpp
+++ clang-tools-extra/clangd/index/dex/Dex.cpp
@@ -147,6 +147,16 @@
for (DocID SymbolRank = 0; SymbolRank < Symbols.size(); ++SymbolRank)
Builder.add(*Symbols[SymbolRank], SymbolRank);
InvertedIndex = std::move(Builder).build();
+
+ // Build RevRefs
+ for (const auto &[ID, RefList] : Refs)
+ for (const auto &R : RefList)
+ if ((R.Kind & RefKind::Call) != RefKind::Unknown)
+ RevRefs.emplace_back(R, ID);
+ // Sort by container ID so we can use binary search for lookup.
+ llvm::sort(RevRefs, [](const RevRef &A, const RevRef &B) {
+ return A.ref().Container < B.ref().Container;
+ });
}
std::unique_ptr<Iterator> Dex::iterator(const Token &Tok) const {
@@ -314,6 +324,38 @@
return false; // We reported all refs.
}
+llvm::iterator_range<std::vector<Dex::RevRef>::const_iterator>
+Dex::lookupRevRefs(const SymbolID &Container) const {
+ // equal_range() requires an element of the same type as the elements of the
+ // range, so construct a dummy RevRef with the container of interest.
+ Ref QueryRef;
+ QueryRef.Container = Container;
+ RevRef Query(QueryRef, SymbolID{});
+
+ auto ItPair = std::equal_range(RevRefs.cbegin(), RevRefs.cend(), Query,
+ [](const RevRef &A, const RevRef &B) {
+ return A.ref().Container < B.ref().Container;
+ });
+ return {ItPair.first, ItPair.second};
+}
+
+bool Dex::refersTo(
+ const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback) const {
+ trace::Span Tracer("Dex reversed refs");
+ uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
+ for (const auto &ID : Req.IDs)
+ for (const auto &Rev : lookupRevRefs(ID)) {
+ if (!static_cast<int>(Req.Filter & Rev.ref().Kind))
+ continue;
+ if (Remaining == 0)
+ return true; // More refs were available.
+ --Remaining;
+ Callback(Rev.refersToResult());
+ }
+ return false; // We reported all refs.
+}
+
void Dex::relations(
const RelationsRequest &Req,
llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
@@ -350,6 +392,7 @@
for (const auto &TokenToPostingList : InvertedIndex)
Bytes += TokenToPostingList.second.bytes();
Bytes += Refs.getMemorySize();
+ Bytes += RevRefs.size() * sizeof(RevRef);
Bytes += Relations.getMemorySize();
return Bytes + BackingDataSize;
}
Index: clang-tools-extra/clangd/index/SymbolCollector.h
===================================================================
--- clang-tools-extra/clangd/index/SymbolCollector.h
+++ clang-tools-extra/clangd/index/SymbolCollector.h
@@ -201,6 +201,7 @@
SourceLocation Loc;
FileID FID;
index::SymbolRoleSet Roles;
+ index::SymbolKind Kind;
const Decl *Container;
bool Spelled;
};
Index: clang-tools-extra/clangd/index/SymbolCollector.cpp
===================================================================
--- clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -18,6 +18,7 @@
#include "clang-include-cleaner/Record.h"
#include "clang-include-cleaner/Types.h"
#include "index/CanonicalIncludes.h"
+#include "index/Ref.h"
#include "index/Relation.h"
#include "index/Symbol.h"
#include "index/SymbolID.h"
@@ -612,7 +613,7 @@
auto FileLoc = SM.getFileLoc(Loc);
auto FID = SM.getFileID(FileLoc);
if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
- addRef(ID, SymbolRef{FileLoc, FID, Roles,
+ addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
getRefContainer(ASTNode.Parent, Opts),
isSpelled(FileLoc, *ND)});
}
@@ -722,8 +723,10 @@
// FIXME: Populate container information for macro references.
// FIXME: All MacroRefs are marked as Spelled now, but this should be
// checked.
- addRef(ID, SymbolRef{Loc, SM.getFileID(Loc), Roles, /*Container=*/nullptr,
- /*Spelled=*/true});
+ addRef(ID,
+ SymbolRef{Loc, SM.getFileID(Loc), Roles, index::SymbolKind::Macro,
+ /*Container=*/nullptr,
+ /*Spelled=*/true});
}
// Collect symbols.
@@ -1077,6 +1080,14 @@
return I.first->second;
}
+static bool refIsCall(index::SymbolKind Kind) {
+ using SK = index::SymbolKind;
+ return Kind == SK::Function || Kind == SK::InstanceMethod ||
+ Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
+ Kind == SK::Constructor || Kind == SK::Destructor ||
+ Kind == SK::ConversionFunction;
+}
+
void SymbolCollector::addRef(SymbolID ID, const SymbolRef &SR) {
const auto &SM = ASTCtx->getSourceManager();
// FIXME: use the result to filter out references.
@@ -1088,6 +1099,9 @@
R.Location.End = Range.second;
R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
R.Kind = toRefKind(SR.Roles, SR.Spelled);
+ if (refIsCall(SR.Kind)) {
+ R.Kind |= RefKind::Call;
+ }
R.Container = getSymbolIDCached(SR.Container);
Refs.insert(ID, R);
}
Index: clang-tools-extra/clangd/index/Ref.h
===================================================================
--- clang-tools-extra/clangd/index/Ref.h
+++ clang-tools-extra/clangd/index/Ref.h
@@ -63,6 +63,9 @@
// ^ this references Foo, but does not explicitly spell out its name
// };
Spelled = 1 << 3,
+ // A reference which is a call. Used as a filter for which references
+ // to store in data structures used for computing outgoing calls.
+ Call = 1 << 4,
All = Declaration | Definition | Reference | Spelled,
};
Index: clang-tools-extra/clangd/index/ProjectAware.cpp
===================================================================
--- clang-tools-extra/clangd/index/ProjectAware.cpp
+++ clang-tools-extra/clangd/index/ProjectAware.cpp
@@ -35,6 +35,10 @@
/// Query all indexes while prioritizing the associated one (if any).
bool refs(const RefsRequest &Req,
llvm::function_ref<void(const Ref &)> Callback) const override;
+ /// Query all indexes while prioritizing the associated one (if any).
+ bool refersTo(
+ const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback) const override;
/// Queries only the associates index when Req.RestrictForCodeCompletion is
/// set, otherwise queries all.
@@ -94,6 +98,15 @@
return false;
}
+bool ProjectAwareIndex::refersTo(
+ const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback) const {
+ trace::Span Tracer("ProjectAwareIndex::refersTo");
+ if (auto *Idx = getIndex())
+ return Idx->refersTo(Req, Callback);
+ return false;
+}
+
bool ProjectAwareIndex::fuzzyFind(
const FuzzyFindRequest &Req,
llvm::function_ref<void(const Symbol &)> Callback) const {
Index: clang-tools-extra/clangd/index/Merge.h
===================================================================
--- clang-tools-extra/clangd/index/Merge.h
+++ clang-tools-extra/clangd/index/Merge.h
@@ -38,6 +38,9 @@
llvm::function_ref<void(const Symbol &)>) const override;
bool refs(const RefsRequest &,
llvm::function_ref<void(const Ref &)>) const override;
+ bool
+ refersTo(const RefsRequest &,
+ llvm::function_ref<void(const RefersToResult &)>) const override;
void relations(const RelationsRequest &,
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override;
Index: clang-tools-extra/clangd/index/Merge.cpp
===================================================================
--- clang-tools-extra/clangd/index/Merge.cpp
+++ clang-tools-extra/clangd/index/Merge.cpp
@@ -155,6 +155,40 @@
return More || StaticHadMore;
}
+bool MergedIndex::refersTo(
+ const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback) const {
+ trace::Span Tracer("MergedIndex refersTo");
+ bool More = false;
+ uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
+ // We don't want duplicated refs from the static/dynamic indexes,
+ // and we can't reliably deduplicate them because offsets may differ slightly.
+ // We consider the dynamic index authoritative and report all its refs,
+ // and only report static index refs from other files.
+ More |= Dynamic->refersTo(Req, [&](const auto &O) {
+ Callback(O);
+ assert(Remaining != 0);
+ --Remaining;
+ });
+ if (Remaining == 0 && More)
+ return More;
+ auto DynamicContainsFile = Dynamic->indexedFiles();
+ // We return less than Req.Limit if static index returns more refs for dirty
+ // files.
+ bool StaticHadMore = Static->refersTo(Req, [&](const auto &O) {
+ if ((DynamicContainsFile(O.Location.FileURI) & IndexContents::References) !=
+ IndexContents::None)
+ return; // ignore refs that have been seen from dynamic index.
+ if (Remaining == 0) {
+ More = true;
+ return;
+ }
+ --Remaining;
+ Callback(O);
+ });
+ return More || StaticHadMore;
+}
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
MergedIndex::indexedFiles() const {
return [DynamicContainsFile{Dynamic->indexedFiles()},
Index: clang-tools-extra/clangd/index/MemIndex.h
===================================================================
--- clang-tools-extra/clangd/index/MemIndex.h
+++ clang-tools-extra/clangd/index/MemIndex.h
@@ -72,6 +72,10 @@
bool refs(const RefsRequest &Req,
llvm::function_ref<void(const Ref &)> Callback) const override;
+ bool refersTo(
+ const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback) const override;
+
void relations(const RelationsRequest &Req,
llvm::function_ref<void(const SymbolID &, const Symbol &)>
Callback) const override;
Index: clang-tools-extra/clangd/index/MemIndex.cpp
===================================================================
--- clang-tools-extra/clangd/index/MemIndex.cpp
+++ clang-tools-extra/clangd/index/MemIndex.cpp
@@ -85,6 +85,25 @@
return false; // We reported all refs.
}
+bool MemIndex::refersTo(
+ const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback) const {
+ trace::Span Tracer("MemIndex refersTo");
+ uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
+ for (const auto &Pair : Refs) {
+ for (const auto &R : Pair.second) {
+ if (!static_cast<int>(Req.Filter & R.Kind) ||
+ !Req.IDs.contains(R.Container))
+ continue;
+ if (Remaining == 0)
+ return true; // More refs were available.
+ --Remaining;
+ Callback({R.Location, R.Kind, Pair.first});
+ }
+ }
+ return false; // We reported all refs.
+}
+
void MemIndex::relations(
const RelationsRequest &Req,
llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
Index: clang-tools-extra/clangd/index/Index.h
===================================================================
--- clang-tools-extra/clangd/index/Index.h
+++ clang-tools-extra/clangd/index/Index.h
@@ -84,6 +84,14 @@
std::optional<uint32_t> Limit;
};
+struct RefersToResult {
+ /// The source location where the symbol is named.
+ SymbolLocation Location;
+ RefKind Kind = RefKind::Unknown;
+ /// The ID of the symbol which is referred to
+ SymbolID Symbol;
+};
+
/// Describes what data is covered by an index.
///
/// Indexes may contain symbols but not references from a file, etc.
@@ -141,6 +149,17 @@
virtual bool refs(const RefsRequest &Req,
llvm::function_ref<void(const Ref &)> Callback) const = 0;
+ /// Find all symbols that are referenced by a symbol and apply
+ /// \p Callback on each result.
+ ///
+ /// Results should be returned in arbitrary order.
+ /// The returned result must be deep-copied if it's used outside Callback.
+ ///
+ /// Returns true if there will be more results (limited by Req.Limit);
+ virtual bool
+ refersTo(const RefsRequest &Req,
+ llvm::function_ref<void(const RefersToResult &)> Callback) const = 0;
+
/// Finds all relations (S, P, O) stored in the index such that S is among
/// Req.Subjects and P is Req.Predicate, and invokes \p Callback for (S, O) in
/// each.
@@ -175,6 +194,9 @@
llvm::function_ref<void(const Symbol &)>) const override;
bool refs(const RefsRequest &,
llvm::function_ref<void(const Ref &)>) const override;
+ bool
+ refersTo(const RefsRequest &,
+ llvm::function_ref<void(const RefersToResult &)>) const override;
void relations(const RelationsRequest &,
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override;
Index: clang-tools-extra/clangd/index/Index.cpp
===================================================================
--- clang-tools-extra/clangd/index/Index.cpp
+++ clang-tools-extra/clangd/index/Index.cpp
@@ -66,6 +66,11 @@
llvm::function_ref<void(const Ref &)> CB) const {
return snapshot()->refs(R, CB);
}
+bool SwapIndex::refersTo(
+ const RefsRequest &R,
+ llvm::function_ref<void(const RefersToResult &)> CB) const {
+ return snapshot()->refersTo(R, CB);
+}
void SwapIndex::relations(
const RelationsRequest &R,
llvm::function_ref<void(const SymbolID &, const Symbol &)> CB) const {
Index: clang-tools-extra/clangd/XRefs.h
===================================================================
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -150,6 +150,9 @@
std::vector<CallHierarchyIncomingCall>
incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index);
+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
@@ -1700,6 +1700,7 @@
HierarchyItem HI;
HI.name = printName(Ctx, ND);
+ // FIXME: Populate HI.detail the way we do in symbolToHierarchyItem?
HI.kind = SK;
HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()),
sourceLocToPosition(SM, DeclRange->getEnd())};
@@ -1751,6 +1752,7 @@
}
HierarchyItem HI;
HI.name = std::string(S.Name);
+ HI.detail = (S.Scope + S.Name).str();
HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind);
HI.selectionRange = Loc->range;
// FIXME: Populate 'range' correctly
@@ -2303,6 +2305,67 @@
return Results;
}
+std::vector<CallHierarchyOutgoingCall>
+outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
+ std::vector<CallHierarchyOutgoingCall> Results;
+ if (!Index || Item.data.empty())
+ return Results;
+ auto ID = SymbolID::fromStr(Item.data);
+ if (!ID) {
+ elog("outgoingCalls failed to find symbol: {0}", ID.takeError());
+ return Results;
+ }
+ // In this function, we find outgoing calls based on the index only.
+ RefsRequest Request;
+ Request.IDs.insert(*ID);
+ // Note that RefKind::Call just restricts the matched SymbolKind to
+ // functions, not the form of the reference (e.g. address-of-function,
+ // which can indicate an indirect call, should still be caught).
+ Request.Filter = RefKind::Call;
+ // Initially store the ranges in a map keyed by SymbolID of the callee.
+ // This allows us to group different calls to the same function
+ // into the same CallHierarchyOutgoingCall.
+ llvm::DenseMap<SymbolID, std::vector<Range>> CallsOut;
+ // We can populate the ranges based on a refs request only. As we do so, we
+ // also accumulate the callee IDs into a lookup request.
+ LookupRequest CallsOutLookup;
+ Index->refersTo(Request, [&](const auto &R) {
+ auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
+ if (!Loc) {
+ elog("outgoingCalls failed to convert location: {0}", Loc.takeError());
+ return;
+ }
+ auto It = CallsOut.try_emplace(R.Symbol, std::vector<Range>{}).first;
+ It->second.push_back(Loc->range);
+
+ CallsOutLookup.IDs.insert(R.Symbol);
+ });
+ // Perform the lookup request and combine its results with CallsOut to
+ // get complete CallHierarchyOutgoingCall objects.
+ Index->lookup(CallsOutLookup, [&](const Symbol &Callee) {
+ // Filter references to only keep function calls
+ using SK = index::SymbolKind;
+ auto Kind = Callee.SymInfo.Kind;
+ bool NotCall = (Kind != SK::Function && Kind != SK::InstanceMethod &&
+ Kind != SK::ClassMethod && Kind != SK::StaticMethod &&
+ Kind != SK::Constructor && Kind != SK::Destructor &&
+ Kind != SK::ConversionFunction);
+ assert(!NotCall);
+
+ auto It = CallsOut.find(Callee.ID);
+ assert(It != CallsOut.end());
+ if (auto CHI = symbolToCallHierarchyItem(Callee, Item.uri.file()))
+ Results.push_back(
+ CallHierarchyOutgoingCall{std::move(*CHI), std::move(It->second)});
+ });
+ // Sort results by name of the callee.
+ llvm::sort(Results, [](const CallHierarchyOutgoingCall &A,
+ const CallHierarchyOutgoingCall &B) {
+ return A.to.name < B.to.name;
+ });
+ return Results;
+}
+
llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
const FunctionDecl *FD) {
if (!FD->hasBody())
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -288,6 +288,10 @@
void incomingCalls(const CallHierarchyItem &Item,
Callback<std::vector<CallHierarchyIncomingCall>>);
+ /// Resolve outgoing calls for a given call hierarchy item.
+ void outgoingCalls(const CallHierarchyItem &Item,
+ Callback<std::vector<CallHierarchyOutgoingCall>>);
+
/// Resolve inlay hints for a given document.
void inlayHints(PathRef File, std::optional<Range> RestrictRange,
Callback<std::vector<InlayHint>>);
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -880,6 +880,15 @@
WorkScheduler->runWithAST("InlayHints", File, std::move(Action), Transient);
}
+void ClangdServer::outgoingCalls(
+ const CallHierarchyItem &Item,
+ Callback<std::vector<CallHierarchyOutgoingCall>> CB) {
+ WorkScheduler->run("Outgoing Calls", "",
+ [CB = std::move(CB), Item, this]() mutable {
+ CB(clangd::outgoingCalls(Item, Index));
+ });
+}
+
void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
// FIXME: Do nothing for now. This will be used for indexing and potentially
// invalidating other caches.
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -153,6 +153,9 @@
void onCallHierarchyIncomingCalls(
const CallHierarchyIncomingCallsParams &,
Callback<std::vector<CallHierarchyIncomingCall>>);
+ void onCallHierarchyOutgoingCalls(
+ const CallHierarchyOutgoingCallsParams &,
+ Callback<std::vector<CallHierarchyOutgoingCall>>);
void onClangdInlayHints(const InlayHintsParams &,
Callback<llvm::json::Value>);
void onInlayHint(const InlayHintsParams &, Callback<std::vector<InlayHint>>);
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -1373,6 +1373,12 @@
std::move(Reply));
}
+void ClangdLSPServer::onCallHierarchyOutgoingCalls(
+ const CallHierarchyOutgoingCallsParams &Params,
+ Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
+ Server->outgoingCalls(Params.item, std::move(Reply));
+}
+
void ClangdLSPServer::applyConfiguration(
const ConfigurationSettings &Settings) {
// Per-file update to the compilation database.
@@ -1654,6 +1660,7 @@
Bind.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes);
Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy);
Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
+ Bind.method("callHierarchy/outgoingCalls", this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange);
Bind.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink);
Bind.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens);
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits