Skip to content

Commit 61fe67a

Browse files
HighCommander4kadircet
authored andcommitted
[clangd] support outgoing calls in call hierarchy (#117673)
This reverts commit ce0f113.
1 parent 5d38a34 commit 61fe67a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+770
-171
lines changed

Diff for: clang-tools-extra/clangd/ClangdLSPServer.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,12 @@ void ClangdLSPServer::onInlayHint(const InlayHintsParams &Params,
14151415
std::move(Reply));
14161416
}
14171417

1418+
void ClangdLSPServer::onCallHierarchyOutgoingCalls(
1419+
const CallHierarchyOutgoingCallsParams &Params,
1420+
Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
1421+
Server->outgoingCalls(Params.item, std::move(Reply));
1422+
}
1423+
14181424
void ClangdLSPServer::applyConfiguration(
14191425
const ConfigurationSettings &Settings) {
14201426
// Per-file update to the compilation database.
@@ -1693,6 +1699,8 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind,
16931699
Bind.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes);
16941700
Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy);
16951701
Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1702+
if (Opts.EnableOutgoingCalls)
1703+
Bind.method("callHierarchy/outgoingCalls", this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
16961704
Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange);
16971705
Bind.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink);
16981706
Bind.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens);

Diff for: clang-tools-extra/clangd/ClangdLSPServer.h

+3
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
156156
void onCallHierarchyIncomingCalls(
157157
const CallHierarchyIncomingCallsParams &,
158158
Callback<std::vector<CallHierarchyIncomingCall>>);
159+
void onCallHierarchyOutgoingCalls(
160+
const CallHierarchyOutgoingCallsParams &,
161+
Callback<std::vector<CallHierarchyOutgoingCall>>);
159162
void onClangdInlayHints(const InlayHintsParams &,
160163
Callback<llvm::json::Value>);
161164
void onInlayHint(const InlayHintsParams &, Callback<std::vector<InlayHint>>);

Diff for: clang-tools-extra/clangd/ClangdServer.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,9 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
215215
const ThreadsafeFS &TFS, const Options &Opts,
216216
Callbacks *Callbacks)
217217
: FeatureModules(Opts.FeatureModules), CDB(CDB), TFS(TFS),
218-
DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex() : nullptr),
218+
DynamicIdx(Opts.BuildDynamicSymbolIndex
219+
? new FileIndex(Opts.EnableOutgoingCalls)
220+
: nullptr),
219221
ModulesManager(Opts.ModulesManager),
220222
ClangTidyProvider(Opts.ClangTidyProvider),
221223
UseDirtyHeaders(Opts.UseDirtyHeaders),
@@ -256,6 +258,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
256258
Callbacks->onBackgroundIndexProgress(S);
257259
};
258260
BGOpts.ContextProvider = Opts.ContextProvider;
261+
BGOpts.SupportContainedRefs = Opts.EnableOutgoingCalls;
259262
BackgroundIdx = std::make_unique<BackgroundIndex>(
260263
TFS, CDB,
261264
BackgroundIndexStorage::createDiskBackedStorageFactory(
@@ -912,6 +915,15 @@ void ClangdServer::inlayHints(PathRef File, std::optional<Range> RestrictRange,
912915
WorkScheduler->runWithAST("InlayHints", File, std::move(Action), Transient);
913916
}
914917

918+
void ClangdServer::outgoingCalls(
919+
const CallHierarchyItem &Item,
920+
Callback<std::vector<CallHierarchyOutgoingCall>> CB) {
921+
WorkScheduler->run("Outgoing Calls", "",
922+
[CB = std::move(CB), Item, this]() mutable {
923+
CB(clangd::outgoingCalls(Item, Index));
924+
});
925+
}
926+
915927
void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
916928
// FIXME: Do nothing for now. This will be used for indexing and potentially
917929
// invalidating other caches.

Diff for: clang-tools-extra/clangd/ClangdServer.h

+9
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ class ClangdServer {
110110
/// Cached preambles are potentially large. If false, store them on disk.
111111
bool StorePreamblesInMemory = true;
112112

113+
/// Call hierarchy's outgoing calls feature requires additional index
114+
/// serving structures which increase memory usage. If false, these are
115+
/// not created and the feature is not enabled.
116+
bool EnableOutgoingCalls = true;
117+
113118
/// This throttler controls which preambles may be built at a given time.
114119
clangd::PreambleThrottler *PreambleThrottler = nullptr;
115120

@@ -292,6 +297,10 @@ class ClangdServer {
292297
void incomingCalls(const CallHierarchyItem &Item,
293298
Callback<std::vector<CallHierarchyIncomingCall>>);
294299

300+
/// Resolve outgoing calls for a given call hierarchy item.
301+
void outgoingCalls(const CallHierarchyItem &Item,
302+
Callback<std::vector<CallHierarchyOutgoingCall>>);
303+
295304
/// Resolve inlay hints for a given document.
296305
void inlayHints(PathRef File, std::optional<Range> RestrictRange,
297306
Callback<std::vector<InlayHint>>);

Diff for: clang-tools-extra/clangd/XRefs.cpp

+61
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ declToHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
16881688

16891689
HierarchyItem HI;
16901690
HI.name = printName(Ctx, ND);
1691+
// FIXME: Populate HI.detail the way we do in symbolToHierarchyItem?
16911692
HI.kind = SK;
16921693
HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()),
16931694
sourceLocToPosition(SM, DeclRange->getEnd())};
@@ -1739,6 +1740,7 @@ static std::optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
17391740
}
17401741
HierarchyItem HI;
17411742
HI.name = std::string(S.Name);
1743+
HI.detail = (S.Scope + S.Name).str();
17421744
HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind);
17431745
HI.selectionRange = Loc->range;
17441746
// FIXME: Populate 'range' correctly
@@ -2305,6 +2307,65 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
23052307
return Results;
23062308
}
23072309

