Skip to content

Commit 99b6254

Browse files
kadircettru
authored andcommitted
[clangd] Support for standard type hierarchy
This is mostly a mechanical change to adapt standard type hierarchy support proposed in LSP 3.17 on top of clangd's existing extension support. This does mainly two things: - Incorporate symbolids for all the parents inside resolution parameters, so that they can be retrieved from index later on. This is a new code path, as extension always resolved them eagerly. - Propogate parent information when resolving children, so that at least one branch of parents is always preserved. This is to address a shortcoming in the extension. This doesn't drop support for the extension, but it's deprecated from now on and will be deleted in upcoming releases. Currently we use the same struct internally but don't serialize extra fields. Fixes clangd/clangd#826. Differential Revision: https://reviews.llvm.org/D131385 (cherry picked from commit 83411bf)
1 parent 0777364 commit 99b6254

File tree

12 files changed

+785
-362
lines changed

12 files changed

+785
-362
lines changed

clang-tools-extra/clangd/ClangdLSPServer.cpp

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "support/Trace.h"
2727
#include "clang/Tooling/Core/Replacement.h"
2828
#include "llvm/ADT/ArrayRef.h"
29+
#include "llvm/ADT/FunctionExtras.h"
30+
#include "llvm/ADT/None.h"
2931
#include "llvm/ADT/Optional.h"
3032
#include "llvm/ADT/ScopeExit.h"
3133
#include "llvm/ADT/StringRef.h"
@@ -571,8 +573,12 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
571573
{"referencesProvider", true},
572574
{"astProvider", true}, // clangd extension
573575
{"typeHierarchyProvider", true},
574-
{"memoryUsageProvider", true}, // clangd extension
575-
{"compilationDatabase", // clangd extension
576+
// Unfortunately our extension made use of the same capability name as the
577+
// standard. Advertise this capability to tell clients that implement our
578+
// extension we really have support for the standardized one as well.
579+
{"standardTypeHierarchyProvider", true}, // clangd extension
580+
{"memoryUsageProvider", true}, // clangd extension
581+
{"compilationDatabase", // clangd extension
576582
llvm::json::Object{{"automaticReload", true}}},
577583
{"callHierarchyProvider", true},
578584
{"clangdInlayHintsProvider", true},
@@ -1183,18 +1189,94 @@ void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
11831189
});
11841190
}
11851191

1186-
void ClangdLSPServer::onTypeHierarchy(
1187-
const TypeHierarchyParams &Params,
1188-
Callback<Optional<TypeHierarchyItem>> Reply) {
1192+
// Our extension has a different representation on the wire than the standard.
1193+
// https://clangd.llvm.org/extensions#type-hierarchy
1194+
llvm::json::Value serializeTHIForExtension(TypeHierarchyItem THI) {
1195+
llvm::json::Object Result{{
1196+
{"name", std::move(THI.name)},
1197+
{"kind", static_cast<int>(THI.kind)},
1198+
{"uri", std::move(THI.uri)},
1199+
{"range", THI.range},
1200+
{"selectionRange", THI.selectionRange},
1201+
{"data", std::move(THI.data)},
1202+
}};
1203+
if (THI.deprecated)
1204+
Result["deprecated"] = THI.deprecated;
1205+
if (THI.detail)
1206+
Result["detail"] = std::move(*THI.detail);
1207+
1208+
if (THI.parents) {
1209+
llvm::json::Array Parents;
1210+
for (auto &Parent : *THI.parents)
1211+
Parents.emplace_back(serializeTHIForExtension(std::move(Parent)));
1212+
Result["parents"] = std::move(Parents);
1213+
}
1214+
1215+
if (THI.children) {
1216+
llvm::json::Array Children;
1217+
for (auto &child : *THI.children)
1218+
Children.emplace_back(serializeTHIForExtension(std::move(child)));
1219+
Result["children"] = std::move(Children);
1220+
}
1221+
return Result;
1222+
}
1223+
1224+
void ClangdLSPServer::onTypeHierarchy(const TypeHierarchyPrepareParams &Params,
1225+
Callback<llvm::json::Value> Reply) {
1226+
auto Serialize =
1227+
[Reply = std::move(Reply)](
1228+
llvm::Expected<std::vector<TypeHierarchyItem>> Resp) mutable {
1229+
if (!Resp) {
1230+
Reply(Resp.takeError());
1231+
return;
1232+
}
1233+
if (Resp->empty()) {
1234+
Reply(nullptr);
1235+
return;
1236+
}
1237+
Reply(serializeTHIForExtension(std::move(Resp->front())));
1238+
};
11891239
Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1190-
Params.resolve, Params.direction, std::move(Reply));
1240+
Params.resolve, Params.direction, std::move(Serialize));
11911241
}
11921242

