mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 06:10:12 +00:00
[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 https://github.com/clangd/clangd/issues/826. Differential Revision: https://reviews.llvm.org/D131385
This commit is contained in:
parent
177cbb1c9b
commit
83411bf06f
@ -26,6 +26,8 @@
|
||||
#include "support/Trace.h"
|
||||
#include "clang/Tooling/Core/Replacement.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/FunctionExtras.h"
|
||||
#include "llvm/ADT/None.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
@ -571,8 +573,12 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
|
||||
{"referencesProvider", true},
|
||||
{"astProvider", true}, // clangd extension
|
||||
{"typeHierarchyProvider", true},
|
||||
{"memoryUsageProvider", true}, // clangd extension
|
||||
{"compilationDatabase", // clangd extension
|
||||
// Unfortunately our extension made use of the same capability name as the
|
||||
// standard. Advertise this capability to tell clients that implement our
|
||||
// extension we really have support for the standardized one as well.
|
||||
{"standardTypeHierarchyProvider", true}, // clangd extension
|
||||
{"memoryUsageProvider", true}, // clangd extension
|
||||
{"compilationDatabase", // clangd extension
|
||||
llvm::json::Object{{"automaticReload", true}}},
|
||||
{"callHierarchyProvider", true},
|
||||
{"clangdInlayHintsProvider", true},
|
||||
@ -1183,18 +1189,94 @@ void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
|
||||
});
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onTypeHierarchy(
|
||||
const TypeHierarchyParams &Params,
|
||||
Callback<Optional<TypeHierarchyItem>> Reply) {
|
||||
// Our extension has a different representation on the wire than the standard.
|
||||
// https://clangd.llvm.org/extensions#type-hierarchy
|
||||
llvm::json::Value serializeTHIForExtension(TypeHierarchyItem THI) {
|
||||
llvm::json::Object Result{{
|
||||
{"name", std::move(THI.name)},
|
||||
{"kind", static_cast<int>(THI.kind)},
|
||||
{"uri", std::move(THI.uri)},
|
||||
{"range", THI.range},
|
||||
{"selectionRange", THI.selectionRange},
|
||||
{"data", std::move(THI.data)},
|
||||
}};
|
||||
if (THI.deprecated)
|
||||
Result["deprecated"] = THI.deprecated;
|
||||
if (THI.detail)
|
||||
Result["detail"] = std::move(*THI.detail);
|
||||
|
||||
if (THI.parents) {
|
||||
llvm::json::Array Parents;
|
||||
for (auto &Parent : *THI.parents)
|
||||
Parents.emplace_back(serializeTHIForExtension(std::move(Parent)));
|
||||
Result["parents"] = std::move(Parents);
|
||||
}
|
||||
|
||||
if (THI.children) {
|
||||
llvm::json::Array Children;
|
||||
for (auto &child : *THI.children)
|
||||
Children.emplace_back(serializeTHIForExtension(std::move(child)));
|
||||
Result["children"] = std::move(Children);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onTypeHierarchy(const TypeHierarchyPrepareParams &Params,
|
||||
Callback<llvm::json::Value> Reply) {
|
||||
auto Serialize =
|
||||
[Reply = std::move(Reply)](
|
||||
llvm::Expected<std::vector<TypeHierarchyItem>> Resp) mutable {
|
||||
if (!Resp) {
|
||||
Reply(Resp.takeError());
|
||||
return;
|
||||
}
|
||||
if (Resp->empty()) {
|
||||
Reply(nullptr);
|
||||
return;
|
||||
}
|
||||
Reply(serializeTHIForExtension(std::move(Resp->front())));
|
||||
};
|
||||
Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
|
||||
Params.resolve, Params.direction, std::move(Reply));
|
||||
Params.resolve, Params.direction, std::move(Serialize));
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onResolveTypeHierarchy(
|
||||
const ResolveTypeHierarchyItemParams &Params,
|
||||
Callback<Optional<TypeHierarchyItem>> Reply) {
|
||||
Callback<llvm::json::Value> Reply) {
|
||||
auto Serialize =
|
||||
[Reply = std::move(Reply)](
|
||||
llvm::Expected<llvm::Optional<TypeHierarchyItem>> Resp) mutable {
|
||||
if (!Resp) {
|
||||
Reply(Resp.takeError());
|
||||
return;
|
||||
}
|
||||
if (!*Resp) {
|
||||
Reply(std::move(*Resp));
|
||||
return;
|
||||
}
|
||||
Reply(serializeTHIForExtension(std::move(**Resp)));
|
||||
};
|
||||
Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
|
||||
std::move(Reply));
|
||||
std::move(Serialize));
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onPrepareTypeHierarchy(
|
||||
const TypeHierarchyPrepareParams &Params,
|
||||
Callback<std::vector<TypeHierarchyItem>> Reply) {
|
||||
Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
|
||||
Params.resolve, Params.direction, std::move(Reply));
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onSuperTypes(
|
||||
const ResolveTypeHierarchyItemParams &Params,
|
||||
Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> Reply) {
|
||||
Server->superTypes(Params.item, std::move(Reply));
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onSubTypes(
|
||||
const ResolveTypeHierarchyItemParams &Params,
|
||||
Callback<std::vector<TypeHierarchyItem>> Reply) {
|
||||
Server->subTypes(Params.item, std::move(Reply));
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onPrepareCallHierarchy(
|
||||
@ -1523,6 +1605,9 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind,
|
||||
Bind.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo);
|
||||
Bind.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy);
|
||||
Bind.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy);
|
||||
Bind.method("textDocument/prepareTypeHierarchy", this, &ClangdLSPServer::onPrepareTypeHierarchy);
|
||||
Bind.method("typeHierarchy/supertypes", this, &ClangdLSPServer::onSuperTypes);
|
||||
Bind.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes);
|
||||
Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy);
|
||||
Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
|
||||
Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
@ -132,10 +133,16 @@ private:
|
||||
void onRename(const RenameParams &, Callback<WorkspaceEdit>);
|
||||
void onHover(const TextDocumentPositionParams &,
|
||||
Callback<llvm::Optional<Hover>>);
|
||||
void onTypeHierarchy(const TypeHierarchyParams &,
|
||||
Callback<llvm::Optional<TypeHierarchyItem>>);
|
||||
void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &,
|
||||
Callback<std::vector<TypeHierarchyItem>>);
|
||||
void onSuperTypes(const ResolveTypeHierarchyItemParams &,
|
||||
Callback<llvm::Optional<std::vector<TypeHierarchyItem>>>);
|
||||
void onSubTypes(const ResolveTypeHierarchyItemParams &,
|
||||
Callback<std::vector<TypeHierarchyItem>>);
|
||||
void onTypeHierarchy(const TypeHierarchyPrepareParams &,
|
||||
Callback<llvm::json::Value>);
|
||||
void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &,
|
||||
Callback<llvm::Optional<TypeHierarchyItem>>);
|
||||
Callback<llvm::json::Value>);
|
||||
void onPrepareCallHierarchy(const CallHierarchyPrepareParams &,
|
||||
Callback<std::vector<CallHierarchyItem>>);
|
||||
void onCallHierarchyIncomingCalls(
|
||||
|
@ -751,7 +751,7 @@ void ClangdServer::findHover(PathRef File, Position Pos,
|
||||
|
||||
void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
|
||||
TypeHierarchyDirection Direction,
|
||||
Callback<Optional<TypeHierarchyItem>> CB) {
|
||||
Callback<std::vector<TypeHierarchyItem>> CB) {
|
||||
auto Action = [File = File.str(), Pos, Resolve, Direction, CB = std::move(CB),
|
||||
this](Expected<InputsAndAST> InpAST) mutable {
|
||||
if (!InpAST)
|
||||
@ -763,6 +763,22 @@ void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
|
||||
WorkScheduler->runWithAST("TypeHierarchy", File, std::move(Action));
|
||||
}
|
||||
|
||||
void ClangdServer::superTypes(
|
||||
const TypeHierarchyItem &Item,
|
||||
Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> CB) {
|
||||
WorkScheduler->run("typeHierarchy/superTypes", /*Path=*/"",
|
||||
[=, CB = std::move(CB)]() mutable {
|
||||
CB(clangd::superTypes(Item, Index));
|
||||
});
|
||||
}
|
||||
|
||||
void ClangdServer::subTypes(const TypeHierarchyItem &Item,
|
||||
Callback<std::vector<TypeHierarchyItem>> CB) {
|
||||
WorkScheduler->run(
|
||||
"typeHierarchy/subTypes", /*Path=*/"",
|
||||
[=, CB = std::move(CB)]() mutable { CB(clangd::subTypes(Item, Index)); });
|
||||
}
|
||||
|
||||
void ClangdServer::resolveTypeHierarchy(
|
||||
TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction,
|
||||
Callback<llvm::Optional<TypeHierarchyItem>> CB) {
|
||||
|
@ -253,7 +253,13 @@ public:
|
||||
/// Get information about type hierarchy for a given position.
|
||||
void typeHierarchy(PathRef File, Position Pos, int Resolve,
|
||||
TypeHierarchyDirection Direction,
|
||||
Callback<llvm::Optional<TypeHierarchyItem>> CB);
|
||||
Callback<std::vector<TypeHierarchyItem>> CB);
|
||||
/// Get direct parents of a type hierarchy item.
|
||||
void superTypes(const TypeHierarchyItem &Item,
|
||||
Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> CB);
|
||||
/// Get direct children of a type hierarchy item.
|
||||
void subTypes(const TypeHierarchyItem &Item,
|
||||
Callback<std::vector<TypeHierarchyItem>> CB);
|
||||
|
||||
/// Resolve type hierarchy item in the given direction.
|
||||
void resolveTypeHierarchy(TypeHierarchyItem Item, int Resolve,
|
||||
|
@ -1207,12 +1207,13 @@ bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, TypeHierarchyParams &R,
|
||||
bool fromJSON(const llvm::json::Value &Params, TypeHierarchyPrepareParams &R,
|
||||
llvm::json::Path P) {
|
||||
llvm::json::ObjectMapper O(Params, P);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("position", R.position) && O.map("resolve", R.resolve) &&
|
||||
O.map("direction", R.direction);
|
||||
O.map("position", R.position) &&
|
||||
mapOptOrNull(Params, "resolve", R.resolve, P) &&
|
||||
mapOptOrNull(Params, "direction", R.direction, P);
|
||||
}
|
||||
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
|
||||
@ -1220,23 +1221,28 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
|
||||
return O << I.name << " - " << toJSON(I);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const TypeHierarchyItem::ResolveParams &RP) {
|
||||
llvm::json::Object Result{{"symbolID", RP.symbolID}};
|
||||
if (RP.parents)
|
||||
Result["parents"] = RP.parents;
|
||||
return std::move(Result);
|
||||
}
|
||||
bool fromJSON(const llvm::json::Value &Params,
|
||||
TypeHierarchyItem::ResolveParams &RP, llvm::json::Path P) {
|
||||
llvm::json::ObjectMapper O(Params, P);
|
||||
return O && O.map("symbolID", RP.symbolID) &&
|
||||
mapOptOrNull(Params, "parents", RP.parents, P);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const TypeHierarchyItem &I) {
|
||||
llvm::json::Object Result{{"name", I.name},
|
||||
{"kind", static_cast<int>(I.kind)},
|
||||
{"range", I.range},
|
||||
{"selectionRange", I.selectionRange},
|
||||
{"uri", I.uri}};
|
||||
llvm::json::Object Result{
|
||||
{"name", I.name}, {"kind", static_cast<int>(I.kind)},
|
||||
{"range", I.range}, {"selectionRange", I.selectionRange},
|
||||
{"uri", I.uri}, {"data", I.data},
|
||||
};
|
||||
|
||||
if (I.detail)
|
||||
Result["detail"] = I.detail;
|
||||
if (I.deprecated)
|
||||
Result["deprecated"] = I.deprecated;
|
||||
if (I.parents)
|
||||
Result["parents"] = I.parents;
|
||||
if (I.children)
|
||||
Result["children"] = I.children;
|
||||
if (I.data)
|
||||
Result["data"] = I.data;
|
||||
return std::move(Result);
|
||||
}
|
||||
|
||||
@ -1258,8 +1264,9 @@ bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I,
|
||||
bool fromJSON(const llvm::json::Value &Params,
|
||||
ResolveTypeHierarchyItemParams &R, llvm::json::Path P) {
|
||||
llvm::json::ObjectMapper O(Params, P);
|
||||
return O && O.map("item", R.item) && O.map("resolve", R.resolve) &&
|
||||
O.map("direction", R.direction);
|
||||
return O && O.map("item", R.item) &&
|
||||
mapOptOrNull(Params, "resolve", R.resolve, P) &&
|
||||
mapOptOrNull(Params, "direction", R.direction, P);
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, ReferenceContext &R,
|
||||
@ -1510,5 +1517,22 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ASTNode &Root) {
|
||||
return OS;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &E, SymbolID &S, llvm::json::Path P) {
|
||||
auto Str = E.getAsString();
|
||||
if (!Str) {
|
||||
P.report("expected a string");
|
||||
return false;
|
||||
}
|
||||
auto ID = SymbolID::fromStr(*Str);
|
||||
if (!ID) {
|
||||
elog("Malformed symbolid: {0}", ID.takeError());
|
||||
P.report("malformed symbolid");
|
||||
return false;
|
||||
}
|
||||
S = *ID;
|
||||
return true;
|
||||
}
|
||||
llvm::json::Value toJSON(const SymbolID &S) { return S.str(); }
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
@ -77,6 +77,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
bool fromJSON(const llvm::json::Value &, SymbolID &, llvm::json::Path);
|
||||
llvm::json::Value toJSON(const SymbolID &);
|
||||
|
||||
// URI in "file" scheme for a file.
|
||||
struct URIForFile {
|
||||
URIForFile() = default;
|
||||
@ -1379,58 +1382,66 @@ bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out,
|
||||
/// The type hierarchy params is an extension of the
|
||||
/// `TextDocumentPositionsParams` with optional properties which can be used to
|
||||
/// eagerly resolve the item when requesting from the server.
|
||||
struct TypeHierarchyParams : public TextDocumentPositionParams {
|
||||
struct TypeHierarchyPrepareParams : public TextDocumentPositionParams {
|
||||
/// The hierarchy levels to resolve. `0` indicates no level.
|
||||
/// This is a clangd extension.
|
||||
int resolve = 0;
|
||||
|
||||
/// The direction of the hierarchy levels to resolve.
|
||||
/// This is a clangd extension.
|
||||
TypeHierarchyDirection direction = TypeHierarchyDirection::Parents;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, TypeHierarchyParams &,
|
||||
bool fromJSON(const llvm::json::Value &, TypeHierarchyPrepareParams &,
|
||||
llvm::json::Path);
|
||||
|
||||
struct TypeHierarchyItem {
|
||||
/// The human readable name of the hierarchy item.
|
||||
/// The name of this item.
|
||||
std::string name;
|
||||
|
||||
/// Optional detail for the hierarchy item. It can be, for instance, the
|
||||
/// signature of a function or method.
|
||||
llvm::Optional<std::string> detail;
|
||||
|
||||
/// The kind of the hierarchy item. For instance, class or interface.
|
||||
/// The kind of this item.
|
||||
SymbolKind kind;
|
||||
|
||||
/// `true` if the hierarchy item is deprecated. Otherwise, `false`.
|
||||
bool deprecated = false;
|
||||
/// More detail for this item, e.g. the signature of a function.
|
||||
llvm::Optional<std::string> detail;
|
||||
|
||||
/// The URI of the text document where this type hierarchy item belongs to.
|
||||
/// The resource identifier of this item.
|
||||
URIForFile uri;
|
||||
|
||||
/// The range enclosing this type hierarchy item not including
|
||||
/// leading/trailing whitespace but everything else like comments. This
|
||||
/// information is typically used to determine if the client's cursor is
|
||||
/// inside the type hierarch item to reveal in the symbol in the UI.
|
||||
/// The range enclosing this symbol not including leading/trailing whitespace
|
||||
/// but everything else, e.g. comments and code.
|
||||
Range range;
|
||||
|
||||
/// The range that should be selected and revealed when this type hierarchy
|
||||
/// item is being picked, e.g. the name of a function. Must be contained by
|
||||
/// the `range`.
|
||||
/// The range that should be selected and revealed when this symbol is being
|
||||
/// picked, e.g. the name of a function. Must be contained by the `range`.
|
||||
Range selectionRange;
|
||||
|
||||
/// If this type hierarchy item is resolved, it contains the direct parents.
|
||||
/// Could be empty if the item does not have direct parents. If not defined,
|
||||
/// the parents have not been resolved yet.
|
||||
/// Used to resolve a client provided item back.
|
||||
struct ResolveParams {
|
||||
SymbolID symbolID;
|
||||
/// None means parents aren't resolved and empty is no parents.
|
||||
llvm::Optional<std::vector<ResolveParams>> parents;
|
||||
};
|
||||
/// A data entry field that is preserved between a type hierarchy prepare and
|
||||
/// supertypes or subtypes requests. It could also be used to identify the
|
||||
/// type hierarchy in the server, helping improve the performance on resolving
|
||||
/// supertypes and subtypes.
|
||||
ResolveParams data;
|
||||
|
||||
/// `true` if the hierarchy item is deprecated. Otherwise, `false`.
|
||||
/// This is a clangd exntesion.
|
||||
bool deprecated = false;
|
||||
|
||||
/// This is a clangd exntesion.
|
||||
llvm::Optional<std::vector<TypeHierarchyItem>> parents;
|
||||
|
||||
/// If this type hierarchy item is resolved, it contains the direct children
|
||||
/// of the current item. Could be empty if the item does not have any
|
||||
/// descendants. If not defined, the children have not been resolved.
|
||||
/// This is a clangd exntesion.
|
||||
llvm::Optional<std::vector<TypeHierarchyItem>> children;
|
||||
|
||||
/// An optional 'data' field, which can be used to identify a type hierarchy
|
||||
/// item in a resolve request.
|
||||
llvm::Optional<std::string> data;
|
||||
};
|
||||
llvm::json::Value toJSON(const TypeHierarchyItem::ResolveParams &);
|
||||
bool fromJSON(const TypeHierarchyItem::ResolveParams &);
|
||||
llvm::json::Value toJSON(const TypeHierarchyItem &);
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TypeHierarchyItem &);
|
||||
bool fromJSON(const llvm::json::Value &, TypeHierarchyItem &, llvm::json::Path);
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/None.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
@ -60,6 +61,7 @@
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
@ -864,7 +866,8 @@ public:
|
||||
};
|
||||
|
||||
ReferenceFinder(const ParsedAST &AST,
|
||||
const llvm::ArrayRef<const NamedDecl *> Targets, bool PerToken)
|
||||
const llvm::ArrayRef<const NamedDecl *> Targets,
|
||||
bool PerToken)
|
||||
: PerToken(PerToken), AST(AST) {
|
||||
for (const NamedDecl *ND : Targets) {
|
||||
const Decl *CD = ND->getCanonicalDecl();
|
||||
@ -937,7 +940,7 @@ private:
|
||||
};
|
||||
|
||||
std::vector<ReferenceFinder::Reference>
|
||||
findRefs(const llvm::ArrayRef<const NamedDecl*> TargetDecls, ParsedAST &AST,
|
||||
findRefs(const llvm::ArrayRef<const NamedDecl *> TargetDecls, ParsedAST &AST,
|
||||
bool PerToken) {
|
||||
ReferenceFinder RefFinder(AST, TargetDecls, PerToken);
|
||||
index::IndexingOptions IndexOpts;
|
||||
@ -1225,8 +1228,8 @@ std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
|
||||
if (const SelectionTree::Node *N = ST.commonAncestor()) {
|
||||
DeclRelationSet Relations =
|
||||
DeclRelation::TemplatePattern | DeclRelation::Alias;
|
||||
auto TargetDecls=
|
||||
targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver());
|
||||
auto TargetDecls =
|
||||
targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver());
|
||||
if (!TargetDecls.empty()) {
|
||||
// FIXME: we may get multiple DocumentHighlights with the same location
|
||||
// and different kinds, deduplicate them.
|
||||
@ -1595,29 +1598,32 @@ declToHierarchyItem(const NamedDecl &ND, llvm::StringRef 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))
|
||||
HI.data = ID.str();
|
||||
|
||||
return HI;
|
||||
}
|
||||
|
||||
static llvm::Optional<TypeHierarchyItem>
|
||||
declToTypeHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
|
||||
auto Result = declToHierarchyItem<TypeHierarchyItem>(ND, TUPath);
|
||||
if (Result)
|
||||
if (Result) {
|
||||
Result->deprecated = ND.isDeprecated();
|
||||
// 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.
|
||||
Result->data.symbolID = getSymbolID(&ND);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
static llvm::Optional<CallHierarchyItem>
|
||||
declToCallHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
|
||||
auto Result = declToHierarchyItem<CallHierarchyItem>(ND, TUPath);
|
||||
if (Result && ND.isDeprecated())
|
||||
if (!Result)
|
||||
return Result;
|
||||
if (ND.isDeprecated())
|
||||
Result->tags.push_back(SymbolTag::Deprecated);
|
||||
if (auto ID = getSymbolID(&ND))
|
||||
Result->data = ID.str();
|
||||
return Result;
|
||||
}
|
||||
|
||||
@ -1637,10 +1643,6 @@ static llvm::Optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
|
||||
// (https://github.com/clangd/clangd/issues/59).
|
||||
HI.range = HI.selectionRange;
|
||||
HI.uri = Loc->uri;
|
||||
// Store the SymbolID in the 'data' field. The client will
|
||||
// send this back in requests to resolve additional levels
|
||||
// of the hierarchy.
|
||||
HI.data = S.ID.str();
|
||||
|
||||
return HI;
|
||||
}
|
||||
@ -1648,15 +1650,20 @@ static llvm::Optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
|
||||
static llvm::Optional<TypeHierarchyItem>
|
||||
symbolToTypeHierarchyItem(const Symbol &S, PathRef TUPath) {
|
||||
auto Result = symbolToHierarchyItem<TypeHierarchyItem>(S, TUPath);
|
||||
if (Result)
|
||||
if (Result) {
|
||||
Result->deprecated = (S.Flags & Symbol::Deprecated);
|
||||
Result->data.symbolID = S.ID;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
static llvm::Optional<CallHierarchyItem>
|
||||
symbolToCallHierarchyItem(const Symbol &S, PathRef TUPath) {
|
||||
auto Result = symbolToHierarchyItem<CallHierarchyItem>(S, TUPath);
|
||||
if (Result && (S.Flags & Symbol::Deprecated))
|
||||
if (!Result)
|
||||
return Result;
|
||||
Result->data = S.ID.str();
|
||||
if (S.Flags & Symbol::Deprecated)
|
||||
Result->tags.push_back(SymbolTag::Deprecated);
|
||||
return Result;
|
||||
}
|
||||
@ -1681,9 +1688,12 @@ static void fillSubTypes(const SymbolID &ID,
|
||||
|
||||
using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>;
|
||||
|
||||
// Extracts parents from AST and populates the type hierarchy item.
|
||||
static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,
|
||||
std::vector<TypeHierarchyItem> &SuperTypes,
|
||||
TypeHierarchyItem &Item,
|
||||
RecursionProtectionSet &RPSet) {
|
||||
Item.parents.emplace();
|
||||
Item.data.parents.emplace();
|
||||
// typeParents() will replace dependent template specializations
|
||||
// with their class template, so to avoid infinite recursion for
|
||||
// certain types of hierarchies, keep the templates encountered
|
||||
@ -1699,9 +1709,9 @@ static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,
|
||||
for (const CXXRecordDecl *ParentDecl : typeParents(&CXXRD)) {
|
||||
if (Optional<TypeHierarchyItem> ParentSym =
|
||||
declToTypeHierarchyItem(*ParentDecl, TUPath)) {
|
||||
ParentSym->parents.emplace();
|
||||
fillSuperTypes(*ParentDecl, TUPath, *ParentSym->parents, RPSet);
|
||||
SuperTypes.emplace_back(std::move(*ParentSym));
|
||||
fillSuperTypes(*ParentDecl, TUPath, *ParentSym, RPSet);
|
||||
Item.data.parents->emplace_back(ParentSym->data);
|
||||
Item.parents->emplace_back(std::move(*ParentSym));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1710,11 +1720,12 @@ static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,
|
||||
}
|
||||
}
|
||||
|
||||
const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
|
||||
auto RecordFromNode =
|
||||
[&AST](const SelectionTree::Node *N) -> const CXXRecordDecl * {
|
||||
std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST,
|
||||
Position Pos) {
|
||||
auto RecordFromNode = [&AST](const SelectionTree::Node *N) {
|
||||
std::vector<const CXXRecordDecl *> Records;
|
||||
if (!N)
|
||||
return nullptr;
|
||||
return Records;
|
||||
|
||||
// Note: explicitReferenceTargets() will search for both template
|
||||
// instantiations and template patterns, and prefer the former if available
|
||||
@ -1722,30 +1733,32 @@ const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
|
||||
// class template).
|
||||
auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying,
|
||||
AST.getHeuristicResolver());
|
||||
if (Decls.empty())
|
||||
return nullptr;
|
||||
for (const NamedDecl *D : Decls) {
|
||||
|
||||
const NamedDecl *D = Decls[0];
|
||||
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
|
||||
// If this is a variable, use the type of the variable.
|
||||
Records.push_back(VD->getType().getTypePtr()->getAsCXXRecordDecl());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
|
||||
// If this is a variable, use the type of the variable.
|
||||
return VD->getType().getTypePtr()->getAsCXXRecordDecl();
|
||||
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
|
||||
// If this is a method, use the type of the class.
|
||||
Records.push_back(Method->getParent());
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't handle FieldDecl because it's not clear what behaviour
|
||||
// the user would expect: the enclosing class type (as with a
|
||||
// method), or the field's type (as with a variable).
|
||||
|
||||
if (auto *RD = dyn_cast<CXXRecordDecl>(D))
|
||||
Records.push_back(RD);
|
||||
}
|
||||
|
||||
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
|
||||
// If this is a method, use the type of the class.
|
||||
return Method->getParent();
|
||||
}
|
||||
|
||||
// We don't handle FieldDecl because it's not clear what behaviour
|
||||
// the user would expect: the enclosing class type (as with a
|
||||
// method), or the field's type (as with a variable).
|
||||
|
||||
return dyn_cast<CXXRecordDecl>(D);
|
||||
return Records;
|
||||
};
|
||||
|
||||
const SourceManager &SM = AST.getSourceManager();
|
||||
const CXXRecordDecl *Result = nullptr;
|
||||
std::vector<const CXXRecordDecl *> Result;
|
||||
auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos);
|
||||
if (!Offset) {
|
||||
llvm::consumeError(Offset.takeError());
|
||||
@ -1754,7 +1767,7 @@ const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
|
||||
SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), *Offset,
|
||||
*Offset, [&](SelectionTree ST) {
|
||||
Result = RecordFromNode(ST.commonAncestor());
|
||||
return Result != nullptr;
|
||||
return !Result.empty();
|
||||
});
|
||||
return Result;
|
||||
}
|
||||
@ -1998,53 +2011,79 @@ std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
llvm::Optional<TypeHierarchyItem>
|
||||
std::vector<TypeHierarchyItem>
|
||||
getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
|
||||
TypeHierarchyDirection Direction, const SymbolIndex *Index,
|
||||
PathRef TUPath) {
|
||||
const CXXRecordDecl *CXXRD = findRecordTypeAt(AST, Pos);
|
||||
if (!CXXRD)
|
||||
return llvm::None;
|
||||
std::vector<TypeHierarchyItem> Results;
|
||||
for (const auto *CXXRD : findRecordTypeAt(AST, Pos)) {
|
||||
|
||||
bool WantParents = Direction == TypeHierarchyDirection::Parents ||
|
||||
Direction == TypeHierarchyDirection::Both;
|
||||
bool WantChildren = Direction == TypeHierarchyDirection::Children ||
|
||||
Direction == TypeHierarchyDirection::Both;
|
||||
bool WantChildren = Direction == TypeHierarchyDirection::Children ||
|
||||
Direction == TypeHierarchyDirection::Both;
|
||||
|
||||
// If we're looking for children, we're doing the lookup in the index.
|
||||
// The index does not store relationships between implicit
|
||||
// specializations, so if we have one, use the template pattern instead.
|
||||
// Note that this needs to be done before the declToTypeHierarchyItem(),
|
||||
// otherwise the type hierarchy item would misleadingly contain the
|
||||
// specialization parameters, while the children would involve classes
|
||||
// that derive from other specializations of the template.
|
||||
if (WantChildren) {
|
||||
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
|
||||
CXXRD = CTSD->getTemplateInstantiationPattern();
|
||||
}
|
||||
// If we're looking for children, we're doing the lookup in the index.
|
||||
// The index does not store relationships between implicit
|
||||
// specializations, so if we have one, use the template pattern instead.
|
||||
// Note that this needs to be done before the declToTypeHierarchyItem(),
|
||||
// otherwise the type hierarchy item would misleadingly contain the
|
||||
// specialization parameters, while the children would involve classes
|
||||
// that derive from other specializations of the template.
|
||||
if (WantChildren) {
|
||||
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
|
||||
CXXRD = CTSD->getTemplateInstantiationPattern();
|
||||
}
|
||||
|
||||
Optional<TypeHierarchyItem> Result =
|
||||
declToTypeHierarchyItem(*CXXRD, AST.tuPath());
|
||||
if (!Result)
|
||||
return Result;
|
||||
|
||||
if (WantParents) {
|
||||
Result->parents.emplace();
|
||||
Optional<TypeHierarchyItem> Result =
|
||||
declToTypeHierarchyItem(*CXXRD, AST.tuPath());
|
||||
if (!Result)
|
||||
continue;
|
||||
|
||||
RecursionProtectionSet RPSet;
|
||||
fillSuperTypes(*CXXRD, AST.tuPath(), *Result->parents, RPSet);
|
||||
}
|
||||
fillSuperTypes(*CXXRD, AST.tuPath(), *Result, RPSet);
|
||||
|
||||
if (WantChildren && ResolveLevels > 0) {
|
||||
Result->children.emplace();
|
||||
if (WantChildren && ResolveLevels > 0) {
|
||||
Result->children.emplace();
|
||||
|
||||
if (Index) {
|
||||
if (auto ID = getSymbolID(CXXRD))
|
||||
fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath);
|
||||
if (Index) {
|
||||
if (auto ID = getSymbolID(CXXRD))
|
||||
fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath);
|
||||
}
|
||||
}
|
||||
Results.emplace_back(std::move(*Result));
|
||||
}
|
||||
|
||||
return Result;
|
||||
return Results;
|
||||
}
|
||||
|
||||
llvm::Optional<std::vector<TypeHierarchyItem>>
|
||||
superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) {
|
||||
std::vector<TypeHierarchyItem> Results;
|
||||
if (!Item.data.parents)
|
||||
return llvm::None;
|
||||
if (Item.data.parents->empty())
|
||||
return Results;
|
||||
LookupRequest Req;
|
||||
llvm::DenseMap<SymbolID, const TypeHierarchyItem::ResolveParams *> IDToData;
|
||||
for (const auto &Parent : *Item.data.parents) {
|
||||
Req.IDs.insert(Parent.symbolID);
|
||||
IDToData[Parent.symbolID] = &Parent;
|
||||
}
|
||||
Index->lookup(Req, [&Item, &Results, &IDToData](const Symbol &S) {
|
||||
if (auto THI = symbolToTypeHierarchyItem(S, Item.uri.file())) {
|
||||
THI->data = *IDToData.lookup(S.ID);
|
||||
Results.emplace_back(std::move(*THI));
|
||||
}
|
||||
});
|
||||
return Results;
|
||||
}
|
||||
|
||||
std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item,
|
||||
const SymbolIndex *Index) {
|
||||
std::vector<TypeHierarchyItem> Results;
|
||||
fillSubTypes(Item.data.symbolID, Results, Index, 1, Item.uri.file());
|
||||
for (auto &ChildSym : Results)
|
||||
ChildSym.data.parents = {Item.data};
|
||||
return Results;
|
||||
}
|
||||
|
||||
void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
|
||||
@ -2052,18 +2091,13 @@ void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
|
||||
const SymbolIndex *Index) {
|
||||
// We only support typeHierarchy/resolve for children, because for parents
|
||||
// we ignore ResolveLevels and return all levels of parents eagerly.
|
||||
if (Direction == TypeHierarchyDirection::Parents || ResolveLevels == 0)
|
||||
if (!Index || Direction == TypeHierarchyDirection::Parents ||
|
||||
ResolveLevels == 0)
|
||||
return;
|
||||
|
||||
Item.children.emplace();
|
||||
|
||||
if (Index && Item.data) {
|
||||
// We store the item's SymbolID in the 'data' field, and the client
|
||||
// passes it back to us in typeHierarchy/resolve.
|
||||
if (Expected<SymbolID> ID = SymbolID::fromStr(*Item.data)) {
|
||||
fillSubTypes(*ID, *Item.children, Index, ResolveLevels, Item.uri.file());
|
||||
}
|
||||
}
|
||||
fillSubTypes(Item.data.symbolID, *Item.children, Index, ResolveLevels,
|
||||
Item.uri.file());
|
||||
}
|
||||
|
||||
std::vector<CallHierarchyItem>
|
||||
|
@ -117,17 +117,26 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
|
||||
/// Get info about symbols at \p Pos.
|
||||
std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos);
|
||||
|
||||
/// Find the record type references at \p Pos.
|
||||
const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos);
|
||||
/// Find the record types referenced at \p Pos.
|
||||
std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST,
|
||||
Position Pos);
|
||||
|
||||
/// Given a record type declaration, find its base (parent) types.
|
||||
std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD);
|
||||
|
||||
/// Get type hierarchy information at \p Pos.
|
||||
llvm::Optional<TypeHierarchyItem> getTypeHierarchy(
|
||||
std::vector<TypeHierarchyItem> getTypeHierarchy(
|
||||
ParsedAST &AST, Position Pos, int Resolve, TypeHierarchyDirection Direction,
|
||||
const SymbolIndex *Index = nullptr, PathRef TUPath = PathRef{});
|
||||
|
||||
/// Returns direct parents of a TypeHierarchyItem using SymbolIDs stored inside
|
||||
/// the item.
|
||||
llvm::Optional<std::vector<TypeHierarchyItem>>
|
||||
superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index);
|
||||
/// Returns direct children of a TypeHierarchyItem.
|
||||
std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item,
|
||||
const SymbolIndex *Index);
|
||||
|
||||
void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
|
||||
TypeHierarchyDirection Direction,
|
||||
const SymbolIndex *Index);
|
||||
|
@ -88,6 +88,7 @@
|
||||
# CHECK-NEXT: ","
|
||||
# CHECK-NEXT: ]
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "standardTypeHierarchyProvider": true,
|
||||
# CHECK-NEXT: "textDocumentSync": {
|
||||
# CHECK-NEXT: "change": 2,
|
||||
# CHECK-NEXT: "openClose": true,
|
||||
|
211
clang-tools-extra/clangd/test/type-hierarchy-ext.test
Normal file
211
clang-tools-extra/clangd/test/type-hierarchy-ext.test
Normal file
@ -0,0 +1,211 @@
|
||||
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
|
||||
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
|
||||
---
|
||||
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct Parent {};\nstruct Child1 : Parent {};\nstruct Child2 : Child1 {};\nstruct Child3 : Child2 {};\nstruct Child4 : Child3 {};"}}}
|
||||
---
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/typeHierarchy","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":2,"character":11},"direction":2,"resolve":1}}
|
||||
# CHECK: "id": 1
|
||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||
# CHECK-NEXT: "result": {
|
||||
# CHECK-NEXT: "children": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": {
|
||||
# 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: ],
|
||||
# CHECK-NEXT: "data": {
|
||||
# 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: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child2",
|
||||
# CHECK-NEXT: "parents": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": {
|
||||
# CHECK-NEXT: "parents": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "parents": [],
|
||||
# CHECK-NEXT: "symbolID": "FE546E7B648D69A7"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "symbolID": "ECDC0C46D75120F4"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child1",
|
||||
# CHECK-NEXT: "parents": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": {
|
||||
# CHECK-NEXT: "parents": [],
|
||||
# CHECK-NEXT: "symbolID": "FE546E7B648D69A7"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Parent",
|
||||
# CHECK-NEXT: "parents": [],
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 16,
|
||||
# CHECK-NEXT: "line": 0
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 0,
|
||||
# CHECK-NEXT: "line": 0
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 0
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 0
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 25,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 0,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 25,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 0,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# CHECK-NEXT: }
|
||||
---
|
||||
{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/resolve","params":{"item":{"uri":"test:///main.cpp","data":{"symbolID":"A6576FE083F2949A"},"name":"Child3","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}},"direction":0,"resolve":1}}
|
||||
# CHECK: "id": 2
|
||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||
# CHECK-NEXT: "result": {
|
||||
# CHECK-NEXT: "children": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": {
|
||||
# CHECK-NEXT: "symbolID": "5705B382DFC77CBC"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child4",
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 4
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 4
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 4
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 4
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "data": {
|
||||
# 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: }
|
||||
---
|
||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||
---
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
@ -3,178 +3,140 @@
|
||||
---
|
||||
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct Parent {};\nstruct Child1 : Parent {};\nstruct Child2 : Child1 {};\nstruct Child3 : Child2 {};\nstruct Child4 : Child3 {};"}}}
|
||||
---
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/typeHierarchy","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":2,"character":11},"direction":2,"resolve":1}}
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/prepareTypeHierarchy","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":2,"character":11},"direction":2,"resolve":1}}
|
||||
# CHECK: "id": 1
|
||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||
# CHECK-NEXT: "result": {
|
||||
# CHECK-NEXT: "children": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": "A6576FE083F2949A",
|
||||
# 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: ],
|
||||
# CHECK-NEXT: "data": "8A991335E4E67D08",
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child2",
|
||||
# CHECK-NEXT: "parents": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": "ECDC0C46D75120F4",
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child1",
|
||||
# CHECK-NEXT: "parents": [
|
||||
# CHECK-NEXT: "result": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": {
|
||||
# CHECK-NEXT: "parents": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": "FE546E7B648D69A7",
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Parent",
|
||||
# CHECK-NEXT: "parents": [],
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 16,
|
||||
# CHECK-NEXT: "line": 0
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 0,
|
||||
# CHECK-NEXT: "line": 0
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 0
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 0
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# 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: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 25,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 0,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 25,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 0,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: "symbolID": "8A991335E4E67D08"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child2",
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 25,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 0,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 2
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ]
|
||||
---
|
||||
{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/resolve","params":{"item":{"uri":"test:///main.cpp","data":"A6576FE083F2949A","name":"Child3","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}},"direction":0,"resolve":1}}
|
||||
{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/supertypes","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: "children": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": "5705B382DFC77CBC",
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child4",
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 4
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 4
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 4
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 4
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "data": "A6576FE083F2949A",
|
||||
# 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: "result": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "data": {
|
||||
# CHECK-NEXT: "parents": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "parents": [],
|
||||
# CHECK-NEXT: "symbolID": "FE546E7B648D69A7"
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "symbolID": "ECDC0C46D75120F4"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child1",
|
||||
# CHECK-NEXT: "range": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "selectionRange": {
|
||||
# CHECK-NEXT: "end": {
|
||||
# CHECK-NEXT: "character": 13,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "start": {
|
||||
# CHECK-NEXT: "character": 7,
|
||||
# CHECK-NEXT: "line": 1
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/main.cpp"
|
||||
# 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"}
|
||||
---
|
||||
|
@ -5,6 +5,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "AST.h"
|
||||
#include "Annotations.h"
|
||||
#include "Matchers.h"
|
||||
#include "ParsedAST.h"
|
||||
@ -16,6 +17,7 @@
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
@ -26,6 +28,7 @@ using ::testing::ElementsAre;
|
||||
using ::testing::Field;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::Matcher;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
// GMock helpers for matching TypeHierarchyItem.
|
||||
@ -45,6 +48,10 @@ template <class... ChildMatchers>
|
||||
// Note: "not resolved" is different from "resolved but empty"!
|
||||
MATCHER(parentsNotResolved, "") { return !arg.parents; }
|
||||
MATCHER(childrenNotResolved, "") { return !arg.children; }
|
||||
MATCHER_P(withResolveID, SID, "") { return arg.symbolID.str() == SID; }
|
||||
MATCHER_P(withResolveParents, M, "") {
|
||||
return testing::ExplainMatchResult(M, arg.data.parents, result_listener);
|
||||
}
|
||||
|
||||
TEST(FindRecordTypeAt, TypeOrVariable) {
|
||||
Annotations Source(R"cpp(
|
||||
@ -64,8 +71,10 @@ int main() {
|
||||
auto AST = TU.build();
|
||||
|
||||
for (Position Pt : Source.points()) {
|
||||
const CXXRecordDecl *RD = findRecordTypeAt(AST, Pt);
|
||||
EXPECT_EQ(&findDecl(AST, "Child2"), static_cast<const NamedDecl *>(RD));
|
||||
auto Records = findRecordTypeAt(AST, Pt);
|
||||
ASSERT_THAT(Records, SizeIs(1));
|
||||
EXPECT_EQ(&findDecl(AST, "Child2"),
|
||||
static_cast<const NamedDecl *>(Records.front()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,8 +95,10 @@ int main() {
|
||||
auto AST = TU.build();
|
||||
|
||||
for (Position Pt : Source.points()) {
|
||||
const CXXRecordDecl *RD = findRecordTypeAt(AST, Pt);
|
||||
EXPECT_EQ(&findDecl(AST, "Child2"), static_cast<const NamedDecl *>(RD));
|
||||
auto Records = findRecordTypeAt(AST, Pt);
|
||||
ASSERT_THAT(Records, SizeIs(1));
|
||||
EXPECT_EQ(&findDecl(AST, "Child2"),
|
||||
static_cast<const NamedDecl *>(Records.front()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,11 +118,10 @@ int main() {
|
||||
auto AST = TU.build();
|
||||
|
||||
for (Position Pt : Source.points()) {
|
||||
const CXXRecordDecl *RD = findRecordTypeAt(AST, Pt);
|
||||
// A field does not unambiguously specify a record type
|
||||
// (possible associated reocrd types could be the field's type,
|
||||
// or the type of the record that the field is a member of).
|
||||
EXPECT_EQ(nullptr, RD);
|
||||
EXPECT_THAT(findRecordTypeAt(AST, Pt), SizeIs(0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,11 +369,11 @@ int main() {
|
||||
for (Position Pt : Source.points()) {
|
||||
// Set ResolveLevels to 0 because it's only used for Children;
|
||||
// for Parents, getTypeHierarchy() always returns all levels.
|
||||
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
AST, Pt, /*ResolveLevels=*/0, TypeHierarchyDirection::Parents);
|
||||
ASSERT_TRUE(bool(Result));
|
||||
auto Result = getTypeHierarchy(AST, Pt, /*ResolveLevels=*/0,
|
||||
TypeHierarchyDirection::Parents);
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(
|
||||
*Result,
|
||||
Result.front(),
|
||||
AllOf(
|
||||
withName("Child"), withKind(SymbolKind::Struct),
|
||||
parents(AllOf(withName("Parent1"), withKind(SymbolKind::Struct),
|
||||
@ -398,11 +408,11 @@ TEST(TypeHierarchy, RecursiveHierarchyUnbounded) {
|
||||
// The parent is reported as "S" because "S<0>" is an invalid instantiation.
|
||||
// We then iterate once more and find "S" again before detecting the
|
||||
// recursion.
|
||||
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
AST, Source.points()[0], 0, TypeHierarchyDirection::Parents);
|
||||
ASSERT_TRUE(bool(Result));
|
||||
auto Result = getTypeHierarchy(AST, Source.points()[0], 0,
|
||||
TypeHierarchyDirection::Parents);
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(
|
||||
*Result,
|
||||
Result.front(),
|
||||
AllOf(withName("S<0>"), withKind(SymbolKind::Struct),
|
||||
parents(
|
||||
AllOf(withName("S"), withKind(SymbolKind::Struct),
|
||||
@ -432,11 +442,11 @@ TEST(TypeHierarchy, RecursiveHierarchyBounded) {
|
||||
|
||||
// Make sure getTypeHierarchy() doesn't get into an infinite recursion
|
||||
// for either a concrete starting point or a dependent starting point.
|
||||
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
AST, Source.point("SRefConcrete"), 0, TypeHierarchyDirection::Parents);
|
||||
ASSERT_TRUE(bool(Result));
|
||||
auto Result = getTypeHierarchy(AST, Source.point("SRefConcrete"), 0,
|
||||
TypeHierarchyDirection::Parents);
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(
|
||||
*Result,
|
||||
Result.front(),
|
||||
AllOf(withName("S<2>"), withKind(SymbolKind::Struct),
|
||||
parents(AllOf(
|
||||
withName("S<1>"), withKind(SymbolKind::Struct),
|
||||
@ -445,9 +455,9 @@ TEST(TypeHierarchy, RecursiveHierarchyBounded) {
|
||||
parents()))))));
|
||||
Result = getTypeHierarchy(AST, Source.point("SRefDependent"), 0,
|
||||
TypeHierarchyDirection::Parents);
|
||||
ASSERT_TRUE(bool(Result));
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(
|
||||
*Result,
|
||||
Result.front(),
|
||||
AllOf(withName("S"), withKind(SymbolKind::Struct),
|
||||
parents(AllOf(withName("S"), withKind(SymbolKind::Struct),
|
||||
selectionRangeIs(Source.range("SDef")), parents()))));
|
||||
@ -469,11 +479,11 @@ TEST(TypeHierarchy, DeriveFromImplicitSpec) {
|
||||
auto AST = TU.build();
|
||||
auto Index = TU.index();
|
||||
|
||||
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_TRUE(bool(Result));
|
||||
EXPECT_THAT(*Result,
|
||||
auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
|
||||
TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(Result.front(),
|
||||
AllOf(withName("Parent"), withKind(SymbolKind::Struct),
|
||||
children(AllOf(withName("Child1"),
|
||||
withKind(SymbolKind::Struct), children()),
|
||||
@ -495,12 +505,12 @@ TEST(TypeHierarchy, DeriveFromPartialSpec) {
|
||||
auto AST = TU.build();
|
||||
auto Index = TU.index();
|
||||
|
||||
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_TRUE(bool(Result));
|
||||
EXPECT_THAT(*Result, AllOf(withName("Parent"), withKind(SymbolKind::Struct),
|
||||
children()));
|
||||
auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
|
||||
TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(Result.front(), AllOf(withName("Parent"),
|
||||
withKind(SymbolKind::Struct), children()));
|
||||
}
|
||||
|
||||
TEST(TypeHierarchy, DeriveFromTemplate) {
|
||||
@ -521,11 +531,11 @@ TEST(TypeHierarchy, DeriveFromTemplate) {
|
||||
// FIXME: We'd like this to show the implicit specializations Parent<int>
|
||||
// and Child<int>, but currently libIndex does not expose relationships
|
||||
// between implicit specializations.
|
||||
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_TRUE(bool(Result));
|
||||
EXPECT_THAT(*Result,
|
||||
auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
|
||||
TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(Result.front(),
|
||||
AllOf(withName("Parent"), withKind(SymbolKind::Struct),
|
||||
children(AllOf(withName("Child"),
|
||||
withKind(SymbolKind::Struct), children()))));
|
||||
@ -546,12 +556,12 @@ struct [[Parent]] {
|
||||
TU.HeaderCode = HeaderInPreambleAnnotations.code().str();
|
||||
auto AST = TU.build();
|
||||
|
||||
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
std::vector<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
AST, SourceAnnotations.point(), 1, TypeHierarchyDirection::Parents);
|
||||
|
||||
ASSERT_TRUE(Result);
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(
|
||||
*Result,
|
||||
Result.front(),
|
||||
AllOf(withName("Child"),
|
||||
parents(AllOf(withName("Parent"),
|
||||
selectionRangeIs(HeaderInPreambleAnnotations.range()),
|
||||
@ -722,22 +732,21 @@ struct Child2b : Child1 {};
|
||||
auto AST = TU.build();
|
||||
auto Index = TU.index();
|
||||
|
||||
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
|
||||
AST, Source.point(), /*ResolveLevels=*/1,
|
||||
TypeHierarchyDirection::Children, Index.get(), testPath(TU.Filename));
|
||||
ASSERT_TRUE(bool(Result));
|
||||
auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
|
||||
TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
EXPECT_THAT(
|
||||
*Result,
|
||||
AllOf(withName("Parent"), withKind(SymbolKind::Struct),
|
||||
parentsNotResolved(),
|
||||
Result.front(),
|
||||
AllOf(withName("Parent"), withKind(SymbolKind::Struct), parents(),
|
||||
children(AllOf(withName("Child1"), withKind(SymbolKind::Struct),
|
||||
parentsNotResolved(), childrenNotResolved()))));
|
||||
|
||||
resolveTypeHierarchy((*Result->children)[0], /*ResolveLevels=*/1,
|
||||
resolveTypeHierarchy((*Result.front().children)[0], /*ResolveLevels=*/1,
|
||||
TypeHierarchyDirection::Children, Index.get());
|
||||
|
||||
EXPECT_THAT(
|
||||
(*Result->children)[0],
|
||||
(*Result.front().children)[0],
|
||||
AllOf(withName("Child1"), withKind(SymbolKind::Struct),
|
||||
parentsNotResolved(),
|
||||
children(AllOf(withName("Child2a"), withKind(SymbolKind::Struct),
|
||||
@ -746,6 +755,53 @@ struct Child2b : Child1 {};
|
||||
parentsNotResolved(), childrenNotResolved()))));
|
||||
}
|
||||
|
||||
TEST(Standard, SubTypes) {
|
||||
Annotations Source(R"cpp(
|
||||
struct Pare^nt1 {};
|
||||
struct Parent2 {};
|
||||
struct Child : Parent1, Parent2 {};
|
||||
)cpp");
|
||||
|
||||
TestTU TU = TestTU::withCode(Source.code());
|
||||
auto AST = TU.build();
|
||||
auto Index = TU.index();
|
||||
|
||||
auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
|
||||
TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
auto Children = subTypes(Result.front(), Index.get());
|
||||
|
||||
// Make sure parents are populated when getting children.
|
||||
// FIXME: This is partial.
|
||||
EXPECT_THAT(
|
||||
Children,
|
||||
UnorderedElementsAre(
|
||||
AllOf(withName("Child"),
|
||||
withResolveParents(HasValue(UnorderedElementsAre(withResolveID(
|
||||
getSymbolID(&findDecl(AST, "Parent1")).str())))))));
|
||||
}
|
||||
|
||||
TEST(Standard, SuperTypes) {
|
||||
Annotations Source(R"cpp(
|
||||
struct Parent {};
|
||||
struct Chil^d : Parent {};
|
||||
)cpp");
|
||||
|
||||
TestTU TU = TestTU::withCode(Source.code());
|
||||
auto AST = TU.build();
|
||||
auto Index = TU.index();
|
||||
|
||||
auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
|
||||
TypeHierarchyDirection::Children, Index.get(),
|
||||
testPath(TU.Filename));
|
||||
ASSERT_THAT(Result, SizeIs(1));
|
||||
auto Parents = superTypes(Result.front(), Index.get());
|
||||
|
||||
EXPECT_THAT(Parents, HasValue(UnorderedElementsAre(
|
||||
AllOf(withName("Parent"),
|
||||
withResolveParents(HasValue(IsEmpty()))))));
|
||||
}
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
Loading…
Reference in New Issue
Block a user