2310+
std::vector<CallHierarchyOutgoingCall>
2311+
outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
2312+
std::vector<CallHierarchyOutgoingCall> Results;
2313+
if (!Index || Item.data.empty())
2314+
return Results;
2315+
auto ID = SymbolID::fromStr(Item.data);
2316+
if (!ID) {
2317+
elog("outgoingCalls failed to find symbol: {0}", ID.takeError());
2318+
return Results;
2319+
}
2320+
// In this function, we find outgoing calls based on the index only.
2321+
ContainedRefsRequest Request;
2322+
Request.ID = *ID;
2323+
// Initially store the ranges in a map keyed by SymbolID of the callee.
2324+
// This allows us to group different calls to the same function
2325+
// into the same CallHierarchyOutgoingCall.
2326+
llvm::DenseMap<SymbolID, std::vector<Range>> CallsOut;
2327+
// We can populate the ranges based on a refs request only. As we do so, we
2328+
// also accumulate the callee IDs into a lookup request.
2329+
LookupRequest CallsOutLookup;
2330+
Index->containedRefs(Request, [&](const auto &R) {
2331+
auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
2332+
if (!Loc) {
2333+
elog("outgoingCalls failed to convert location: {0}", Loc.takeError());
2334+
return;
2335+
}
2336+
auto It = CallsOut.try_emplace(R.Symbol, std::vector<Range>{}).first;
2337+
It->second.push_back(Loc->range);
2338+
2339+
CallsOutLookup.IDs.insert(R.Symbol);
2340+
});
2341+
// Perform the lookup request and combine its results with CallsOut to
2342+
// get complete CallHierarchyOutgoingCall objects.
2343+
Index->lookup(CallsOutLookup, [&](const Symbol &Callee) {
2344+
// The containedRefs request should only return symbols which are
2345+
// function-like, i.e. symbols for which references to them can be "calls".
2346+
using SK = index::SymbolKind;
2347+
auto Kind = Callee.SymInfo.Kind;
2348+
assert(Kind == SK::Function || Kind == SK::InstanceMethod ||
2349+
Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
2350+
Kind == SK::Constructor || Kind == SK::Destructor ||
2351+
Kind == SK::ConversionFunction);
2352+
(void)Kind;
2353+
(void)SK::Function;
2354+
2355+
auto It = CallsOut.find(Callee.ID);
2356+
assert(It != CallsOut.end());
2357+
if (auto CHI = symbolToCallHierarchyItem(Callee, Item.uri.file()))
2358+
Results.push_back(
2359+
CallHierarchyOutgoingCall{std::move(*CHI), std::move(It->second)});
2360+
});
2361+
// Sort results by name of the callee.
2362+
llvm::sort(Results, [](const CallHierarchyOutgoingCall &A,
2363+
const CallHierarchyOutgoingCall &B) {
2364+
return A.to.name < B.to.name;
2365+
});
2366+
return Results;
2367+
}
2368+
23082369
llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
23092370
const FunctionDecl *FD) {
23102371
if (!FD->hasBody())

Diff for: clang-tools-extra/clangd/XRefs.h

+3
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath);
150150
std::vector<CallHierarchyIncomingCall>
151151
incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index);
152152