11931243
void ClangdLSPServer::onResolveTypeHierarchy(
11941244
const ResolveTypeHierarchyItemParams &Params,
1195-
Callback<Optional<TypeHierarchyItem>> Reply) {
1245+
Callback<llvm::json::Value> Reply) {
1246+
auto Serialize =
1247+
[Reply = std::move(Reply)](
1248+
llvm::Expected<llvm::Optional<TypeHierarchyItem>> Resp) mutable {
1249+
if (!Resp) {
1250+
Reply(Resp.takeError());
1251+
return;
1252+
}
1253+
if (!*Resp) {
1254+
Reply(std::move(*Resp));
1255+
return;
1256+
}
1257+
Reply(serializeTHIForExtension(std::move(**Resp)));
1258+
};
11961259
Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1197-
std::move(Reply));
1260+
std::move(Serialize));
1261+
}
1262+
1263+
void ClangdLSPServer::onPrepareTypeHierarchy(
1264+
const TypeHierarchyPrepareParams &Params,
1265+
Callback<std::vector<TypeHierarchyItem>> Reply) {
1266+
Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1267+
Params.resolve, Params.direction, std::move(Reply));
1268+
}
1269+
1270+
void ClangdLSPServer::onSuperTypes(
1271+
const ResolveTypeHierarchyItemParams &Params,
1272+
Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> Reply) {
1273+
Server->superTypes(Params.item, std::move(Reply));
1274+
}
1275+
1276+
void ClangdLSPServer::onSubTypes(
1277+
const ResolveTypeHierarchyItemParams &Params,
1278+
Callback<std::vector<TypeHierarchyItem>> Reply) {
1279+
Server->subTypes(Params.item, std::move(Reply));
11981280
}
11991281

12001282
void ClangdLSPServer::onPrepareCallHierarchy(
@@ -1523,6 +1605,9 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind,
15231605
Bind.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo);
15241606
Bind.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy);
15251607
Bind.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy);
1608+
Bind.method("textDocument/prepareTypeHierarchy", this, &ClangdLSPServer::onPrepareTypeHierarchy);
1609+
Bind.method("typeHierarchy/supertypes", this, &ClangdLSPServer::onSuperTypes);
1610+
Bind.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes);
15261611
Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy);
15271612
Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
15281613
Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange);

clang-tools-extra/clangd/ClangdLSPServer.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <chrono>
2424
#include <cstddef>
2525
#include <memory>
26+
#include <vector>
2627

