mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-16 06:46:33 +00:00
[clangd] support outgoing calls in call hierarchy (#117673)
This reverts commit ce0f11325e0c62c5b81391589e9b93b412a85bc1.
This commit is contained in:
parent
5d38a3406b
commit
61fe67a401
@ -1415,6 +1415,12 @@ void ClangdLSPServer::onInlayHint(const InlayHintsParams &Params,
|
||||
std::move(Reply));
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onCallHierarchyOutgoingCalls(
|
||||
const CallHierarchyOutgoingCallsParams &Params,
|
||||
Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
|
||||
Server->outgoingCalls(Params.item, std::move(Reply));
|
||||
}
|
||||
|
||||
void ClangdLSPServer::applyConfiguration(
|
||||
const ConfigurationSettings &Settings) {
|
||||
// Per-file update to the compilation database.
|
||||
@ -1693,6 +1699,8 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind,
|
||||
Bind.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes);
|
||||
Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy);
|
||||
Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
|
||||
if (Opts.EnableOutgoingCalls)
|
||||
Bind.method("callHierarchy/outgoingCalls", this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
|
||||
Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange);
|
||||
Bind.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink);
|
||||
Bind.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens);
|
||||
|
@ -156,6 +156,9 @@ private:
|
||||
void onCallHierarchyIncomingCalls(
|
||||
const CallHierarchyIncomingCallsParams &,
|
||||
Callback<std::vector<CallHierarchyIncomingCall>>);
|
||||
void onCallHierarchyOutgoingCalls(
|
||||
const CallHierarchyOutgoingCallsParams &,
|
||||
Callback<std::vector<CallHierarchyOutgoingCall>>);
|
||||
void onClangdInlayHints(const InlayHintsParams &,
|
||||
Callback<llvm::json::Value>);
|
||||
void onInlayHint(const InlayHintsParams &, Callback<std::vector<InlayHint>>);
|
||||
|
@ -215,7 +215,9 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
|
||||
const ThreadsafeFS &TFS, const Options &Opts,
|
||||
Callbacks *Callbacks)
|
||||
: FeatureModules(Opts.FeatureModules), CDB(CDB), TFS(TFS),
|
||||
DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex() : nullptr),
|
||||
DynamicIdx(Opts.BuildDynamicSymbolIndex
|
||||
? new FileIndex(Opts.EnableOutgoingCalls)
|
||||
: nullptr),
|
||||
ModulesManager(Opts.ModulesManager),
|
||||
ClangTidyProvider(Opts.ClangTidyProvider),
|
||||
UseDirtyHeaders(Opts.UseDirtyHeaders),
|
||||
@ -256,6 +258,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
|
||||
Callbacks->onBackgroundIndexProgress(S);
|
||||
};
|
||||
BGOpts.ContextProvider = Opts.ContextProvider;
|
||||
BGOpts.SupportContainedRefs = Opts.EnableOutgoingCalls;
|
||||
BackgroundIdx = std::make_unique<BackgroundIndex>(
|
||||
TFS, CDB,
|
||||
BackgroundIndexStorage::createDiskBackedStorageFactory(
|
||||
@ -912,6 +915,15 @@ void ClangdServer::inlayHints(PathRef File, std::optional<Range> RestrictRange,
|
||||
WorkScheduler->runWithAST("InlayHints", File, std::move(Action), Transient);
|
||||
}
|
||||
|
||||
void ClangdServer::outgoingCalls(
|
||||
const CallHierarchyItem &Item,
|
||||
Callback<std::vector<CallHierarchyOutgoingCall>> CB) {
|
||||
WorkScheduler->run("Outgoing Calls", "",
|
||||
[CB = std::move(CB), Item, this]() mutable {
|
||||
CB(clangd::outgoingCalls(Item, Index));
|
||||
});
|
||||
}
|
||||
|
||||
void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
|
||||
// FIXME: Do nothing for now. This will be used for indexing and potentially
|
||||
// invalidating other caches.
|
||||
|
@ -110,6 +110,11 @@ public:
|
||||
/// Cached preambles are potentially large. If false, store them on disk.
|
||||
bool StorePreamblesInMemory = true;
|
||||
|
||||
/// Call hierarchy's outgoing calls feature requires additional index
|
||||
/// serving structures which increase memory usage. If false, these are
|
||||
/// not created and the feature is not enabled.
|
||||
bool EnableOutgoingCalls = true;
|
||||
|
||||
/// This throttler controls which preambles may be built at a given time.
|
||||
clangd::PreambleThrottler *PreambleThrottler = nullptr;
|
||||
|
||||
@ -292,6 +297,10 @@ public:
|
||||
void incomingCalls(const CallHierarchyItem &Item,
|
||||
Callback<std::vector<CallHierarchyIncomingCall>>);
|
||||
|
||||
/// Resolve outgoing calls for a given call hierarchy item.
|
||||
void outgoingCalls(const CallHierarchyItem &Item,
|
||||
Callback<std::vector<CallHierarchyOutgoingCall>>);
|
||||
|
||||
/// Resolve inlay hints for a given document.
|
||||
void inlayHints(PathRef File, std::optional<Range> RestrictRange,
|
||||
Callback<std::vector<InlayHint>>);
|
||||
|
@ -1688,6 +1688,7 @@ declToHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
|
||||
|
||||
HierarchyItem HI;
|
||||
HI.name = printName(Ctx, ND);
|
||||
// FIXME: Populate HI.detail the way we do in symbolToHierarchyItem?
|
||||
HI.kind = SK;
|
||||
HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()),
|
||||
sourceLocToPosition(SM, DeclRange->getEnd())};
|
||||
@ -1739,6 +1740,7 @@ static std::optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
|
||||
}
|
||||
HierarchyItem HI;
|
||||
HI.name = std::string(S.Name);
|
||||
HI.detail = (S.Scope + S.Name).str();
|
||||
HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind);
|
||||
HI.selectionRange = Loc->range;
|
||||
// FIXME: Populate 'range' correctly
|
||||
@ -2305,6 +2307,65 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
|
||||
return Results;
|
||||
}
|
||||
|
||||
std::vector<CallHierarchyOutgoingCall>
|
||||
outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
|
||||
std::vector<CallHierarchyOutgoingCall> Results;
|
||||
if (!Index || Item.data.empty())
|
||||
return Results;
|
||||
auto ID = SymbolID::fromStr(Item.data);
|
||||
if (!ID) {
|
||||
elog("outgoingCalls failed to find symbol: {0}", ID.takeError());
|
||||
return Results;
|
||||
}
|
||||
// In this function, we find outgoing calls based on the index only.
|
||||
ContainedRefsRequest Request;
|
||||
Request.ID = *ID;
|
||||
// Initially store the ranges in a map keyed by SymbolID of the callee.
|
||||
// This allows us to group different calls to the same function
|
||||
// into the same CallHierarchyOutgoingCall.
|
||||
llvm::DenseMap<SymbolID, std::vector<Range>> CallsOut;
|
||||
// We can populate the ranges based on a refs request only. As we do so, we
|
||||
// also accumulate the callee IDs into a lookup request.
|
||||
LookupRequest CallsOutLookup;
|
||||
Index->containedRefs(Request, [&](const auto &R) {
|
||||
auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
|
||||
if (!Loc) {
|
||||
elog("outgoingCalls failed to convert location: {0}", Loc.takeError());
|
||||
return;
|
||||
}
|
||||
auto It = CallsOut.try_emplace(R.Symbol, std::vector<Range>{}).first;
|
||||
It->second.push_back(Loc->range);
|
||||
|
||||
CallsOutLookup.IDs.insert(R.Symbol);
|
||||
});
|
||||
// Perform the lookup request and combine its results with CallsOut to
|
||||
// get complete CallHierarchyOutgoingCall objects.
|
||||
Index->lookup(CallsOutLookup, [&](const Symbol &Callee) {
|
||||
// The containedRefs request should only return symbols which are
|
||||
// function-like, i.e. symbols for which references to them can be "calls".
|
||||
using SK = index::SymbolKind;
|
||||
auto Kind = Callee.SymInfo.Kind;
|
||||
assert(Kind == SK::Function || Kind == SK::InstanceMethod ||
|
||||
Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
|
||||
Kind == SK::Constructor || Kind == SK::Destructor ||
|
||||
Kind == SK::ConversionFunction);
|
||||
(void)Kind;
|
||||
(void)SK::Function;
|
||||
|
||||
auto It = CallsOut.find(Callee.ID);
|
||||
assert(It != CallsOut.end());
|
||||
if (auto CHI = symbolToCallHierarchyItem(Callee, Item.uri.file()))
|
||||
Results.push_back(
|
||||
CallHierarchyOutgoingCall{std::move(*CHI), std::move(It->second)});
|
||||
});
|
||||
// Sort results by name of the callee.
|
||||
llvm::sort(Results, [](const CallHierarchyOutgoingCall &A,
|
||||
const CallHierarchyOutgoingCall &B) {
|
||||
return A.to.name < B.to.name;
|
||||
});
|
||||
return Results;
|
||||
}
|
||||
|
||||
llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
|
||||
const FunctionDecl *FD) {
|
||||
if (!FD->hasBody())
|
||||
|
@ -150,6 +150,9 @@ prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath);
|
||||
std::vector<CallHierarchyIncomingCall>
|
||||
incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index);
|
||||
|
||||
std::vector<CallHierarchyOutgoingCall>
|
||||
outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index);
|
||||
|
||||
/// Returns all decls that are referenced in the \p FD except local symbols.
|
||||
llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
|
||||
const FunctionDecl *FD);
|
||||
|
@ -24,12 +24,12 @@ namespace {
|
||||
|
||||
std::unique_ptr<SymbolIndex> buildMem() {
|
||||
return loadIndex(IndexFilename, clang::clangd::SymbolOrigin::Static,
|
||||
/*UseDex=*/false);
|
||||
/*UseDex=*/false, /*SupportContainedRefs=*/true);
|
||||
}
|
||||
|
||||
std::unique_ptr<SymbolIndex> buildDex() {
|
||||
return loadIndex(IndexFilename, clang::clangd::SymbolOrigin::Static,
|
||||
/*UseDex=*/true);
|
||||
/*UseDex=*/true, /*SupportContainedRefs=*/true);
|
||||
}
|
||||
|
||||
// Reads JSON array of serialized FuzzyFindRequest's from user-provided file.
|
||||
|
@ -96,7 +96,7 @@ BackgroundIndex::BackgroundIndex(
|
||||
: SwapIndex(std::make_unique<MemIndex>()), TFS(TFS), CDB(CDB),
|
||||
IndexingPriority(Opts.IndexingPriority),
|
||||
ContextProvider(std::move(Opts.ContextProvider)),
|
||||
IndexedSymbols(IndexContents::All),
|
||||
IndexedSymbols(IndexContents::All, Opts.SupportContainedRefs),
|
||||
Rebuilder(this, &IndexedSymbols, Opts.ThreadPoolSize),
|
||||
IndexStorageFactory(std::move(IndexStorageFactory)),
|
||||
Queue(std::move(Opts.OnProgress)),
|
||||
|
@ -145,6 +145,9 @@ public:
|
||||
// file. Called with the empty string for other tasks.
|
||||
// (When called, the context from BackgroundIndex construction is active).
|
||||
std::function<Context(PathRef)> ContextProvider = nullptr;
|
||||
// Whether the index needs to support the containedRefs() operation.
|
||||
// May use extra memory.
|
||||
bool SupportContainedRefs = true;
|
||||
};
|
||||
|
||||
/// Creates a new background index and starts its threads.
|
||||
|
@ -239,8 +239,8 @@ SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST,
|
||||
/*CollectMainFileRefs=*/false);
|
||||
}
|
||||
|
||||
FileSymbols::FileSymbols(IndexContents IdxContents)
|
||||
: IdxContents(IdxContents) {}
|
||||
FileSymbols::FileSymbols(IndexContents IdxContents, bool SupportContainedRefs)
|
||||
: IdxContents(IdxContents), SupportContainedRefs(SupportContainedRefs) {}
|
||||
|
||||
void FileSymbols::update(llvm::StringRef Key,
|
||||
std::unique_ptr<SymbolSlab> Symbols,
|
||||
@ -395,7 +395,7 @@ FileSymbols::buildIndex(IndexType Type, DuplicateHandling DuplicateHandle,
|
||||
std::move(AllRelations), std::move(Files), IdxContents,
|
||||
std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs),
|
||||
std::move(RefsStorage), std::move(SymsStorage)),
|
||||
StorageSize);
|
||||
StorageSize, SupportContainedRefs);
|
||||
}
|
||||
llvm_unreachable("Unknown clangd::IndexType");
|
||||
}
|
||||
@ -419,11 +419,12 @@ void FileSymbols::profile(MemoryTree &MT) const {
|
||||
}
|
||||
}
|
||||
|
||||
FileIndex::FileIndex()
|
||||
FileIndex::FileIndex(bool SupportContainedRefs)
|
||||
: MergedIndex(&MainFileIndex, &PreambleIndex),
|
||||
PreambleSymbols(IndexContents::Symbols | IndexContents::Relations),
|
||||
PreambleSymbols(IndexContents::Symbols | IndexContents::Relations,
|
||||
SupportContainedRefs),
|
||||
PreambleIndex(std::make_unique<MemIndex>()),
|
||||
MainFileSymbols(IndexContents::All),
|
||||
MainFileSymbols(IndexContents::All, SupportContainedRefs),
|
||||
MainFileIndex(std::make_unique<MemIndex>()) {}
|
||||
|
||||
void FileIndex::updatePreamble(IndexFileIn IF) {
|
||||
|
@ -69,7 +69,7 @@ enum class DuplicateHandling {
|
||||
/// locking when we swap or obtain references to snapshots.
|
||||
class FileSymbols {
|
||||
public:
|
||||
FileSymbols(IndexContents IdxContents);
|
||||
FileSymbols(IndexContents IdxContents, bool SupportContainedRefs);
|
||||
/// Updates all slabs associated with the \p Key.
|
||||
/// If either is nullptr, corresponding data for \p Key will be removed.
|
||||
/// If CountReferences is true, \p Refs will be used for counting references
|
||||
@ -91,6 +91,7 @@ public:
|
||||
|
||||
private:
|
||||
IndexContents IdxContents;
|
||||
bool SupportContainedRefs;
|
||||
|
||||
struct RefSlabAndCountReferences {
|
||||
std::shared_ptr<RefSlab> Slab;
|
||||
@ -108,7 +109,7 @@ private:
|
||||
/// FIXME: Expose an interface to remove files that are closed.
|
||||
class FileIndex : public MergedIndex {
|
||||
public:
|
||||
FileIndex();
|
||||
FileIndex(bool SupportContainedRefs);
|
||||
|
||||
/// Update preamble symbols of file \p Path with all declarations in \p AST
|
||||
/// and macros in \p PP.
|
||||
|
@ -66,6 +66,11 @@ bool SwapIndex::refs(const RefsRequest &R,
|
||||
llvm::function_ref<void(const Ref &)> CB) const {
|
||||
return snapshot()->refs(R, CB);
|
||||
}
|
||||
bool SwapIndex::containedRefs(
|
||||
const ContainedRefsRequest &R,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)> CB) const {
|
||||
return snapshot()->containedRefs(R, CB);
|
||||
}
|
||||
void SwapIndex::relations(
|
||||
const RelationsRequest &R,
|
||||
llvm::function_ref<void(const SymbolID &, const Symbol &)> CB) const {
|
||||
|
@ -77,6 +77,19 @@ struct RefsRequest {
|
||||
bool WantContainer = false;
|
||||
};
|
||||
|
||||
struct ContainedRefsRequest {
|
||||
/// Note that RefKind::Call just restricts the matched SymbolKind to
|
||||
/// functions, not the form of the reference (e.g. address-of-function,
|
||||
/// which can indicate an indirect call, should still be caught).
|
||||
static const RefKind SupportedRefKinds = RefKind::Call;
|
||||
|
||||
SymbolID ID;
|
||||
/// If set, limit the number of refers returned from the index. The index may
|
||||
/// choose to return less than this, e.g. it tries to avoid returning stale
|
||||
/// results.
|
||||
std::optional<uint32_t> Limit;
|
||||
};
|
||||
|
||||
struct RelationsRequest {
|
||||
llvm::DenseSet<SymbolID> Subjects;
|
||||
RelationKind Predicate;
|
||||
@ -84,6 +97,14 @@ struct RelationsRequest {
|
||||
std::optional<uint32_t> Limit;
|
||||
};
|
||||
|
||||
struct ContainedRefsResult {
|
||||
/// The source location where the symbol is named.
|
||||
SymbolLocation Location;
|
||||
RefKind Kind = RefKind::Unknown;
|
||||
/// The ID of the symbol which is referred to
|
||||
SymbolID Symbol;
|
||||
};
|
||||
|
||||
/// Describes what data is covered by an index.
|
||||
///
|
||||
/// Indexes may contain symbols but not references from a file, etc.
|
||||
@ -141,6 +162,17 @@ public:
|
||||
virtual bool refs(const RefsRequest &Req,
|
||||
llvm::function_ref<void(const Ref &)> Callback) const = 0;
|
||||
|
||||
/// Find all symbols that are referenced by a symbol and apply
|
||||
/// \p Callback on each result.
|
||||
///
|
||||
/// Results should be returned in arbitrary order.
|
||||
/// The returned result must be deep-copied if it's used outside Callback.
|
||||
///
|
||||
/// Returns true if there will be more results (limited by Req.Limit);
|
||||
virtual bool containedRefs(
|
||||
const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const = 0;
|
||||
|
||||
/// Finds all relations (S, P, O) stored in the index such that S is among
|
||||
/// Req.Subjects and P is Req.Predicate, and invokes \p Callback for (S, O) in
|
||||
/// each.
|
||||
@ -175,6 +207,9 @@ public:
|
||||
llvm::function_ref<void(const Symbol &)>) const override;
|
||||
bool refs(const RefsRequest &,
|
||||
llvm::function_ref<void(const Ref &)>) const override;
|
||||
bool containedRefs(
|
||||
const ContainedRefsRequest &,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>) const override;
|
||||
void relations(const RelationsRequest &,
|
||||
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
|
||||
const override;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "MemIndex.h"
|
||||
#include "FuzzyMatch.h"
|
||||
#include "Quality.h"
|
||||
#include "index/Index.h"
|
||||
#include "support/Trace.h"
|
||||
|
||||
namespace clang {
|
||||
@ -85,6 +86,25 @@ bool MemIndex::refs(const RefsRequest &Req,
|
||||
return false; // We reported all refs.
|
||||
}
|
||||
|
||||
bool MemIndex::containedRefs(
|
||||
const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const {
|
||||
trace::Span Tracer("MemIndex refersTo");
|
||||
uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
|
||||
for (const auto &Pair : Refs) {
|
||||
for (const auto &R : Pair.second) {
|
||||
if (!static_cast<int>(ContainedRefsRequest::SupportedRefKinds & R.Kind) ||
|
||||
Req.ID != R.Container)
|
||||
continue;
|
||||
if (Remaining == 0)
|
||||
return true; // More refs were available.
|
||||
--Remaining;
|
||||
Callback({R.Location, R.Kind, Pair.first});
|
||||
}
|
||||
}
|
||||
return false; // We reported all refs.
|
||||
}
|
||||
|
||||
void MemIndex::relations(
|
||||
const RelationsRequest &Req,
|
||||
llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
|
||||
|
@ -72,6 +72,10 @@ public:
|
||||
bool refs(const RefsRequest &Req,
|
||||
llvm::function_ref<void(const Ref &)> Callback) const override;
|
||||
|
||||
bool containedRefs(const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>
|
||||
Callback) const override;
|
||||
|
||||
void relations(const RelationsRequest &Req,
|
||||
llvm::function_ref<void(const SymbolID &, const Symbol &)>
|
||||
Callback) const override;
|
||||
|
@ -155,6 +155,40 @@ bool MergedIndex::refs(const RefsRequest &Req,
|
||||
return More || StaticHadMore;
|
||||
}
|
||||
|
||||
bool MergedIndex::containedRefs(
|
||||
const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const {
|
||||
trace::Span Tracer("MergedIndex refersTo");
|
||||
bool More = false;
|
||||
uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
|
||||
// We don't want duplicated refs from the static/dynamic indexes,
|
||||
// and we can't reliably deduplicate them because offsets may differ slightly.
|
||||
// We consider the dynamic index authoritative and report all its refs,
|
||||
// and only report static index refs from other files.
|
||||
More |= Dynamic->containedRefs(Req, [&](const auto &O) {
|
||||
Callback(O);
|
||||
assert(Remaining != 0);
|
||||
--Remaining;
|
||||
});
|
||||
if (Remaining == 0 && More)
|
||||
return More;
|
||||
auto DynamicContainsFile = Dynamic->indexedFiles();
|
||||
// We return less than Req.Limit if static index returns more refs for dirty
|
||||
// files.
|
||||
bool StaticHadMore = Static->containedRefs(Req, [&](const auto &O) {
|
||||
if ((DynamicContainsFile(O.Location.FileURI) & IndexContents::References) !=
|
||||
IndexContents::None)
|
||||
return; // ignore refs that have been seen from dynamic index.
|
||||
if (Remaining == 0) {
|
||||
More = true;
|
||||
return;
|
||||
}
|
||||
--Remaining;
|
||||
Callback(O);
|
||||
});
|
||||
return More || StaticHadMore;
|
||||
}
|
||||
|
||||
llvm::unique_function<IndexContents(llvm::StringRef) const>
|
||||
MergedIndex::indexedFiles() const {
|
||||
return [DynamicContainsFile{Dynamic->indexedFiles()},
|
||||
|
@ -38,6 +38,9 @@ public:
|
||||
llvm::function_ref<void(const Symbol &)>) const override;
|
||||
bool refs(const RefsRequest &,
|
||||
llvm::function_ref<void(const Ref &)>) const override;
|
||||
bool containedRefs(
|
||||
const ContainedRefsRequest &,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>) const override;
|
||||
void relations(const RelationsRequest &,
|
||||
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
|
||||
const override;
|
||||
|
@ -35,6 +35,10 @@ public:
|
||||
/// Query all indexes while prioritizing the associated one (if any).
|
||||
bool refs(const RefsRequest &Req,
|
||||
llvm::function_ref<void(const Ref &)> Callback) const override;
|
||||
/// Query all indexes while prioritizing the associated one (if any).
|
||||
bool containedRefs(const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>
|
||||
Callback) const override;
|
||||
|
||||
/// Queries only the associates index when Req.RestrictForCodeCompletion is
|
||||
/// set, otherwise queries all.
|
||||
@ -94,6 +98,15 @@ bool ProjectAwareIndex::refs(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProjectAwareIndex::containedRefs(
|
||||
const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const {
|
||||
trace::Span Tracer("ProjectAwareIndex::refersTo");
|
||||
if (auto *Idx = getIndex())
|
||||
return Idx->containedRefs(Req, Callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProjectAwareIndex::fuzzyFind(
|
||||
const FuzzyFindRequest &Req,
|
||||
llvm::function_ref<void(const Symbol &)> Callback) const {
|
||||
|
@ -63,6 +63,9 @@ enum class RefKind : uint8_t {
|
||||
// ^ this references Foo, but does not explicitly spell out its name
|
||||
// };
|
||||
Spelled = 1 << 3,
|
||||
// A reference which is a call. Used as a filter for which references
|
||||
// to store in data structures used for computing outgoing calls.
|
||||
Call = 1 << 4,
|
||||
All = Declaration | Definition | Reference | Spelled,
|
||||
};
|
||||
|
||||
|
@ -457,7 +457,7 @@ readCompileCommand(Reader CmdReader, llvm::ArrayRef<llvm::StringRef> Strings) {
|
||||
// The current versioning scheme is simple - non-current versions are rejected.
|
||||
// If you make a breaking change, bump this version number to invalidate stored
|
||||
// data. Later we may want to support some backward compatibility.
|
||||
constexpr static uint32_t Version = 19;
|
||||
constexpr static uint32_t Version = 20;
|
||||
|
||||
llvm::Expected<IndexFileIn> readRIFF(llvm::StringRef Data,
|
||||
SymbolOrigin Origin) {
|
||||
@ -704,7 +704,8 @@ llvm::Expected<IndexFileIn> readIndexFile(llvm::StringRef Data,
|
||||
}
|
||||
|
||||
std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename,
|
||||
SymbolOrigin Origin, bool UseDex) {
|
||||
SymbolOrigin Origin, bool UseDex,
|
||||
bool SupportContainedRefs) {
|
||||
trace::Span OverallTracer("LoadIndex");
|
||||
auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename);
|
||||
if (!Buffer) {
|
||||
@ -735,10 +736,11 @@ std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename,
|
||||
size_t NumRelations = Relations.size();
|
||||
|
||||
trace::Span Tracer("BuildIndex");
|
||||
auto Index = UseDex ? dex::Dex::build(std::move(Symbols), std::move(Refs),
|
||||
std::move(Relations))
|
||||
: MemIndex::build(std::move(Symbols), std::move(Refs),
|
||||
std::move(Relations));
|
||||
auto Index = UseDex
|
||||
? dex::Dex::build(std::move(Symbols), std::move(Refs),
|
||||
std::move(Relations), SupportContainedRefs)
|
||||
: MemIndex::build(std::move(Symbols), std::move(Refs),
|
||||
std::move(Relations));
|
||||
vlog("Loaded {0} from {1} with estimated memory usage {2} bytes\n"
|
||||
" - number of symbols: {3}\n"
|
||||
" - number of refs: {4}\n"
|
||||
|
@ -83,7 +83,8 @@ std::string toYAML(const Ref &);
|
||||
// Build an in-memory static index from an index file.
|
||||
// The size should be relatively small, so data can be managed in memory.
|
||||
std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef Filename,
|
||||
SymbolOrigin Origin, bool UseDex = true);
|
||||
SymbolOrigin Origin, bool UseDex,
|
||||
bool SupportContainedRefs);
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "clang-include-cleaner/Types.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "index/Ref.h"
|
||||
#include "index/Relation.h"
|
||||
#include "index/Symbol.h"
|
||||
#include "index/SymbolID.h"
|
||||
@ -660,7 +661,7 @@ bool SymbolCollector::handleDeclOccurrence(
|
||||
auto FileLoc = SM.getFileLoc(Loc);
|
||||
auto FID = SM.getFileID(FileLoc);
|
||||
if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
|
||||
addRef(ID, SymbolRef{FileLoc, FID, Roles,
|
||||
addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
|
||||
getRefContainer(ASTNode.Parent, Opts),
|
||||
isSpelled(FileLoc, *ND)});
|
||||
}
|
||||
@ -774,8 +775,10 @@ bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name,
|
||||
// FIXME: Populate container information for macro references.
|
||||
// FIXME: All MacroRefs are marked as Spelled now, but this should be
|
||||
// checked.
|
||||
addRef(ID, SymbolRef{Loc, SM.getFileID(Loc), Roles, /*Container=*/nullptr,
|
||||
/*Spelled=*/true});
|
||||
addRef(ID,
|
||||
SymbolRef{Loc, SM.getFileID(Loc), Roles, index::SymbolKind::Macro,
|
||||
/*Container=*/nullptr,
|
||||
/*Spelled=*/true});
|
||||
}
|
||||
|
||||
// Collect symbols.
|
||||
@ -1166,6 +1169,14 @@ bool SymbolCollector::shouldIndexFile(FileID FID) {
|
||||
return I.first->second;
|
||||
}
|
||||
|
||||
static bool refIsCall(index::SymbolKind Kind) {
|
||||
using SK = index::SymbolKind;
|
||||
return Kind == SK::Function || Kind == SK::InstanceMethod ||
|
||||
Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
|
||||
Kind == SK::Constructor || Kind == SK::Destructor ||
|
||||
Kind == SK::ConversionFunction;
|
||||
}
|
||||
|
||||
void SymbolCollector::addRef(SymbolID ID, const SymbolRef &SR) {
|
||||
const auto &SM = ASTCtx->getSourceManager();
|
||||
// FIXME: use the result to filter out references.
|
||||
@ -1177,6 +1188,9 @@ void SymbolCollector::addRef(SymbolID ID, const SymbolRef &SR) {
|
||||
R.Location.End = Range.second;
|
||||
R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
|
||||
R.Kind = toRefKind(SR.Roles, SR.Spelled);
|
||||
if (refIsCall(SR.Kind)) {
|
||||
R.Kind |= RefKind::Call;
|
||||
}
|
||||
R.Container = getSymbolIDCached(SR.Container);
|
||||
Refs.insert(ID, R);
|
||||
}
|
||||
|
@ -209,6 +209,7 @@ private:
|
||||
SourceLocation Loc;
|
||||
FileID FID;
|
||||
index::SymbolRoleSet Roles;
|
||||
index::SymbolKind Kind;
|
||||
const Decl *Container;
|
||||
bool Spelled;
|
||||
};
|
||||
|
@ -33,13 +33,14 @@ namespace clangd {
|
||||
namespace dex {
|
||||
|
||||
std::unique_ptr<SymbolIndex> Dex::build(SymbolSlab Symbols, RefSlab Refs,
|
||||
RelationSlab Rels) {
|
||||
RelationSlab Rels,
|
||||
bool SupportContainedRefs) {
|
||||
auto Size = Symbols.bytes() + Refs.bytes();
|
||||
// There is no need to include "Rels" in Data because the relations are self-
|
||||
// contained, without references into a backing store.
|
||||
auto Data = std::make_pair(std::move(Symbols), std::move(Refs));
|
||||
return std::make_unique<Dex>(Data.first, Data.second, Rels, std::move(Data),
|
||||
Size);
|
||||
Size, SupportContainedRefs);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -120,7 +121,7 @@ public:
|
||||
|
||||
} // namespace
|
||||
|
||||
void Dex::buildIndex() {
|
||||
void Dex::buildIndex(bool SupportContainedRefs) {
|
||||
this->Corpus = dex::Corpus(Symbols.size());
|
||||
std::vector<std::pair<float, const Symbol *>> ScoredSymbols(Symbols.size());
|
||||
|
||||
@ -147,6 +148,20 @@ void Dex::buildIndex() {
|
||||
for (DocID SymbolRank = 0; SymbolRank < Symbols.size(); ++SymbolRank)
|
||||
Builder.add(*Symbols[SymbolRank], SymbolRank);
|
||||
InvertedIndex = std::move(Builder).build();
|
||||
|
||||
// If the containedRefs() operation is supported, build the RevRefs
|
||||
// data structure used to implement it.
|
||||
if (!SupportContainedRefs)
|
||||
return;
|
||||
for (const auto &[ID, RefList] : Refs)
|
||||
for (const auto &R : RefList)
|
||||
if ((R.Kind & ContainedRefsRequest::SupportedRefKinds) !=
|
||||
RefKind::Unknown)
|
||||
RevRefs.emplace_back(R, ID);
|
||||
// Sort by container ID so we can use binary search for lookup.
|
||||
llvm::sort(RevRefs, [](const RevRef &A, const RevRef &B) {
|
||||
return A.ref().Container < B.ref().Container;
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> Dex::iterator(const Token &Tok) const {
|
||||
@ -314,6 +329,36 @@ bool Dex::refs(const RefsRequest &Req,
|
||||
return false; // We reported all refs.
|
||||
}
|
||||
|
||||
llvm::iterator_range<std::vector<Dex::RevRef>::const_iterator>
|
||||
Dex::lookupRevRefs(const SymbolID &Container) const {
|
||||
// equal_range() requires an element of the same type as the elements of the
|
||||
// range, so construct a dummy RevRef with the container of interest.
|
||||
Ref QueryRef;
|
||||
QueryRef.Container = Container;
|
||||
RevRef Query(QueryRef, SymbolID{});
|
||||
|
||||
auto ItPair = std::equal_range(RevRefs.cbegin(), RevRefs.cend(), Query,
|
||||
[](const RevRef &A, const RevRef &B) {
|
||||
return A.ref().Container < B.ref().Container;
|
||||
});
|
||||
return {ItPair.first, ItPair.second};
|
||||
}
|
||||
|
||||
bool Dex::containedRefs(
|
||||
const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const {
|
||||
trace::Span Tracer("Dex reversed refs");
|
||||
uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
|
||||
for (const auto &Rev : lookupRevRefs(Req.ID)) {
|
||||
// RevRefs are already filtered to ContainedRefsRequest::SupportedRefKinds
|
||||
if (Remaining == 0)
|
||||
return true; // More refs were available.
|
||||
--Remaining;
|
||||
Callback(Rev.containedRefsResult());
|
||||
}
|
||||
return false; // We reported all refs.
|
||||
}
|
||||
|
||||
void Dex::relations(
|
||||
const RelationsRequest &Req,
|
||||
llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
|
||||
@ -350,6 +395,7 @@ size_t Dex::estimateMemoryUsage() const {
|
||||
for (const auto &TokenToPostingList : InvertedIndex)
|
||||
Bytes += TokenToPostingList.second.bytes();
|
||||
Bytes += Refs.getMemorySize();
|
||||
Bytes += RevRefs.size() * sizeof(RevRef);
|
||||
Bytes += Relations.getMemorySize();
|
||||
return Bytes + BackingDataSize;
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ class Dex : public SymbolIndex {
|
||||
public:
|
||||
// All data must outlive this index.
|
||||
template <typename SymbolRange, typename RefsRange, typename RelationsRange>
|
||||
Dex(SymbolRange &&Symbols, RefsRange &&Refs, RelationsRange &&Relations)
|
||||
Dex(SymbolRange &&Symbols, RefsRange &&Refs, RelationsRange &&Relations,
|
||||
bool SupportContainedRefs)
|
||||
: Corpus(0) {
|
||||
for (auto &&Sym : Symbols)
|
||||
this->Symbols.push_back(&Sym);
|
||||
@ -46,15 +47,15 @@ public:
|
||||
this->Relations[std::make_pair(Rel.Subject,
|
||||
static_cast<uint8_t>(Rel.Predicate))]
|
||||
.push_back(Rel.Object);
|
||||
buildIndex();
|
||||
buildIndex(SupportContainedRefs);
|
||||
}
|
||||
// Symbols and Refs are owned by BackingData, Index takes ownership.
|
||||
template <typename SymbolRange, typename RefsRange, typename RelationsRange,
|
||||
typename Payload>
|
||||
Dex(SymbolRange &&Symbols, RefsRange &&Refs, RelationsRange &&Relations,
|
||||
Payload &&BackingData, size_t BackingDataSize)
|
||||
Payload &&BackingData, size_t BackingDataSize, bool SupportContainedRefs)
|
||||
: Dex(std::forward<SymbolRange>(Symbols), std::forward<RefsRange>(Refs),
|
||||
std::forward<RelationsRange>(Relations)) {
|
||||
std::forward<RelationsRange>(Relations), SupportContainedRefs) {
|
||||
KeepAlive = std::shared_ptr<void>(
|
||||
std::make_shared<Payload>(std::move(BackingData)), nullptr);
|
||||
this->BackingDataSize = BackingDataSize;
|
||||
@ -64,16 +65,18 @@ public:
|
||||
typename FileRange, typename Payload>
|
||||
Dex(SymbolRange &&Symbols, RefsRange &&Refs, RelationsRange &&Relations,
|
||||
FileRange &&Files, IndexContents IdxContents, Payload &&BackingData,
|
||||
size_t BackingDataSize)
|
||||
size_t BackingDataSize, bool SupportContainedRefs)
|
||||
: Dex(std::forward<SymbolRange>(Symbols), std::forward<RefsRange>(Refs),
|
||||
std::forward<RelationsRange>(Relations),
|
||||
std::forward<Payload>(BackingData), BackingDataSize) {
|
||||
std::forward<Payload>(BackingData), BackingDataSize,
|
||||
SupportContainedRefs) {
|
||||
this->Files = std::forward<FileRange>(Files);
|
||||
this->IdxContents = IdxContents;
|
||||
}
|
||||
|
||||
/// Builds an index from slabs. The index takes ownership of the slab.
|
||||
static std::unique_ptr<SymbolIndex> build(SymbolSlab, RefSlab, RelationSlab);
|
||||
static std::unique_ptr<SymbolIndex> build(SymbolSlab, RefSlab, RelationSlab,
|
||||
bool SupportContainedRefs);
|
||||
|
||||
bool
|
||||
fuzzyFind(const FuzzyFindRequest &Req,
|
||||
@ -85,6 +88,10 @@ public:
|
||||
bool refs(const RefsRequest &Req,
|
||||
llvm::function_ref<void(const Ref &)> Callback) const override;
|
||||
|
||||
bool containedRefs(const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>
|
||||
Callback) const override;
|
||||
|
||||
void relations(const RelationsRequest &Req,
|
||||
llvm::function_ref<void(const SymbolID &, const Symbol &)>
|
||||
Callback) const override;
|
||||
@ -95,7 +102,22 @@ public:
|
||||
size_t estimateMemoryUsage() const override;
|
||||
|
||||
private:
|
||||
void buildIndex();
|
||||
class RevRef {
|
||||
const Ref *Reference;
|
||||
SymbolID Target;
|
||||
|
||||
public:
|
||||
RevRef(const Ref &Reference, SymbolID Target)
|
||||
: Reference(&Reference), Target(Target) {}
|
||||
const Ref &ref() const { return *Reference; }
|
||||
ContainedRefsResult containedRefsResult() const {
|
||||
return {ref().Location, ref().Kind, Target};
|
||||
}
|
||||
};
|
||||
|
||||
void buildIndex(bool EnableOutgoingCalls);
|
||||
llvm::iterator_range<std::vector<RevRef>::const_iterator>
|
||||
lookupRevRefs(const SymbolID &Container) const;
|
||||
std::unique_ptr<Iterator> iterator(const Token &Tok) const;
|
||||
std::unique_ptr<Iterator>
|
||||
createFileProximityIterator(llvm::ArrayRef<std::string> ProximityPaths) const;
|
||||
@ -116,6 +138,7 @@ private:
|
||||
llvm::DenseMap<Token, PostingList> InvertedIndex;
|
||||
dex::Corpus Corpus;
|
||||
llvm::DenseMap<SymbolID, llvm::ArrayRef<Ref>> Refs;
|
||||
std::vector<RevRef> RevRefs; // sorted by container ID
|
||||
static_assert(sizeof(RelationKind) == sizeof(uint8_t),
|
||||
"RelationKind should be of same size as a uint8_t");
|
||||
llvm::DenseMap<std::pair<SymbolID, uint8_t>, std::vector<SymbolID>> Relations;
|
||||
|
@ -375,7 +375,8 @@ std::unique_ptr<SymbolIndex> openIndex(llvm::StringRef Index) {
|
||||
return Index.starts_with("remote:")
|
||||
? remote::getClient(Index.drop_front(strlen("remote:")),
|
||||
ProjectRoot)
|
||||
: loadIndex(Index, SymbolOrigin::Static, /*UseDex=*/true);
|
||||
: loadIndex(Index, SymbolOrigin::Static, /*UseDex=*/true,
|
||||
/*SupportContainedRefs=*/true);
|
||||
}
|
||||
|
||||
bool runCommand(std::string Request, const SymbolIndex &Index) {
|
||||
|
@ -146,6 +146,13 @@ public:
|
||||
return streamRPC(Request, &remote::v1::SymbolIndex::Stub::Refs, Callback);
|
||||
}
|
||||
|
||||
bool containedRefs(const clangd::ContainedRefsRequest &Request,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>
|
||||
Callback) const override {
|
||||
return streamRPC(Request, &remote::v1::SymbolIndex::Stub::ContainedRefs,
|
||||
Callback);
|
||||
}
|
||||
|
||||
void
|
||||
relations(const clangd::RelationsRequest &Request,
|
||||
llvm::function_ref<void(const SymbolID &, const clangd::Symbol &)>
|
||||
|
@ -131,3 +131,21 @@ message Relation {
|
||||
optional string subject_id = 1;
|
||||
optional Symbol object = 2;
|
||||
}
|
||||
|
||||
message ContainedRefsRequest {
|
||||
required string id = 1;
|
||||
optional uint32 limit = 2;
|
||||
}
|
||||
|
||||
message ContainedRefsReply {
|
||||
oneof kind {
|
||||
ContainedRef stream_result = 1;
|
||||
FinalResult final_result = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message ContainedRef {
|
||||
required SymbolLocation location = 1;
|
||||
required uint32 kind = 2;
|
||||
required string symbol = 3;
|
||||
}
|
||||
|
@ -21,5 +21,7 @@ service SymbolIndex {
|
||||
|
||||
rpc Refs(RefsRequest) returns (stream RefsReply) {}
|
||||
|
||||
rpc ContainedRefs(ContainedRefsRequest) returns (stream ContainedRefsReply) {}
|
||||
|
||||
rpc Relations(RelationsRequest) returns (stream RelationsReply) {}
|
||||
}
|
||||
|
@ -126,6 +126,18 @@ Marshaller::fromProtobuf(const RefsRequest *Message) {
|
||||
return Req;
|
||||
}
|
||||
|
||||
llvm::Expected<clangd::ContainedRefsRequest>
|
||||
Marshaller::fromProtobuf(const ContainedRefsRequest *Message) {
|
||||
clangd::ContainedRefsRequest Req;
|
||||
auto ID = SymbolID::fromStr(Message->id());
|
||||
if (!ID)
|
||||
return ID.takeError();
|
||||
Req.ID = *ID;
|
||||
if (Message->has_limit())
|
||||
Req.Limit = Message->limit();
|
||||
return Req;
|
||||
}
|
||||
|
||||
llvm::Expected<clangd::RelationsRequest>
|
||||
Marshaller::fromProtobuf(const RelationsRequest *Message) {
|
||||
clangd::RelationsRequest Req;
|
||||
@ -192,6 +204,21 @@ llvm::Expected<clangd::Ref> Marshaller::fromProtobuf(const Ref &Message) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
llvm::Expected<clangd::ContainedRefsResult>
|
||||
Marshaller::fromProtobuf(const ContainedRef &Message) {
|
||||
clangd::ContainedRefsResult Result;
|
||||
auto Location = fromProtobuf(Message.location());
|
||||
if (!Location)
|
||||
return Location.takeError();
|
||||
Result.Location = *Location;
|
||||
Result.Kind = static_cast<RefKind>(Message.kind());
|
||||
auto Symbol = SymbolID::fromStr(Message.symbol());
|
||||
if (!Symbol)
|
||||
return Symbol.takeError();
|
||||
Result.Symbol = *Symbol;
|
||||
return Result;
|
||||
}
|
||||
|
||||
llvm::Expected<std::pair<clangd::SymbolID, clangd::Symbol>>
|
||||
Marshaller::fromProtobuf(const Relation &Message) {
|
||||
auto SubjectID = SymbolID::fromStr(Message.subject_id());
|
||||
@ -244,6 +271,15 @@ RefsRequest Marshaller::toProtobuf(const clangd::RefsRequest &From) {
|
||||
return RPCRequest;
|
||||
}
|
||||
|
||||
ContainedRefsRequest
|
||||
Marshaller::toProtobuf(const clangd::ContainedRefsRequest &From) {
|
||||
ContainedRefsRequest RPCRequest;
|
||||
RPCRequest.set_id(From.ID.str());
|
||||
if (From.Limit)
|
||||
RPCRequest.set_limit(*From.Limit);
|
||||
return RPCRequest;
|
||||
}
|
||||
|
||||
RelationsRequest Marshaller::toProtobuf(const clangd::RelationsRequest &From) {
|
||||
RelationsRequest RPCRequest;
|
||||
for (const auto &ID : From.Subjects)
|
||||
@ -299,6 +335,18 @@ llvm::Expected<Ref> Marshaller::toProtobuf(const clangd::Ref &From) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
llvm::Expected<ContainedRef>
|
||||
Marshaller::toProtobuf(const clangd::ContainedRefsResult &From) {
|
||||
ContainedRef Result;
|
||||
auto Location = toProtobuf(From.Location);
|
||||
if (!Location)
|
||||
return Location.takeError();
|
||||
*Result.mutable_location() = *Location;
|
||||
Result.set_kind(static_cast<uint32_t>(From.Kind));
|
||||
*Result.mutable_symbol() = From.Symbol.str();
|
||||
return Result;
|
||||
}
|
||||
|
||||
llvm::Expected<Relation> Marshaller::toProtobuf(const clangd::SymbolID &Subject,
|
||||
const clangd::Symbol &Object) {
|
||||
Relation Result;
|
||||
|
@ -40,6 +40,8 @@ public:
|
||||
|
||||
llvm::Expected<clangd::Symbol> fromProtobuf(const Symbol &Message);
|
||||
llvm::Expected<clangd::Ref> fromProtobuf(const Ref &Message);
|
||||
llvm::Expected<clangd::ContainedRefsResult>
|
||||
fromProtobuf(const ContainedRef &Message);
|
||||
llvm::Expected<std::pair<clangd::SymbolID, clangd::Symbol>>
|
||||
fromProtobuf(const Relation &Message);
|
||||
|
||||
@ -48,6 +50,8 @@ public:
|
||||
llvm::Expected<clangd::FuzzyFindRequest>
|
||||
fromProtobuf(const FuzzyFindRequest *Message);
|
||||
llvm::Expected<clangd::RefsRequest> fromProtobuf(const RefsRequest *Message);
|
||||
llvm::Expected<clangd::ContainedRefsRequest>
|
||||
fromProtobuf(const ContainedRefsRequest *Message);
|
||||
llvm::Expected<clangd::RelationsRequest>
|
||||
fromProtobuf(const RelationsRequest *Message);
|
||||
|
||||
@ -58,10 +62,13 @@ public:
|
||||
LookupRequest toProtobuf(const clangd::LookupRequest &From);
|
||||
FuzzyFindRequest toProtobuf(const clangd::FuzzyFindRequest &From);
|
||||
RefsRequest toProtobuf(const clangd::RefsRequest &From);
|
||||
ContainedRefsRequest toProtobuf(const clangd::ContainedRefsRequest &From);
|
||||
RelationsRequest toProtobuf(const clangd::RelationsRequest &From);
|
||||
|
||||
llvm::Expected<Symbol> toProtobuf(const clangd::Symbol &From);
|
||||
llvm::Expected<Ref> toProtobuf(const clangd::Ref &From);
|
||||
llvm::Expected<ContainedRef>
|
||||
toProtobuf(const clangd::ContainedRefsResult &From);
|
||||
llvm::Expected<Relation> toProtobuf(const clangd::SymbolID &Subject,
|
||||
const clangd::Symbol &Object);
|
||||
|
||||
|
@ -258,6 +258,53 @@ private:
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
grpc::Status
|
||||
ContainedRefs(grpc::ServerContext *Context,
|
||||
const ContainedRefsRequest *Request,
|
||||
grpc::ServerWriter<ContainedRefsReply> *Reply) override {
|
||||
auto StartTime = stopwatch::now();
|
||||
WithContextValue WithRequestContext(CurrentRequest, Context);
|
||||
logRequest(*Request);
|
||||
trace::Span Tracer("ContainedRefsRequest");
|
||||
auto Req = ProtobufMarshaller->fromProtobuf(Request);
|
||||
if (!Req) {
|
||||
elog("Can not parse ContainedRefsRequest from protobuf: {0}",
|
||||
Req.takeError());
|
||||
return grpc::Status::CANCELLED;
|
||||
}
|
||||
if (!Req->Limit || *Req->Limit > LimitResults) {
|
||||
log("[public] Limiting result size for ContainedRefs request from {0} to "
|
||||
"{1}.",
|
||||
Req->Limit, LimitResults);
|
||||
Req->Limit = LimitResults;
|
||||
}
|
||||
unsigned Sent = 0;
|
||||
unsigned FailedToSend = 0;
|
||||
bool HasMore =
|
||||
Index.containedRefs(*Req, [&](const clangd::ContainedRefsResult &Item) {
|
||||
auto SerializedItem = ProtobufMarshaller->toProtobuf(Item);
|
||||
if (!SerializedItem) {
|
||||
elog("Unable to convert ContainedRefsResult to protobuf: {0}",
|
||||
SerializedItem.takeError());
|
||||
++FailedToSend;
|
||||
return;
|
||||
}
|
||||
ContainedRefsReply NextMessage;
|
||||
*NextMessage.mutable_stream_result() = *SerializedItem;
|
||||
logResponse(NextMessage);
|
||||
Reply->Write(NextMessage);
|
||||
++Sent;
|
||||
});
|
||||
ContainedRefsReply LastMessage;
|
||||
LastMessage.mutable_final_result()->set_has_more(HasMore);
|
||||
logResponse(LastMessage);
|
||||
Reply->Write(LastMessage);
|
||||
SPAN_ATTACH(Tracer, "Sent", Sent);
|
||||
SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);
|
||||
logRequestSummary("v1/ContainedRefs", Sent, StartTime);
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
grpc::Status Relations(grpc::ServerContext *Context,
|
||||
const RelationsRequest *Request,
|
||||
grpc::ServerWriter<RelationsReply> *Reply) override {
|
||||
@ -396,7 +443,8 @@ void hotReload(clangd::SwapIndex &Index, llvm::StringRef IndexPath,
|
||||
LastStatus.getLastModificationTime(), Status->getLastModificationTime());
|
||||
LastStatus = *Status;
|
||||
std::unique_ptr<clang::clangd::SymbolIndex> NewIndex =
|
||||
loadIndex(IndexPath, SymbolOrigin::Static);
|
||||
loadIndex(IndexPath, SymbolOrigin::Static, /*UseDex=*/true,
|
||||
/*SupportContainedRefs=*/true);
|
||||
if (!NewIndex) {
|
||||
elog("Failed to load new index. Old index will be served.");
|
||||
return;
|
||||
@ -532,8 +580,9 @@ int main(int argc, char *argv[]) {
|
||||
return Status.getError().value();
|
||||
}
|
||||
|
||||
auto SymIndex =
|
||||
clang::clangd::loadIndex(IndexPath, clang::clangd::SymbolOrigin::Static);
|
||||
auto SymIndex = clang::clangd::loadIndex(
|
||||
IndexPath, clang::clangd::SymbolOrigin::Static, /*UseDex=*/true,
|
||||
/*SupportContainedRefs=*/true);
|
||||
if (!SymIndex) {
|
||||
llvm::errs() << "Failed to open the index.\n";
|
||||
return -1;
|
||||
|
Binary file not shown.
@ -12,6 +12,7 @@
|
||||
# CHECK-NEXT: "data": {
|
||||
# CHECK-NEXT: "symbolID": "A6576FE083F2949A"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "detail": "Child3",
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child3",
|
||||
# CHECK-NEXT: "range": {
|
||||
@ -153,6 +154,7 @@
|
||||
# CHECK-NEXT: "data": {
|
||||
# CHECK-NEXT: "symbolID": "5705B382DFC77CBC"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "detail": "Child4",
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child4",
|
||||
# CHECK-NEXT: "range": {
|
||||
|
@ -62,6 +62,7 @@
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "symbolID": "ECDC0C46D75120F4"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "detail": "Child1",
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child1",
|
||||
# CHECK-NEXT: "range": {
|
||||
@ -112,6 +113,7 @@
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "symbolID": "A6576FE083F2949A"
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: "detail": "Child3",
|
||||
# CHECK-NEXT: "kind": 23,
|
||||
# CHECK-NEXT: "name": "Child3",
|
||||
# CHECK-NEXT: "range": {
|
||||
|
@ -163,7 +163,7 @@ public:
|
||||
unsigned ErrCount = 0;
|
||||
|
||||
Checker(llvm::StringRef File, const ClangdLSPServer::Options &Opts)
|
||||
: File(File), Opts(Opts) {}
|
||||
: File(File), Opts(Opts), Index(/*SupportContainedRefs=*/true) {}
|
||||
|
||||
// Read compilation database and choose a compile command for the file.
|
||||
bool buildCommand(const ThreadsafeFS &TFS) {
|
||||
|
@ -604,7 +604,7 @@ const char TestScheme::TestDir[] = "/clangd-test";
|
||||
|
||||
std::unique_ptr<SymbolIndex>
|
||||
loadExternalIndex(const Config::ExternalIndexSpec &External,
|
||||
AsyncTaskRunner *Tasks) {
|
||||
AsyncTaskRunner *Tasks, bool SupportContainedRefs) {
|
||||
static const trace::Metric RemoteIndexUsed("used_remote_index",
|
||||
trace::Metric::Value, "address");
|
||||
switch (External.Kind) {
|
||||
@ -620,8 +620,9 @@ loadExternalIndex(const Config::ExternalIndexSpec &External,
|
||||
External.Location);
|
||||
auto NewIndex = std::make_unique<SwapIndex>(std::make_unique<MemIndex>());
|
||||
auto IndexLoadTask = [File = External.Location,
|
||||
PlaceHolder = NewIndex.get()] {
|
||||
if (auto Idx = loadIndex(File, SymbolOrigin::Static, /*UseDex=*/true))
|
||||
PlaceHolder = NewIndex.get(), SupportContainedRefs] {
|
||||
if (auto Idx = loadIndex(File, SymbolOrigin::Static, /*UseDex=*/true,
|
||||
SupportContainedRefs))
|
||||
PlaceHolder->reset(std::move(Idx));
|
||||
};
|
||||
if (Tasks) {
|
||||
@ -909,7 +910,12 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
|
||||
Opts.BackgroundIndexPriority = BackgroundIndexPriority;
|
||||
Opts.ReferencesLimit = ReferencesLimit;
|
||||
Opts.Rename.LimitFiles = RenameFileLimit;
|
||||
auto PAI = createProjectAwareIndex(loadExternalIndex, Sync);
|
||||
auto PAI = createProjectAwareIndex(
|
||||
[SupportContainedRefs = Opts.EnableOutgoingCalls](
|
||||
const Config::ExternalIndexSpec &External, AsyncTaskRunner *Tasks) {
|
||||
return loadExternalIndex(External, Tasks, SupportContainedRefs);
|
||||
},
|
||||
Sync);
|
||||
Opts.StaticIndex = PAI.get();
|
||||
Opts.AsyncThreadsCount = WorkerThreadsCount;
|
||||
Opts.MemoryCleanup = getMemoryCleanupFunction();
|
||||
|
@ -685,7 +685,8 @@ TEST_F(BackgroundIndexTest, Reindex) {
|
||||
class BackgroundIndexRebuilderTest : public testing::Test {
|
||||
protected:
|
||||
BackgroundIndexRebuilderTest()
|
||||
: Source(IndexContents::All), Target(std::make_unique<MemIndex>()),
|
||||
: Source(IndexContents::All, /*SupportContainedRefs=*/true),
|
||||
Target(std::make_unique<MemIndex>()),
|
||||
Rebuilder(&Target, &Source, /*Threads=*/10) {
|
||||
// Prepare FileSymbols with TestSymbol in it, for checkRebuild.
|
||||
TestSymbol.ID = SymbolID("foo");
|
||||
|
@ -44,17 +44,27 @@ using ::testing::UnorderedElementsAre;
|
||||
|
||||
// Helpers for matching call hierarchy data structures.
|
||||
MATCHER_P(withName, N, "") { return arg.name == N; }
|
||||
MATCHER_P(withDetail, N, "") { return arg.detail == N; }
|
||||
MATCHER_P(withSelectionRange, R, "") { return arg.selectionRange == R; }
|
||||
|
||||
template <class ItemMatcher>
|
||||
::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher M) {
|
||||
return Field(&CallHierarchyIncomingCall::from, M);
|
||||
}
|
||||
template <class ItemMatcher>
|
||||
::testing::Matcher<CallHierarchyOutgoingCall> to(ItemMatcher M) {
|
||||
return Field(&CallHierarchyOutgoingCall::to, M);
|
||||
}
|
||||
template <class... RangeMatchers>
|
||||
::testing::Matcher<CallHierarchyIncomingCall> fromRanges(RangeMatchers... M) {
|
||||
::testing::Matcher<CallHierarchyIncomingCall> iFromRanges(RangeMatchers... M) {
|
||||
return Field(&CallHierarchyIncomingCall::fromRanges,
|
||||
UnorderedElementsAre(M...));
|
||||
}
|
||||
template <class... RangeMatchers>
|
||||
::testing::Matcher<CallHierarchyOutgoingCall> oFromRanges(RangeMatchers... M) {
|
||||
return Field(&CallHierarchyOutgoingCall::fromRanges,
|
||||
UnorderedElementsAre(M...));
|
||||
}
|
||||
|
||||
TEST(CallHierarchy, IncomingOneFileCpp) {
|
||||
Annotations Source(R"cpp(
|
||||
@ -79,21 +89,24 @@ TEST(CallHierarchy, IncomingOneFileCpp) {
|
||||
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
|
||||
ASSERT_THAT(Items, ElementsAre(withName("callee")));
|
||||
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(IncomingLevel1,
|
||||
ElementsAre(AllOf(from(withName("caller1")),
|
||||
fromRanges(Source.range("Callee")))));
|
||||
ASSERT_THAT(
|
||||
IncomingLevel1,
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))),
|
||||
iFromRanges(Source.range("Callee")))));
|
||||
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
|
||||
ASSERT_THAT(IncomingLevel2,
|
||||
ElementsAre(AllOf(from(withName("caller2")),
|
||||
fromRanges(Source.range("Caller1A"),
|
||||
Source.range("Caller1B"))),
|
||||
AllOf(from(withName("caller3")),
|
||||
fromRanges(Source.range("Caller1C")))));
|
||||
ASSERT_THAT(
|
||||
IncomingLevel2,
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("caller2"))),
|
||||
iFromRanges(Source.range("Caller1A"),
|
||||
Source.range("Caller1B"))),
|
||||
AllOf(from(AllOf(withName("caller3"), withDetail("caller3"))),
|
||||
iFromRanges(Source.range("Caller1C")))));
|
||||
|
||||
auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
|
||||
ASSERT_THAT(IncomingLevel3,
|
||||
ElementsAre(AllOf(from(withName("caller3")),
|
||||
fromRanges(Source.range("Caller2")))));
|
||||
ASSERT_THAT(
|
||||
IncomingLevel3,
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller3"), withDetail("caller3"))),
|
||||
iFromRanges(Source.range("Caller2")))));
|
||||
|
||||
auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
|
||||
EXPECT_THAT(IncomingLevel4, IsEmpty());
|
||||
@ -125,20 +138,24 @@ TEST(CallHierarchy, IncomingOneFileObjC) {
|
||||
ASSERT_THAT(Items, ElementsAre(withName("callee")));
|
||||
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(IncomingLevel1,
|
||||
ElementsAre(AllOf(from(withName("caller1")),
|
||||
fromRanges(Source.range("Callee")))));
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller1"),
|
||||
withDetail("MyClass::caller1"))),
|
||||
iFromRanges(Source.range("Callee")))));
|
||||
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
|
||||
ASSERT_THAT(IncomingLevel2,
|
||||
ElementsAre(AllOf(from(withName("caller2")),
|
||||
fromRanges(Source.range("Caller1A"),
|
||||
Source.range("Caller1B"))),
|
||||
AllOf(from(withName("caller3")),
|
||||
fromRanges(Source.range("Caller1C")))));
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller2"),
|
||||
withDetail("MyClass::caller2"))),
|
||||
iFromRanges(Source.range("Caller1A"),
|
||||
Source.range("Caller1B"))),
|
||||
AllOf(from(AllOf(withName("caller3"),
|
||||
withDetail("MyClass::caller3"))),
|
||||
iFromRanges(Source.range("Caller1C")))));
|
||||
|
||||
auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
|
||||
ASSERT_THAT(IncomingLevel3,
|
||||
ElementsAre(AllOf(from(withName("caller3")),
|
||||
fromRanges(Source.range("Caller2")))));
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller3"),
|
||||
withDetail("MyClass::caller3"))),
|
||||
iFromRanges(Source.range("Caller2")))));
|
||||
|
||||
auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
|
||||
EXPECT_THAT(IncomingLevel4, IsEmpty());
|
||||
@ -167,14 +184,16 @@ TEST(CallHierarchy, MainFileOnlyRef) {
|
||||
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
|
||||
ASSERT_THAT(Items, ElementsAre(withName("callee")));
|
||||
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(IncomingLevel1,
|
||||
ElementsAre(AllOf(from(withName("caller1")),
|
||||
fromRanges(Source.range("Callee")))));
|
||||
ASSERT_THAT(
|
||||
IncomingLevel1,
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller1"), withDetail("caller1"))),
|
||||
iFromRanges(Source.range("Callee")))));
|
||||
|
||||
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
|
||||
EXPECT_THAT(IncomingLevel2,
|
||||
ElementsAre(AllOf(from(withName("caller2")),
|
||||
fromRanges(Source.range("Caller1")))));
|
||||
EXPECT_THAT(
|
||||
IncomingLevel2,
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller2"), withDetail("caller2"))),
|
||||
iFromRanges(Source.range("Caller1")))));
|
||||
}
|
||||
|
||||
TEST(CallHierarchy, IncomingQualified) {
|
||||
@ -200,14 +219,72 @@ TEST(CallHierarchy, IncomingQualified) {
|
||||
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
|
||||
ASSERT_THAT(Items, ElementsAre(withName("Waldo::find")));
|
||||
auto Incoming = incomingCalls(Items[0], Index.get());
|
||||
EXPECT_THAT(Incoming,
|
||||
ElementsAre(AllOf(from(withName("caller1")),
|
||||
fromRanges(Source.range("Caller1"))),
|
||||
AllOf(from(withName("caller2")),
|
||||
fromRanges(Source.range("Caller2")))));
|
||||
EXPECT_THAT(
|
||||
Incoming,
|
||||
ElementsAre(
|
||||
AllOf(from(AllOf(withName("caller1"), withDetail("ns::caller1"))),
|
||||
iFromRanges(Source.range("Caller1"))),
|
||||
AllOf(from(AllOf(withName("caller2"), withDetail("ns::caller2"))),
|
||||
iFromRanges(Source.range("Caller2")))));
|
||||
}
|
||||
|
||||
TEST(CallHierarchy, IncomingMultiFileCpp) {
|
||||
TEST(CallHierarchy, OutgoingOneFile) {
|
||||
// Test outgoing call on the main file, with namespaces and methods
|
||||
Annotations Source(R"cpp(
|
||||
void callee(int);
|
||||
namespace ns {
|
||||
struct Foo {
|
||||
void caller1();
|
||||
};
|
||||
void Foo::caller1() {
|
||||
$Callee[[callee]](42);
|
||||
}
|
||||
}
|
||||
namespace {
|
||||
void caller2(ns::Foo& F) {
|
||||
F.$Caller1A[[caller1]]();
|
||||
F.$Caller1B[[caller1]]();
|
||||
}
|
||||
}
|
||||
void call^er3(ns::Foo& F) {
|
||||
F.$Caller1C[[caller1]]();
|
||||
$Caller2[[caller2]](F);
|
||||
}
|
||||
)cpp");
|
||||
TestTU TU = TestTU::withCode(Source.code());
|
||||
auto AST = TU.build();
|
||||
auto Index = TU.index();
|
||||
|
||||
std::vector<CallHierarchyItem> Items =
|
||||
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
|
||||
ASSERT_THAT(Items, ElementsAre(withName("caller3")));
|
||||
auto OugoingLevel1 = outgoingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(
|
||||
OugoingLevel1,
|
||||
ElementsAre(
|
||||
AllOf(to(AllOf(withName("caller1"), withDetail("ns::Foo::caller1"))),
|
||||
oFromRanges(Source.range("Caller1C"))),
|
||||
AllOf(to(AllOf(withName("caller2"), withDetail("caller2"))),
|
||||
oFromRanges(Source.range("Caller2")))));
|
||||
|
||||
auto OutgoingLevel2 = outgoingCalls(OugoingLevel1[1].to, Index.get());
|
||||
ASSERT_THAT(
|
||||
OutgoingLevel2,
|
||||
ElementsAre(AllOf(
|
||||
to(AllOf(withName("caller1"), withDetail("ns::Foo::caller1"))),
|
||||
oFromRanges(Source.range("Caller1A"), Source.range("Caller1B")))));
|
||||
|
||||
auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get());
|
||||
ASSERT_THAT(
|
||||
OutgoingLevel3,
|
||||
ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))),
|
||||
oFromRanges(Source.range("Callee")))));
|
||||
|
||||
auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get());
|
||||
EXPECT_THAT(OutgoingLevel4, IsEmpty());
|
||||
}
|
||||
|
||||
TEST(CallHierarchy, MultiFileCpp) {
|
||||
// The test uses a .hh suffix for header files to get clang
|
||||
// to parse them in C++ mode. .h files are parsed in C mode
|
||||
// by default, which causes problems because e.g. symbol
|
||||
@ -221,32 +298,47 @@ TEST(CallHierarchy, IncomingMultiFileCpp) {
|
||||
void calle^e(int) {}
|
||||
)cpp");
|
||||
Annotations Caller1H(R"cpp(
|
||||
void caller1();
|
||||
namespace nsa {
|
||||
void caller1();
|
||||
}
|
||||
)cpp");
|
||||
Annotations Caller1C(R"cpp(
|
||||
#include "callee.hh"
|
||||
#include "caller1.hh"
|
||||
void caller1() {
|
||||
[[calle^e]](42);
|
||||
namespace nsa {
|
||||
void caller1() {
|
||||
[[calle^e]](42);
|
||||
}
|
||||
}
|
||||
)cpp");
|
||||
Annotations Caller2H(R"cpp(
|
||||
void caller2();
|
||||
namespace nsb {
|
||||
void caller2();
|
||||
}
|
||||
)cpp");
|
||||
Annotations Caller2C(R"cpp(
|
||||
#include "caller1.hh"
|
||||
#include "caller2.hh"
|
||||
void caller2() {
|
||||
$A[[caller1]]();
|
||||
$B[[caller1]]();
|
||||
namespace nsb {
|
||||
void caller2() {
|
||||
nsa::$A[[caller1]]();
|
||||
nsa::$B[[caller1]]();
|
||||
}
|
||||
}
|
||||
)cpp");
|
||||
Annotations Caller3H(R"cpp(
|
||||
namespace nsa {
|
||||
void call^er3();
|
||||
}
|
||||
)cpp");
|
||||
Annotations Caller3C(R"cpp(
|
||||
#include "caller1.hh"
|
||||
#include "caller2.hh"
|
||||
void caller3() {
|
||||
$Caller1[[caller1]]();
|
||||
$Caller2[[caller2]]();
|
||||
namespace nsa {
|
||||
void call^er3() {
|
||||
$Caller1[[caller1]]();
|
||||
nsb::$Caller2[[caller2]]();
|
||||
}
|
||||
}
|
||||
)cpp");
|
||||
|
||||
@ -254,6 +346,7 @@ TEST(CallHierarchy, IncomingMultiFileCpp) {
|
||||
Workspace.addSource("callee.hh", CalleeH.code());
|
||||
Workspace.addSource("caller1.hh", Caller1H.code());
|
||||
Workspace.addSource("caller2.hh", Caller2H.code());
|
||||
Workspace.addSource("caller3.hh", Caller3H.code());
|
||||
Workspace.addMainFile("callee.cc", CalleeC.code());
|
||||
Workspace.addMainFile("caller1.cc", Caller1C.code());
|
||||
Workspace.addMainFile("caller2.cc", Caller2C.code());
|
||||
@ -261,46 +354,84 @@ TEST(CallHierarchy, IncomingMultiFileCpp) {
|
||||
|
||||
auto Index = Workspace.index();
|
||||
|
||||
auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
|
||||
auto CheckIncomingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
|
||||
std::vector<CallHierarchyItem> Items =
|
||||
prepareCallHierarchy(AST, Pos, TUPath);
|
||||
ASSERT_THAT(Items, ElementsAre(withName("callee")));
|
||||
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(IncomingLevel1,
|
||||
ElementsAre(AllOf(from(withName("caller1")),
|
||||
fromRanges(Caller1C.range()))));
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller1"),
|
||||
withDetail("nsa::caller1"))),
|
||||
iFromRanges(Caller1C.range()))));
|
||||
|
||||
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
|
||||
ASSERT_THAT(
|
||||
IncomingLevel2,
|
||||
ElementsAre(AllOf(from(withName("caller2")),
|
||||
fromRanges(Caller2C.range("A"), Caller2C.range("B"))),
|
||||
AllOf(from(withName("caller3")),
|
||||
fromRanges(Caller3C.range("Caller1")))));
|
||||
ElementsAre(
|
||||
AllOf(from(AllOf(withName("caller2"), withDetail("nsb::caller2"))),
|
||||
iFromRanges(Caller2C.range("A"), Caller2C.range("B"))),
|
||||
AllOf(from(AllOf(withName("caller3"), withDetail("nsa::caller3"))),
|
||||
iFromRanges(Caller3C.range("Caller1")))));
|
||||
|
||||
auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
|
||||
ASSERT_THAT(IncomingLevel3,
|
||||
ElementsAre(AllOf(from(withName("caller3")),
|
||||
fromRanges(Caller3C.range("Caller2")))));
|
||||
ElementsAre(AllOf(from(AllOf(withName("caller3"),
|
||||
withDetail("nsa::caller3"))),
|
||||
iFromRanges(Caller3C.range("Caller2")))));
|
||||
|
||||
auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
|
||||
EXPECT_THAT(IncomingLevel4, IsEmpty());
|
||||
};
|
||||
|
||||
auto CheckOutgoingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
|
||||
std::vector<CallHierarchyItem> Items =
|
||||
prepareCallHierarchy(AST, Pos, TUPath);
|
||||
ASSERT_THAT(Items, ElementsAre(withName("caller3")));
|
||||
auto OutgoingLevel1 = outgoingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(
|
||||
OutgoingLevel1,
|
||||
ElementsAre(
|
||||
AllOf(to(AllOf(withName("caller1"), withDetail("nsa::caller1"))),
|
||||
oFromRanges(Caller3C.range("Caller1"))),
|
||||
AllOf(to(AllOf(withName("caller2"), withDetail("nsb::caller2"))),
|
||||
oFromRanges(Caller3C.range("Caller2")))));
|
||||
|
||||
auto OutgoingLevel2 = outgoingCalls(OutgoingLevel1[1].to, Index.get());
|
||||
ASSERT_THAT(OutgoingLevel2,
|
||||
ElementsAre(AllOf(
|
||||
to(AllOf(withName("caller1"), withDetail("nsa::caller1"))),
|
||||
oFromRanges(Caller2C.range("A"), Caller2C.range("B")))));
|
||||
|
||||
auto OutgoingLevel3 = outgoingCalls(OutgoingLevel2[0].to, Index.get());
|
||||
ASSERT_THAT(
|
||||
OutgoingLevel3,
|
||||
ElementsAre(AllOf(to(AllOf(withName("callee"), withDetail("callee"))),
|
||||
oFromRanges(Caller1C.range()))));
|
||||
|
||||
auto OutgoingLevel4 = outgoingCalls(OutgoingLevel3[0].to, Index.get());
|
||||
EXPECT_THAT(OutgoingLevel4, IsEmpty());
|
||||
};
|
||||
|
||||
// Check that invoking from a call site works.
|
||||
auto AST = Workspace.openFile("caller1.cc");
|
||||
ASSERT_TRUE(bool(AST));
|
||||
CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1.cc"));
|
||||
CheckIncomingCalls(*AST, Caller1C.point(), testPath("caller1.cc"));
|
||||
|
||||
// Check that invoking from the declaration site works.
|
||||
AST = Workspace.openFile("callee.hh");
|
||||
ASSERT_TRUE(bool(AST));
|
||||
CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee.hh"));
|
||||
CheckIncomingCalls(*AST, CalleeH.point(), testPath("callee.hh"));
|
||||
AST = Workspace.openFile("caller3.hh");
|
||||
ASSERT_TRUE(bool(AST));
|
||||
CheckOutgoingCalls(*AST, Caller3H.point(), testPath("caller3.hh"));
|
||||
|
||||
// Check that invoking from the definition site works.
|
||||
AST = Workspace.openFile("callee.cc");
|
||||
ASSERT_TRUE(bool(AST));
|
||||
CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.cc"));
|
||||
CheckIncomingCalls(*AST, CalleeC.point(), testPath("callee.cc"));
|
||||
AST = Workspace.openFile("caller3.cc");
|
||||
ASSERT_TRUE(bool(AST));
|
||||
CheckOutgoingCalls(*AST, Caller3C.point(), testPath("caller3.cc"));
|
||||
}
|
||||
|
||||
TEST(CallHierarchy, IncomingMultiFileObjC) {
|
||||
@ -377,20 +508,20 @@ TEST(CallHierarchy, IncomingMultiFileObjC) {
|
||||
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(IncomingLevel1,
|
||||
ElementsAre(AllOf(from(withName("caller1")),
|
||||
fromRanges(Caller1C.range()))));
|
||||
iFromRanges(Caller1C.range()))));
|
||||
|
||||
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
|
||||
ASSERT_THAT(
|
||||
IncomingLevel2,
|
||||
ElementsAre(AllOf(from(withName("caller2")),
|
||||
fromRanges(Caller2C.range("A"), Caller2C.range("B"))),
|
||||
AllOf(from(withName("caller3")),
|
||||
fromRanges(Caller3C.range("Caller1")))));
|
||||
ASSERT_THAT(IncomingLevel2,
|
||||
ElementsAre(AllOf(from(withName("caller2")),
|
||||
iFromRanges(Caller2C.range("A"),
|
||||
Caller2C.range("B"))),
|
||||
AllOf(from(withName("caller3")),
|
||||
iFromRanges(Caller3C.range("Caller1")))));
|
||||
|
||||
auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
|
||||
ASSERT_THAT(IncomingLevel3,
|
||||
ElementsAre(AllOf(from(withName("caller3")),
|
||||
fromRanges(Caller3C.range("Caller2")))));
|
||||
iFromRanges(Caller3C.range("Caller2")))));
|
||||
|
||||
auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
|
||||
EXPECT_THAT(IncomingLevel4, IsEmpty());
|
||||
@ -438,12 +569,12 @@ TEST(CallHierarchy, CallInLocalVarDecl) {
|
||||
ASSERT_THAT(Items, ElementsAre(withName("callee")));
|
||||
|
||||
auto Incoming = incomingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(
|
||||
Incoming,
|
||||
ElementsAre(
|
||||
AllOf(from(withName("caller1")), fromRanges(Source.range("call1"))),
|
||||
AllOf(from(withName("caller2")), fromRanges(Source.range("call2"))),
|
||||
AllOf(from(withName("caller3")), fromRanges(Source.range("call3")))));
|
||||
ASSERT_THAT(Incoming, ElementsAre(AllOf(from(withName("caller1")),
|
||||
iFromRanges(Source.range("call1"))),
|
||||
AllOf(from(withName("caller2")),
|
||||
iFromRanges(Source.range("call2"))),
|
||||
AllOf(from(withName("caller3")),
|
||||
iFromRanges(Source.range("call3")))));
|
||||
}
|
||||
|
||||
TEST(CallHierarchy, HierarchyOnField) {
|
||||
@ -467,7 +598,7 @@ TEST(CallHierarchy, HierarchyOnField) {
|
||||
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(IncomingLevel1,
|
||||
ElementsAre(AllOf(from(withName("caller")),
|
||||
fromRanges(Source.range("Callee")))));
|
||||
iFromRanges(Source.range("Callee")))));
|
||||
}
|
||||
|
||||
TEST(CallHierarchy, HierarchyOnVar) {
|
||||
@ -488,7 +619,7 @@ TEST(CallHierarchy, HierarchyOnVar) {
|
||||
auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
|
||||
ASSERT_THAT(IncomingLevel1,
|
||||
ElementsAre(AllOf(from(withName("caller")),
|
||||
fromRanges(Source.range("Callee")))));
|
||||
iFromRanges(Source.range("Callee")))));
|
||||
}
|
||||
|
||||
TEST(CallHierarchy, CallInDifferentFileThanCaller) {
|
||||
@ -517,7 +648,7 @@ TEST(CallHierarchy, CallInDifferentFileThanCaller) {
|
||||
// header. The protocol does not allow us to represent such calls, so we drop
|
||||
// them. (The call hierarchy item itself is kept.)
|
||||
EXPECT_THAT(Incoming,
|
||||
ElementsAre(AllOf(from(withName("caller")), fromRanges())));
|
||||
ElementsAre(AllOf(from(withName("caller")), iFromRanges())));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -1703,6 +1703,12 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool containedRefs(
|
||||
const ContainedRefsRequest &,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
void relations(const RelationsRequest &,
|
||||
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
|
||||
const override {}
|
||||
|
@ -476,7 +476,7 @@ TEST(DexSearchTokens, SymbolPath) {
|
||||
|
||||
TEST(Dex, Lookup) {
|
||||
auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(),
|
||||
RelationSlab());
|
||||
RelationSlab(), true);
|
||||
EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc"));
|
||||
EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}),
|
||||
UnorderedElementsAre("ns::abc", "ns::xyz"));
|
||||
@ -489,7 +489,7 @@ TEST(Dex, FuzzyFind) {
|
||||
auto Index =
|
||||
Dex::build(generateSymbols({"ns::ABC", "ns::BCD", "::ABC",
|
||||
"ns::nested::ABC", "other::ABC", "other::A"}),
|
||||
RefSlab(), RelationSlab());
|
||||
RefSlab(), RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Query = "ABC";
|
||||
Req.Scopes = {"ns::"};
|
||||
@ -511,7 +511,8 @@ TEST(Dex, FuzzyFind) {
|
||||
}
|
||||
|
||||
TEST(DexTest, DexLimitedNumMatches) {
|
||||
auto I = Dex::build(generateNumSymbols(0, 100), RefSlab(), RelationSlab());
|
||||
auto I =
|
||||
Dex::build(generateNumSymbols(0, 100), RefSlab(), RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Query = "5";
|
||||
Req.AnyScope = true;
|
||||
@ -526,7 +527,7 @@ TEST(DexTest, DexLimitedNumMatches) {
|
||||
TEST(DexTest, FuzzyMatch) {
|
||||
auto I = Dex::build(
|
||||
generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}),
|
||||
RefSlab(), RelationSlab());
|
||||
RefSlab(), RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Query = "lol";
|
||||
Req.AnyScope = true;
|
||||
@ -537,7 +538,7 @@ TEST(DexTest, FuzzyMatch) {
|
||||
|
||||
TEST(DexTest, ShortQuery) {
|
||||
auto I = Dex::build(generateSymbols({"_OneTwoFourSix"}), RefSlab(),
|
||||
RelationSlab());
|
||||
RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.AnyScope = true;
|
||||
bool Incomplete;
|
||||
@ -580,7 +581,7 @@ TEST(DexTest, ShortQuery) {
|
||||
|
||||
TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) {
|
||||
auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(),
|
||||
RelationSlab());
|
||||
RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.AnyScope = true;
|
||||
Req.Query = "y";
|
||||
@ -589,7 +590,7 @@ TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) {
|
||||
|
||||
TEST(DexTest, MatchQualifiedNamesWithGlobalScope) {
|
||||
auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(),
|
||||
RelationSlab());
|
||||
RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Query = "y";
|
||||
Req.Scopes = {""};
|
||||
@ -599,7 +600,7 @@ TEST(DexTest, MatchQualifiedNamesWithGlobalScope) {
|
||||
TEST(DexTest, MatchQualifiedNamesWithOneScope) {
|
||||
auto I =
|
||||
Dex::build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}),
|
||||
RefSlab(), RelationSlab());
|
||||
RefSlab(), RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Query = "y";
|
||||
Req.Scopes = {"a::"};
|
||||
@ -609,7 +610,7 @@ TEST(DexTest, MatchQualifiedNamesWithOneScope) {
|
||||
TEST(DexTest, MatchQualifiedNamesWithMultipleScopes) {
|
||||
auto I =
|
||||
Dex::build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}),
|
||||
RefSlab(), RelationSlab());
|
||||
RefSlab(), RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Query = "y";
|
||||
Req.Scopes = {"a::", "b::"};
|
||||
@ -618,7 +619,7 @@ TEST(DexTest, MatchQualifiedNamesWithMultipleScopes) {
|
||||
|
||||
TEST(DexTest, NoMatchNestedScopes) {
|
||||
auto I = Dex::build(generateSymbols({"a::y1", "a::b::y2"}), RefSlab(),
|
||||
RelationSlab());
|
||||
RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Query = "y";
|
||||
Req.Scopes = {"a::"};
|
||||
@ -627,7 +628,7 @@ TEST(DexTest, NoMatchNestedScopes) {
|
||||
|
||||
TEST(DexTest, WildcardScope) {
|
||||
auto I = Dex::build(generateSymbols({"a::y1", "a::b::y2", "c::y3"}),
|
||||
RefSlab(), RelationSlab());
|
||||
RefSlab(), RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.AnyScope = true;
|
||||
Req.Query = "y";
|
||||
@ -638,7 +639,7 @@ TEST(DexTest, WildcardScope) {
|
||||
|
||||
TEST(DexTest, IgnoreCases) {
|
||||
auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab(),
|
||||
RelationSlab());
|
||||
RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Query = "AB";
|
||||
Req.Scopes = {"ns::"};
|
||||
@ -648,7 +649,7 @@ TEST(DexTest, IgnoreCases) {
|
||||
TEST(DexTest, UnknownPostingList) {
|
||||
// Regression test: we used to ignore unknown scopes and accept any symbol.
|
||||
auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab(),
|
||||
RelationSlab());
|
||||
RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.Scopes = {"ns2::"};
|
||||
EXPECT_THAT(match(*I, Req), UnorderedElementsAre());
|
||||
@ -656,7 +657,7 @@ TEST(DexTest, UnknownPostingList) {
|
||||
|
||||
TEST(DexTest, Lookup) {
|
||||
auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(),
|
||||
RelationSlab());
|
||||
RelationSlab(), true);
|
||||
EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc"));
|
||||
EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}),
|
||||
UnorderedElementsAre("ns::abc", "ns::xyz"));
|
||||
@ -671,7 +672,7 @@ TEST(DexTest, SymbolIndexOptionsFilter) {
|
||||
CodeCompletionSymbol.Flags = Symbol::SymbolFlag::IndexedForCodeCompletion;
|
||||
NonCodeCompletionSymbol.Flags = Symbol::SymbolFlag::None;
|
||||
std::vector<Symbol> Symbols{CodeCompletionSymbol, NonCodeCompletionSymbol};
|
||||
Dex I(Symbols, RefSlab(), RelationSlab());
|
||||
Dex I(Symbols, RefSlab(), RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.AnyScope = true;
|
||||
Req.RestrictForCodeCompletion = false;
|
||||
@ -687,7 +688,7 @@ TEST(DexTest, ProximityPathsBoosting) {
|
||||
CloseSymbol.CanonicalDeclaration.FileURI = "unittest:///a/b/c/d/e/f/file.h";
|
||||
|
||||
std::vector<Symbol> Symbols{CloseSymbol, RootSymbol};
|
||||
Dex I(Symbols, RefSlab(), RelationSlab());
|
||||
Dex I(Symbols, RefSlab(), RelationSlab(), true);
|
||||
|
||||
FuzzyFindRequest Req;
|
||||
Req.AnyScope = true;
|
||||
@ -726,7 +727,7 @@ TEST(DexTests, Refs) {
|
||||
Req.Filter = RefKind::Declaration | RefKind::Definition;
|
||||
|
||||
std::vector<std::string> Files;
|
||||
EXPECT_FALSE(Dex(std::vector<Symbol>{Foo, Bar}, Refs, RelationSlab())
|
||||
EXPECT_FALSE(Dex(std::vector<Symbol>{Foo, Bar}, Refs, RelationSlab(), true)
|
||||
.refs(Req, [&](const Ref &R) {
|
||||
Files.push_back(R.Location.FileURI);
|
||||
}));
|
||||
@ -734,7 +735,7 @@ TEST(DexTests, Refs) {
|
||||
|
||||
Req.Limit = 1;
|
||||
Files.clear();
|
||||
EXPECT_TRUE(Dex(std::vector<Symbol>{Foo, Bar}, Refs, RelationSlab())
|
||||
EXPECT_TRUE(Dex(std::vector<Symbol>{Foo, Bar}, Refs, RelationSlab(), true)
|
||||
.refs(Req, [&](const Ref &R) {
|
||||
Files.push_back(R.Location.FileURI);
|
||||
}));
|
||||
@ -751,7 +752,7 @@ TEST(DexTests, Relations) {
|
||||
std::vector<Relation> Relations{{Parent.ID, RelationKind::BaseOf, Child1.ID},
|
||||
{Parent.ID, RelationKind::BaseOf, Child2.ID}};
|
||||
|
||||
Dex I{Symbols, RefSlab(), Relations};
|
||||
Dex I{Symbols, RefSlab(), Relations, true};
|
||||
|
||||
std::vector<SymbolID> Results;
|
||||
RelationsRequest Req;
|
||||
@ -770,7 +771,7 @@ TEST(DexIndex, IndexedFiles) {
|
||||
auto Data = std::make_pair(std::move(Symbols), std::move(Refs));
|
||||
llvm::StringSet<> Files = {"unittest:///foo.cc", "unittest:///bar.cc"};
|
||||
Dex I(std::move(Data.first), std::move(Data.second), RelationSlab(),
|
||||
std::move(Files), IndexContents::All, std::move(Data), Size);
|
||||
std::move(Files), IndexContents::All, std::move(Data), Size, true);
|
||||
auto ContainsFile = I.indexedFiles();
|
||||
EXPECT_EQ(ContainsFile("unittest:///foo.cc"), IndexContents::All);
|
||||
EXPECT_EQ(ContainsFile("unittest:///bar.cc"), IndexContents::All);
|
||||
@ -784,7 +785,7 @@ TEST(DexTest, PreferredTypesBoosting) {
|
||||
Sym2.Type = "T2";
|
||||
|
||||
std::vector<Symbol> Symbols{Sym1, Sym2};
|
||||
Dex I(Symbols, RefSlab(), RelationSlab());
|
||||
Dex I(Symbols, RefSlab(), RelationSlab(), true);
|
||||
|
||||
FuzzyFindRequest Req;
|
||||
Req.AnyScope = true;
|
||||
@ -820,7 +821,8 @@ TEST(DexTest, TemplateSpecialization) {
|
||||
index::SymbolProperty::TemplatePartialSpecialization);
|
||||
B.insert(S);
|
||||
|
||||
auto I = dex::Dex::build(std::move(B).build(), RefSlab(), RelationSlab());
|
||||
auto I =
|
||||
dex::Dex::build(std::move(B).build(), RefSlab(), RelationSlab(), true);
|
||||
FuzzyFindRequest Req;
|
||||
Req.AnyScope = true;
|
||||
|
||||
|
@ -104,7 +104,7 @@ std::unique_ptr<RelationSlab> relSlab(llvm::ArrayRef<const Relation> Rels) {
|
||||
}
|
||||
|
||||
TEST(FileSymbolsTest, UpdateAndGet) {
|
||||
FileSymbols FS(IndexContents::All);
|
||||
FileSymbols FS(IndexContents::All, true);
|
||||
EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty());
|
||||
|
||||
FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc"), nullptr,
|
||||
@ -116,7 +116,7 @@ TEST(FileSymbolsTest, UpdateAndGet) {
|
||||
}
|
||||
|
||||
TEST(FileSymbolsTest, Overlap) {
|
||||
FileSymbols FS(IndexContents::All);
|
||||
FileSymbols FS(IndexContents::All, true);
|
||||
FS.update("f1", numSlab(1, 3), nullptr, nullptr, false);
|
||||
FS.update("f2", numSlab(3, 5), nullptr, nullptr, false);
|
||||
for (auto Type : {IndexType::Light, IndexType::Heavy})
|
||||
@ -126,7 +126,7 @@ TEST(FileSymbolsTest, Overlap) {
|
||||
}
|
||||
|
||||
TEST(FileSymbolsTest, MergeOverlap) {
|
||||
FileSymbols FS(IndexContents::All);
|
||||
FileSymbols FS(IndexContents::All, true);
|
||||
auto OneSymboSlab = [](Symbol Sym) {
|
||||
SymbolSlab::Builder S;
|
||||
S.insert(Sym);
|
||||
@ -147,7 +147,7 @@ TEST(FileSymbolsTest, MergeOverlap) {
|
||||
}
|
||||
|
||||
TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
|
||||
FileSymbols FS(IndexContents::All);
|
||||
FileSymbols FS(IndexContents::All, true);
|
||||
|
||||
SymbolID ID("1");
|
||||
FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc"), nullptr, false);
|
||||
@ -180,14 +180,14 @@ void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, CustomizedURIScheme) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
update(M, "f", "class string {};");
|
||||
|
||||
EXPECT_THAT(runFuzzyFind(M, ""), ElementsAre(declURI("unittest:///f.h")));
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, IndexAST) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
update(M, "f1", "namespace ns { void f() {} class X {}; }");
|
||||
|
||||
FuzzyFindRequest Req;
|
||||
@ -198,7 +198,7 @@ TEST(FileIndexTest, IndexAST) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, NoLocal) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
update(M, "f1", "namespace ns { void f() { int local = 0; } class X {}; }");
|
||||
|
||||
EXPECT_THAT(
|
||||
@ -207,7 +207,7 @@ TEST(FileIndexTest, NoLocal) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
update(M, "f1", "namespace ns { void f() {} class X {}; }");
|
||||
update(M, "f2", "namespace ns { void ff() {} class X {}; }");
|
||||
|
||||
@ -219,7 +219,7 @@ TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, ClassMembers) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
update(M, "f1", "class X { static int m1; int m2; static void f(); };");
|
||||
|
||||
EXPECT_THAT(runFuzzyFind(M, ""),
|
||||
@ -228,7 +228,7 @@ TEST(FileIndexTest, ClassMembers) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, IncludeCollected) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
update(
|
||||
M, "f",
|
||||
"// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
|
||||
@ -240,7 +240,7 @@ TEST(FileIndexTest, IncludeCollected) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, IWYUPragmaExport) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
|
||||
TestTU File;
|
||||
File.Code = R"cpp(#pragma once
|
||||
@ -286,7 +286,7 @@ template <class Ty, class Arg>
|
||||
vector<Ty> make_vector(Arg A) {}
|
||||
)cpp";
|
||||
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
update(M, "f", Source);
|
||||
|
||||
auto Symbols = runFuzzyFind(M, "");
|
||||
@ -334,7 +334,7 @@ TEST(FileIndexTest, RebuildWithPreamble) {
|
||||
IgnoreDiagnostics IgnoreDiags;
|
||||
auto CI = buildCompilerInvocation(PI, IgnoreDiags);
|
||||
|
||||
FileIndex Index;
|
||||
FileIndex Index(true);
|
||||
bool IndexUpdated = false;
|
||||
buildPreamble(
|
||||
FooCpp, *CI, PI,
|
||||
@ -374,7 +374,7 @@ TEST(FileIndexTest, Refs) {
|
||||
RefsRequest Request;
|
||||
Request.IDs = {Foo.ID};
|
||||
|
||||
FileIndex Index;
|
||||
FileIndex Index(true);
|
||||
// Add test.cc
|
||||
TestTU Test;
|
||||
Test.HeaderCode = HeaderCode;
|
||||
@ -409,7 +409,7 @@ TEST(FileIndexTest, MacroRefs) {
|
||||
}
|
||||
)cpp");
|
||||
|
||||
FileIndex Index;
|
||||
FileIndex Index(true);
|
||||
// Add test.cc
|
||||
TestTU Test;
|
||||
Test.HeaderCode = std::string(HeaderCode.code());
|
||||
@ -432,7 +432,7 @@ TEST(FileIndexTest, MacroRefs) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, CollectMacros) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
update(M, "f", "#define CLANGD 1");
|
||||
EXPECT_THAT(runFuzzyFind(M, ""), Contains(qName("CLANGD")));
|
||||
}
|
||||
@ -443,7 +443,7 @@ TEST(FileIndexTest, Relations) {
|
||||
TU.HeaderFilename = "f.h";
|
||||
TU.HeaderCode = "class A {}; class B : public A {};";
|
||||
auto AST = TU.build();
|
||||
FileIndex Index;
|
||||
FileIndex Index(true);
|
||||
Index.updatePreamble(testPath(TU.Filename), /*Version=*/"null",
|
||||
AST.getASTContext(), AST.getPreprocessor(),
|
||||
AST.getPragmaIncludes());
|
||||
@ -493,7 +493,7 @@ TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
|
||||
)cpp");
|
||||
TU.Code = std::string(Main.code());
|
||||
auto AST = TU.build();
|
||||
FileIndex Index;
|
||||
FileIndex Index(true);
|
||||
Index.updateMain(testPath(TU.Filename), AST);
|
||||
|
||||
// Expect to see references in main file, references in headers are excluded
|
||||
@ -510,7 +510,7 @@ TEST(FileIndexTest, MergeMainFileSymbols) {
|
||||
Cpp.HeaderFilename = "foo.h";
|
||||
Cpp.HeaderCode = CommonHeader;
|
||||
|
||||
FileIndex Index;
|
||||
FileIndex Index(true);
|
||||
auto HeaderAST = Header.build();
|
||||
auto CppAST = Cpp.build();
|
||||
Index.updateMain(testPath("foo.h"), HeaderAST);
|
||||
@ -524,7 +524,7 @@ TEST(FileIndexTest, MergeMainFileSymbols) {
|
||||
}
|
||||
|
||||
TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
|
||||
FileSymbols FS(IndexContents::All);
|
||||
FileSymbols FS(IndexContents::All, true);
|
||||
FS.update("f1", numSlab(1, 3), nullptr, nullptr, true);
|
||||
FS.update("f2", numSlab(1, 3), nullptr, nullptr, false);
|
||||
EXPECT_THAT(
|
||||
@ -536,7 +536,7 @@ TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
|
||||
}
|
||||
|
||||
TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
|
||||
FileSymbols FS(IndexContents::All);
|
||||
FileSymbols FS(IndexContents::All, true);
|
||||
FS.update("f1cpp", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cpp"), nullptr,
|
||||
true);
|
||||
FS.update("f1h", numSlab(1, 3), refSlab(SymbolID("1"), "f1.h"), nullptr,
|
||||
@ -558,7 +558,7 @@ TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
TestTU File;
|
||||
File.HeaderFilename = "a.h";
|
||||
|
||||
@ -581,7 +581,7 @@ TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
|
||||
|
||||
// Verifies that concurrent calls to updateMain don't "lose" any updates.
|
||||
TEST(FileIndexTest, Threadsafety) {
|
||||
FileIndex M;
|
||||
FileIndex M(true);
|
||||
Notification Go;
|
||||
|
||||
constexpr int Count = 10;
|
||||
@ -714,7 +714,7 @@ TEST(FileShardedIndexTest, Sharding) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, Profile) {
|
||||
FileIndex FI;
|
||||
FileIndex FI(true);
|
||||
|
||||
auto FileName = testPath("foo.cpp");
|
||||
auto AST = TestTU::withHeaderCode("int a;").build();
|
||||
@ -738,7 +738,7 @@ TEST(FileIndexTest, Profile) {
|
||||
}
|
||||
|
||||
TEST(FileSymbolsTest, Profile) {
|
||||
FileSymbols FS(IndexContents::All);
|
||||
FileSymbols FS(IndexContents::All, true);
|
||||
FS.update("f1", numSlab(1, 2), nullptr, nullptr, false);
|
||||
FS.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false);
|
||||
FS.update("f3", nullptr, nullptr,
|
||||
@ -758,7 +758,7 @@ TEST(FileSymbolsTest, Profile) {
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, MacrosFromMainFile) {
|
||||
FileIndex Idx;
|
||||
FileIndex Idx(true);
|
||||
TestTU TU;
|
||||
TU.Code = "#pragma once\n#define FOO";
|
||||
TU.Filename = "foo.h";
|
||||
|
@ -292,7 +292,7 @@ TEST(MergeIndexTest, Lookup) {
|
||||
}
|
||||
|
||||
TEST(MergeIndexTest, LookupRemovedDefinition) {
|
||||
FileIndex DynamicIndex, StaticIndex;
|
||||
FileIndex DynamicIndex(true), StaticIndex(true);
|
||||
MergedIndex Merge(&DynamicIndex, &StaticIndex);
|
||||
|
||||
const char *HeaderCode = "class Foo;";
|
||||
@ -349,7 +349,7 @@ TEST(MergeIndexTest, FuzzyFind) {
|
||||
}
|
||||
|
||||
TEST(MergeIndexTest, FuzzyFindRemovedSymbol) {
|
||||
FileIndex DynamicIndex, StaticIndex;
|
||||
FileIndex DynamicIndex(true), StaticIndex(true);
|
||||
MergedIndex Merge(&DynamicIndex, &StaticIndex);
|
||||
|
||||
const char *HeaderCode = "class Foo;";
|
||||
@ -446,8 +446,8 @@ TEST(MergeTest, PreferSymbolLocationInCodegenFile) {
|
||||
}
|
||||
|
||||
TEST(MergeIndexTest, Refs) {
|
||||
FileIndex Dyn;
|
||||
FileIndex StaticIndex;
|
||||
FileIndex Dyn(true);
|
||||
FileIndex StaticIndex(true);
|
||||
MergedIndex Merge(&Dyn, &StaticIndex);
|
||||
|
||||
const char *HeaderCode = "class Foo;";
|
||||
|
@ -1548,7 +1548,7 @@ TEST(CrossFileRenameTests, DirtyBuffer) {
|
||||
std::string BarPath = testPath("bar.cc");
|
||||
// Build the index, the index has "Foo" references from foo.cc and "Bar"
|
||||
// references from bar.cc.
|
||||
FileSymbols FSymbols(IndexContents::All);
|
||||
FileSymbols FSymbols(IndexContents::All, true);
|
||||
FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
|
||||
nullptr, false);
|
||||
FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
|
||||
@ -1601,6 +1601,12 @@ TEST(CrossFileRenameTests, DirtyBuffer) {
|
||||
return true; // has more references
|
||||
}
|
||||
|
||||
bool containedRefs(const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>
|
||||
Callback) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fuzzyFind(
|
||||
const FuzzyFindRequest &Req,
|
||||
llvm::function_ref<void(const Symbol &)> Callback) const override {
|
||||
@ -1652,6 +1658,12 @@ TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool containedRefs(const ContainedRefsRequest &Req,
|
||||
llvm::function_ref<void(const ContainedRefsResult &)>
|
||||
Callback) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fuzzyFind(const FuzzyFindRequest &,
|
||||
llvm::function_ref<void(const Symbol &)>) const override {
|
||||
return false;
|
||||
|
@ -174,7 +174,7 @@ RefSlab TestTU::headerRefs() const {
|
||||
|
||||
std::unique_ptr<SymbolIndex> TestTU::index() const {
|
||||
auto AST = build();
|
||||
auto Idx = std::make_unique<FileIndex>();
|
||||
auto Idx = std::make_unique<FileIndex>(/*SupportContainedRefs=*/true);
|
||||
Idx->updatePreamble(testPath(Filename), /*Version=*/"null",
|
||||
AST.getASTContext(), AST.getPreprocessor(),
|
||||
AST.getPragmaIncludes());
|
||||
|
@ -17,7 +17,7 @@ namespace clang {
|
||||
namespace clangd {
|
||||
|
||||
std::unique_ptr<SymbolIndex> TestWorkspace::index() {
|
||||
auto Index = std::make_unique<FileIndex>();
|
||||
auto Index = std::make_unique<FileIndex>(/*SupportContainedRefs=*/true);
|
||||
for (const auto &Input : Inputs) {
|
||||
if (!Input.second.IsMainFile)
|
||||
continue;
|
||||
|
Loading…
x
Reference in New Issue
Block a user