153+
std::vector<CallHierarchyOutgoingCall>
154+
outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index);
155+
153156
/// Returns all decls that are referenced in the \p FD except local symbols.
154157
llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
155158
const FunctionDecl *FD);

Diff for: clang-tools-extra/clangd/benchmarks/IndexBenchmark.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ namespace {
2424

2525
std::unique_ptr<SymbolIndex> buildMem() {
2626
return loadIndex(IndexFilename, clang::clangd::SymbolOrigin::Static,
27-
/*UseDex=*/false);
27+
/*UseDex=*/false, /*SupportContainedRefs=*/true);
2828
}
2929

3030
std::unique_ptr<SymbolIndex> buildDex() {
3131
return loadIndex(IndexFilename, clang::clangd::SymbolOrigin::Static,
32-
/*UseDex=*/true);
32+
/*UseDex=*/true, /*SupportContainedRefs=*/true);
3333
}
3434

3535
// Reads JSON array of serialized FuzzyFindRequest's from user-provided file.

Diff for: clang-tools-extra/clangd/index/Background.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ BackgroundIndex::BackgroundIndex(
9696
: SwapIndex(std::make_unique<MemIndex>()), TFS(TFS), CDB(CDB),
9797
IndexingPriority(Opts.IndexingPriority),
9898
ContextProvider(std::move(Opts.ContextProvider)),
99-
IndexedSymbols(IndexContents::All),
99+
IndexedSymbols(IndexContents::All, Opts.SupportContainedRefs),
100100
Rebuilder(this, &IndexedSymbols, Opts.ThreadPoolSize),
101101
IndexStorageFactory(std::move(IndexStorageFactory)),
102102
Queue(std::move(Opts.OnProgress)),

Diff for: clang-tools-extra/clangd/index/Background.h

+3
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ class BackgroundIndex : public SwapIndex {
145145
// file. Called with the empty string for other tasks.
146146
// (When called, the context from BackgroundIndex construction is active).
147147
std::function<Context(PathRef)> ContextProvider = nullptr;
148+
// Whether the index needs to support the containedRefs() operation.
149+
// May use extra memory.
150+
bool SupportContainedRefs = true;
148151
};
149152

150153
/// Creates a new background index and starts its threads.

Diff for: clang-tools-extra/clangd/index/FileIndex.cpp

+7-6
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST,
239239
/*CollectMainFileRefs=*/false);
240240
}
241241

242-
FileSymbols::FileSymbols(IndexContents IdxContents)
243-
: IdxContents(IdxContents) {}
242+
FileSymbols::FileSymbols(IndexContents IdxContents, bool SupportContainedRefs)
243+
: IdxContents(IdxContents), SupportContainedRefs(SupportContainedRefs) {}
244244