2728
namespace clang {
2829
namespace clangd {
@@ -132,10 +133,16 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
132133
void onRename(const RenameParams &, Callback<WorkspaceEdit>);
133134
void onHover(const TextDocumentPositionParams &,
134135
Callback<llvm::Optional<Hover>>);
135-
void onTypeHierarchy(const TypeHierarchyParams &,
136-
Callback<llvm::Optional<TypeHierarchyItem>>);
136+
void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &,
137+
Callback<std::vector<TypeHierarchyItem>>);
138+
void onSuperTypes(const ResolveTypeHierarchyItemParams &,
139+
Callback<llvm::Optional<std::vector<TypeHierarchyItem>>>);
140+
void onSubTypes(const ResolveTypeHierarchyItemParams &,
141+
Callback<std::vector<TypeHierarchyItem>>);
142+
void onTypeHierarchy(const TypeHierarchyPrepareParams &,
143+
Callback<llvm::json::Value>);
137144
void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &,
138-
Callback<llvm::Optional<TypeHierarchyItem>>);
145+
Callback<llvm::json::Value>);
139146
void onPrepareCallHierarchy(const CallHierarchyPrepareParams &,
140147
Callback<std::vector<CallHierarchyItem>>);
141148
void onCallHierarchyIncomingCalls(

clang-tools-extra/clangd/ClangdServer.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ void ClangdServer::findHover(PathRef File, Position Pos,
751751

752752
void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
753753
TypeHierarchyDirection Direction,
754-
Callback<Optional<TypeHierarchyItem>> CB) {
754+
Callback<std::vector<TypeHierarchyItem>> CB) {
755755
auto Action = [File = File.str(), Pos, Resolve, Direction, CB = std::move(CB),
756756
this](Expected<InputsAndAST> InpAST) mutable {
757757
if (!InpAST)
@@ -763,6 +763,22 @@ void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
763763
WorkScheduler->runWithAST("TypeHierarchy", File, std::move(Action));
764764
}
765765

766+
void ClangdServer::superTypes(
767+
const TypeHierarchyItem &Item,
768+
Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> CB) {
769+
WorkScheduler->run("typeHierarchy/superTypes", /*Path=*/"",
770+
[=, CB = std::move(CB)]() mutable {
771+
CB(clangd::superTypes(Item, Index));
772+
});
773+
}
774+
775+
void ClangdServer::subTypes(const TypeHierarchyItem &Item,
776+
Callback<std::vector<TypeHierarchyItem>> CB) {
777+
WorkScheduler->run(
778+
"typeHierarchy/subTypes", /*Path=*/"",
779+
[=, CB = std::move(CB)]() mutable { CB(clangd::subTypes(Item, Index)); });
780+
}
781+
766782
void ClangdServer::resolveTypeHierarchy(
767783
TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction,
768784
Callback<llvm::Optional<TypeHierarchyItem>> CB) {

clang-tools-extra/clangd/ClangdServer.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,13 @@ class ClangdServer {
253253
/// Get information about type hierarchy for a given position.
254254
void typeHierarchy(PathRef File, Position Pos, int Resolve,
255255
TypeHierarchyDirection Direction,
256-
Callback<llvm::Optional<TypeHierarchyItem>> CB);
256+
Callback<std::vector<TypeHierarchyItem>> CB);
257+
/// Get direct parents of a type hierarchy item.
258+
void superTypes(const TypeHierarchyItem &Item,
259+
Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> CB);
260+
/// Get direct children of a type hierarchy item.
261+
void subTypes(const TypeHierarchyItem &Item,
262+
Callback<std::vector<TypeHierarchyItem>> CB);
257263

258264
/// Resolve type hierarchy item in the given direction.
259265
void resolveTypeHierarchy(TypeHierarchyItem Item, int Resolve,

clang-tools-extra/clangd/Protocol.cpp

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,36 +1199,42 @@ bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out,
11991199
return true;
12001200
}
12011201

1202-
bool fromJSON(const llvm::json::Value &Params, TypeHierarchyParams &R,
1202+
bool fromJSON(const llvm::json::Value &Params, TypeHierarchyPrepareParams &R,
12031203
llvm::json::Path P) {
12041204
llvm::json::ObjectMapper O(Params, P);
12051205
return O && O.map("textDocument", R.textDocument) &&
1206-
O.map("position", R.position) && O.map("resolve", R.resolve) &&
1207-
O.map("direction", R.direction);
1206+
O.map("position", R.position) &&
1207+
mapOptOrNull(Params, "resolve", R.resolve, P) &&
1208+
mapOptOrNull(Params, "direction", R.direction, P);
12081209
}
12091210

12101211
llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
12111212
const TypeHierarchyItem &I) {
12121213
return O << I.name << " - " << toJSON(I);
12131214
}
12141215

1216+
llvm::json::Value toJSON(const TypeHierarchyItem::ResolveParams &RP) {
1217+
llvm::json::Object Result{{"symbolID", RP.symbolID}};
1218+
if (RP.parents)
1219+
Result["parents"] = RP.parents;
1220+
return std::move(Result);
1221+
}
1222+
bool fromJSON(const llvm::json::Value &Params,
1223+
TypeHierarchyItem::ResolveParams &RP, llvm::json::Path P) {
1224+
llvm::json::ObjectMapper O(Params, P);
1225+
return O && O.map("symbolID", RP.symbolID) &&
1226+
mapOptOrNull(Params, "parents", RP.parents, P);
1227+
}
1228+
12151229
llvm::json::Value toJSON(const TypeHierarchyItem &I) {
1216-
llvm::json::Object Result{{"name", I.name},
1217-
{"kind", static_cast<int>(I.kind)},
1218-
{"range", I.range},
1219-
{"selectionRange", I.selectionRange},
1220-
{"uri", I.uri}};
1230+
llvm::json::Object Result{
1231+
{"name", I.name}, {"kind", static_cast<int>(I.kind)},
1232+
{"range", I.range}, {"selectionRange", I.selectionRange},
1233+
{"uri", I.uri}, {"data", I.data},
1234+
};
12211235

12221236
if (I.detail)
12231237
Result["detail"] = I.detail;
1224-
if (I.deprecated)
1225-
Result["deprecated"] = I.deprecated;
1226-
if (I.parents)
1227-
Result["parents"] = I.parents;
1228-
if (I.children)
1229-
Result["children"] = I.children;
1230-
if (I.data)
1231-
Result["data"] = I.data;
12321238
return std::move(Result);
12331239
}
12341240

@@ -1250,8 +1256,9 @@ bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I,
12501256
bool fromJSON(const llvm::json::Value &Params,
12511257
ResolveTypeHierarchyItemParams &R, llvm::json::Path P) {
12521258
llvm::json::ObjectMapper O(Params, P);
1253-
return O && O.map("item", R.item) && O.map("resolve", R.resolve) &&
1254-
O.map("direction", R.direction);
1259+
return O && O.map("item", R.item) &&
1260+
mapOptOrNull(Params, "resolve", R.resolve, P) &&
1261+
mapOptOrNull(Params, "direction", R.direction, P);
12551262
}
12561263

12571264
bool fromJSON(const llvm::json::Value &Params, ReferenceContext &R,
@@ -1498,5 +1505,22 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ASTNode &Root) {
14981505
return OS;
14991506
}
15001507

1508+
bool fromJSON(const llvm::json::Value &E, SymbolID &S, llvm::json::Path P) {
1509+
auto Str = E.getAsString();
1510+
if (!Str) {
1511+
P.report("expected a string");
1512+
return false;
1513+
}
1514+
auto ID = SymbolID::fromStr(*Str);
1515+
if (!ID) {
1516+
elog("Malformed symbolid: {0}", ID.takeError());
1517+
P.report("malformed symbolid");
1518+
return false;
1519+
}
1520+
S = *ID;
1521+
return true;
1522+
}
1523+
llvm::json::Value toJSON(const SymbolID &S) { return S.str(); }
1524+
15011525
} // namespace clangd
15021526
} // namespace clang

0 commit comments

Comments
 (0)