245245
void FileSymbols::update(llvm::StringRef Key,
246246
std::unique_ptr<SymbolSlab> Symbols,
@@ -395,7 +395,7 @@ FileSymbols::buildIndex(IndexType Type, DuplicateHandling DuplicateHandle,
395395
std::move(AllRelations), std::move(Files), IdxContents,
396396
std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs),
397397
std::move(RefsStorage), std::move(SymsStorage)),
398-
StorageSize);
398+
StorageSize, SupportContainedRefs);
399399
}
400400
llvm_unreachable("Unknown clangd::IndexType");
401401
}
@@ -419,11 +419,12 @@ void FileSymbols::profile(MemoryTree &MT) const {
419419
}
420420
}
421421

422-
FileIndex::FileIndex()
422+
FileIndex::FileIndex(bool SupportContainedRefs)
423423
: MergedIndex(&MainFileIndex, &PreambleIndex),
424-
PreambleSymbols(IndexContents::Symbols | IndexContents::Relations),
424+
PreambleSymbols(IndexContents::Symbols | IndexContents::Relations,
425+
SupportContainedRefs),
425426
PreambleIndex(std::make_unique<MemIndex>()),
426-
MainFileSymbols(IndexContents::All),
427+
MainFileSymbols(IndexContents::All, SupportContainedRefs),
427428
MainFileIndex(std::make_unique<MemIndex>()) {}
428429

429430
void FileIndex::updatePreamble(IndexFileIn IF) {

Diff for: clang-tools-extra/clangd/index/FileIndex.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ enum class DuplicateHandling {
6969
/// locking when we swap or obtain references to snapshots.
7070
class FileSymbols {
7171
public:
72-
FileSymbols(IndexContents IdxContents);
72+
FileSymbols(IndexContents IdxContents, bool SupportContainedRefs);
7373
/// Updates all slabs associated with the \p Key.
7474
/// If either is nullptr, corresponding data for \p Key will be removed.
7575
/// If CountReferences is true, \p Refs will be used for counting references
@@ -91,6 +91,7 @@ class FileSymbols {
9191

9292
private:
9393
IndexContents IdxContents;
94+
bool SupportContainedRefs;
9495

9596
struct RefSlabAndCountReferences {
9697
std::shared_ptr<RefSlab> Slab;
@@ -108,7 +109,7 @@ class FileSymbols {
108109
/// FIXME: Expose an interface to remove files that are closed.
109110
class FileIndex : public MergedIndex {
110111
public:
111-
FileIndex();
112+
FileIndex(bool SupportContainedRefs);
112113

113114
/// Update preamble symbols of file \p Path with all declarations in \p AST
114115
/// and macros in \p PP.

Diff for: clang-tools-extra/clangd/index/Index.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ bool SwapIndex::refs(const RefsRequest &R,
6666
llvm::function_ref<void(const Ref &)> CB) const {
6767
return snapshot()->refs(R, CB);
6868
}
69+
bool SwapIndex::containedRefs(
70+
const ContainedRefsRequest &R,
71+
llvm::function_ref<void(const ContainedRefsResult &)> CB) const {
72+
return snapshot()->containedRefs(R, CB);
73+
}
6974
void SwapIndex::relations(
7075
const RelationsRequest &R,
7176
llvm::function_ref<void(const SymbolID &, const Symbol &)> CB) const {

Diff for: clang-tools-extra/clangd/index/Index.h

+35
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,34 @@ struct RefsRequest {
7777
bool WantContainer = false;
7878
};
7979

80+
struct ContainedRefsRequest {
81+
/// Note that RefKind::Call just restricts the matched SymbolKind to
82+
/// functions, not the form of the reference (e.g. address-of-function,
83+
/// which can indicate an indirect call, should still be caught).
84+
static const RefKind SupportedRefKinds = RefKind::Call;
85+
86+
SymbolID ID;
87+
/// If set, limit the number of refers returned from the index. The index may
88+
/// choose to return less than this, e.g. it tries to avoid returning stale
89+
/// results.
90+
std::optional<uint32_t> Limit;
91+
};
92+
8093
struct RelationsRequest {
8194
llvm::DenseSet<SymbolID> Subjects;
8295
RelationKind Predicate;
8396
/// If set, limit the number of relations returned from the index.
8497
std::optional<uint32_t> Limit;
8598
};
8699

100+
struct ContainedRefsResult {
101+
/// The source location where the symbol is named.
102+
SymbolLocation Location;
103+
RefKind Kind = RefKind::Unknown;
104+
/// The ID of the symbol which is referred to
105+
SymbolID Symbol;
106+
};
107+
87108
/// Describes what data is covered by an index.
88109
///
89110
/// Indexes may contain symbols but not references from a file, etc.
@@ -141,6 +162,17 @@ class SymbolIndex {
141162
virtual bool refs(const RefsRequest &Req,
142163
llvm::function_ref<void(const Ref &)> Callback) const = 0;
143164

165+
/// Find all symbols that are referenced by a symbol and apply
166+
/// \p Callback on each result.
167+
///
168+
/// Results should be returned in arbitrary order.
169+
/// The returned result must be deep-copied if it's used outside Callback.
170+
///
171+
/// Returns true if there will be more results (limited by Req.Limit);
172+
virtual bool containedRefs(
173+
const ContainedRefsRequest &Req,
174+
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const = 0;
175+
144176
/// Finds all relations (S, P, O) stored in the index such that S is among
145177
/// Req.Subjects and P is Req.Predicate, and invokes \p Callback for (S, O) in
146178
/// each.
@@ -175,6 +207,9 @@ class SwapIndex : public SymbolIndex {
175207
llvm::function_ref<void(const Symbol &)>) const override;
176208
bool refs(const RefsRequest &,
177209
llvm::function_ref<void(const Ref &)>) const override;
210+
bool containedRefs(
211+
const ContainedRefsRequest &,
212+
llvm::function_ref<void(const ContainedRefsResult &)>) const override;
178213
void relations(const RelationsRequest &,
179214
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
180215
const override;

Diff for: clang-tools-extra/clangd/index/MemIndex.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "MemIndex.h"
1010
#include "FuzzyMatch.h"
1111
#include "Quality.h"
12+
#include "index/Index.h"
1213
#include "support/Trace.h"
1314

1415
namespace clang {
@@ -85,6 +86,25 @@ bool MemIndex::refs(const RefsRequest &Req,
8586
return false; // We reported all refs.
8687
}
8788

89+
bool MemIndex::containedRefs(
90+
const ContainedRefsRequest &Req,
91+
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const {
92+
trace::Span Tracer("MemIndex refersTo");
93+
uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
94+
for (const auto &Pair : Refs) {
95+
for (const auto &R : Pair.second) {
96+
if (!static_cast<int>(ContainedRefsRequest::SupportedRefKinds & R.Kind) ||
97+
Req.ID != R.Container)
98+
continue;
99+
if (Remaining == 0)
100+
return true; // More refs were available.
101+
--Remaining;
102+
Callback({R.Location, R.Kind, Pair.first});
103+
}
104+
}
105+
return false; // We reported all refs.
106+
}
107+
88108
void MemIndex::relations(
89109
const RelationsRequest &Req,
90110
llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {

Diff for: clang-tools-extra/clangd/index/MemIndex.h

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ class MemIndex : public SymbolIndex {
7272
bool refs(const RefsRequest &Req,
7373
llvm::function_ref<void(const Ref &)> Callback) const override;
7474

75+
bool containedRefs(const ContainedRefsRequest &Req,
76+
llvm::function_ref<void(const ContainedRefsResult &)>
77+
Callback) const override;
78+
7579
void relations(const RelationsRequest &Req,
7680
llvm::function_ref<void(const SymbolID &, const Symbol &)>
7781
Callback) const override;

0 commit comments

Comments
 (0)