2018-08-15 00:03:32 +08:00
|
|
|
//===--- Merge.cpp -----------------------------------------------*- C++-*-===//
|
2018-01-15 20:33:00 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-01-15 20:33:00 +08:00
|
|
|
//
|
2018-08-15 00:03:32 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-01-15 20:33:00 +08:00
|
|
|
#include "Merge.h"
|
2019-02-28 21:23:03 +08:00
|
|
|
#include "index/Symbol.h"
|
2019-02-28 19:02:01 +08:00
|
|
|
#include "index/SymbolLocation.h"
|
2019-02-28 20:31:49 +08:00
|
|
|
#include "index/SymbolOrigin.h"
|
[clangd] Move non-clang base pieces into separate support/ lib. NFCI
Summary:
This enforces layering, reduces a sprawling clangd/ directory, and makes life
easier for embedders.
Reviewers: kbobyrev
Subscribers: mgorny, ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, jfb, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D79014
2020-04-28 23:49:17 +08:00
|
|
|
#include "support/Logger.h"
|
|
|
|
#include "support/Trace.h"
|
2018-01-15 20:33:00 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2019-02-11 23:05:29 +08:00
|
|
|
#include "llvm/ADT/StringRef.h"
|
[clangd] SymbolOccurrences -> Refs and cleanup
Summary:
A few things that I noticed while merging the SwapIndex patch:
- SymbolOccurrences and particularly SymbolOccurrenceSlab are unwieldy names,
and these names appear *a lot*. Ref, RefSlab, etc seem clear enough
and read/format much better.
- The asymmetry between SymbolSlab and RefSlab (build() vs freeze()) is
confusing and irritating, and doesn't even save much code.
Avoiding RefSlab::Builder was my idea, but it was a bad one; add it.
- DenseMap<SymbolID, ArrayRef<Ref>> seems like a reasonable compromise for
constructing MemIndex - and means many less wasted allocations than the
current DenseMap<SymbolID, vector<Ref*>> for FileIndex, and none for
slabs.
- RefSlab::find() is not actually used for anything, so we can throw
away the DenseMap and keep the representation much more compact.
- A few naming/consistency fixes: e.g. Slabs,Refs -> Symbols,Refs.
Reviewers: ioeric
Subscribers: ilya-biryukov, MaskRay, jkorous, mgrang, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51605
llvm-svn: 341368
2018-09-04 22:39:56 +08:00
|
|
|
#include "llvm/ADT/StringSet.h"
|
2018-01-15 20:33:00 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2019-02-11 23:05:29 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
2018-08-15 00:03:32 +08:00
|
|
|
|
2018-01-15 20:33:00 +08:00
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
2018-08-15 00:03:32 +08:00
|
|
|
|
2018-10-04 22:20:22 +08:00
|
|
|
// FIXME: Deleted symbols in dirty files are still returned (from Static).
|
|
|
|
// To identify these eliminate these, we should:
|
|
|
|
// - find the generating file from each Symbol which is Static-only
|
|
|
|
// - ask Dynamic if it has that file (needs new SymbolIndex method)
|
|
|
|
// - if so, drop the Symbol.
|
2019-01-07 23:45:19 +08:00
|
|
|
bool MergedIndex::fuzzyFind(
|
|
|
|
const FuzzyFindRequest &Req,
|
|
|
|
llvm::function_ref<void(const Symbol &)> Callback) const {
|
2018-10-04 22:20:22 +08:00
|
|
|
// We can't step through both sources in parallel. So:
|
|
|
|
// 1) query all dynamic symbols, slurping results into a slab
|
|
|
|
// 2) query the static symbols, for each one:
|
|
|
|
// a) if it's not in the dynamic slab, yield it directly
|
|
|
|
// b) if it's in the dynamic slab, merge it and yield the result
|
|
|
|
// 3) now yield all the dynamic symbols we haven't processed.
|
|
|
|
trace::Span Tracer("MergedIndex fuzzyFind");
|
|
|
|
bool More = false; // We'll be incomplete if either source was.
|
|
|
|
SymbolSlab::Builder DynB;
|
|
|
|
unsigned DynamicCount = 0;
|
|
|
|
unsigned StaticCount = 0;
|
|
|
|
unsigned MergedCount = 0;
|
|
|
|
More |= Dynamic->fuzzyFind(Req, [&](const Symbol &S) {
|
|
|
|
++DynamicCount;
|
|
|
|
DynB.insert(S);
|
|
|
|
});
|
|
|
|
SymbolSlab Dyn = std::move(DynB).build();
|
|
|
|
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::DenseSet<SymbolID> SeenDynamicSymbols;
|
2018-10-04 22:20:22 +08:00
|
|
|
More |= Static->fuzzyFind(Req, [&](const Symbol &S) {
|
|
|
|
auto DynS = Dyn.find(S.ID);
|
|
|
|
++StaticCount;
|
|
|
|
if (DynS == Dyn.end())
|
|
|
|
return Callback(S);
|
|
|
|
++MergedCount;
|
|
|
|
SeenDynamicSymbols.insert(S.ID);
|
|
|
|
Callback(mergeSymbol(*DynS, S));
|
|
|
|
});
|
|
|
|
SPAN_ATTACH(Tracer, "dynamic", DynamicCount);
|
|
|
|
SPAN_ATTACH(Tracer, "static", StaticCount);
|
|
|
|
SPAN_ATTACH(Tracer, "merged", MergedCount);
|
|
|
|
for (const Symbol &S : Dyn)
|
|
|
|
if (!SeenDynamicSymbols.count(S.ID))
|
|
|
|
Callback(S);
|
|
|
|
return More;
|
|
|
|
}
|
2018-03-14 17:48:05 +08:00
|
|
|
|
2019-01-07 23:45:19 +08:00
|
|
|
void MergedIndex::lookup(
|
|
|
|
const LookupRequest &Req,
|
|
|
|
llvm::function_ref<void(const Symbol &)> Callback) const {
|
2018-10-04 22:20:22 +08:00
|
|
|
trace::Span Tracer("MergedIndex lookup");
|
|
|
|
SymbolSlab::Builder B;
|
|
|
|
|
|
|
|
Dynamic->lookup(Req, [&](const Symbol &S) { B.insert(S); });
|
|
|
|
|
|
|
|
auto RemainingIDs = Req.IDs;
|
|
|
|
Static->lookup(Req, [&](const Symbol &S) {
|
|
|
|
const Symbol *Sym = B.find(S.ID);
|
|
|
|
RemainingIDs.erase(S.ID);
|
|
|
|
if (!Sym)
|
|
|
|
Callback(S);
|
|
|
|
else
|
|
|
|
Callback(mergeSymbol(*Sym, S));
|
|
|
|
});
|
|
|
|
for (const auto &ID : RemainingIDs)
|
|
|
|
if (const Symbol *Sym = B.find(ID))
|
|
|
|
Callback(*Sym);
|
|
|
|
}
|
2018-03-14 17:48:05 +08:00
|
|
|
|
2019-11-13 21:42:26 +08:00
|
|
|
bool MergedIndex::refs(const RefsRequest &Req,
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::function_ref<void(const Ref &)> Callback) const {
|
2018-10-04 22:20:22 +08:00
|
|
|
trace::Span Tracer("MergedIndex refs");
|
2019-11-13 21:42:26 +08:00
|
|
|
bool More = false;
|
2019-01-15 02:11:09 +08:00
|
|
|
uint32_t Remaining =
|
|
|
|
Req.Limit.getValueOr(std::numeric_limits<uint32_t>::max());
|
2018-10-04 22:20:22 +08:00
|
|
|
// We don't want duplicated refs from the static/dynamic indexes,
|
2019-06-15 10:26:47 +08:00
|
|
|
// and we can't reliably deduplicate them because offsets may differ slightly.
|
2018-10-04 22:20:22 +08:00
|
|
|
// We consider the dynamic index authoritative and report all its refs,
|
|
|
|
// and only report static index refs from other files.
|
|
|
|
//
|
|
|
|
// FIXME: The heuristic fails if the dynamic index contains a file, but all
|
|
|
|
// refs were removed (we will report stale ones from the static index).
|
|
|
|
// Ultimately we should explicit check which index has the file instead.
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::StringSet<> DynamicIndexFileURIs;
|
2019-11-13 21:42:26 +08:00
|
|
|
More |= Dynamic->refs(Req, [&](const Ref &O) {
|
2018-10-04 22:20:22 +08:00
|
|
|
DynamicIndexFileURIs.insert(O.Location.FileURI);
|
|
|
|
Callback(O);
|
2019-11-14 21:43:07 +08:00
|
|
|
assert(Remaining != 0);
|
2019-01-15 02:11:09 +08:00
|
|
|
--Remaining;
|
2018-10-04 22:20:22 +08:00
|
|
|
});
|
2019-11-13 21:42:26 +08:00
|
|
|
if (Remaining == 0 && More)
|
|
|
|
return More;
|
2019-01-15 02:11:09 +08:00
|
|
|
// We return less than Req.Limit if static index returns more refs for dirty
|
|
|
|
// files.
|
2019-11-14 21:43:07 +08:00
|
|
|
bool StaticHadMore = Static->refs(Req, [&](const Ref &O) {
|
2019-11-13 21:42:26 +08:00
|
|
|
if (DynamicIndexFileURIs.count(O.Location.FileURI))
|
|
|
|
return; // ignore refs that have been seen from dynamic index.
|
2019-11-14 21:43:07 +08:00
|
|
|
if (Remaining == 0) {
|
2019-11-13 21:42:26 +08:00
|
|
|
More = true;
|
2019-11-14 21:43:07 +08:00
|
|
|
return;
|
2019-01-15 02:11:09 +08:00
|
|
|
}
|
2019-11-14 21:43:07 +08:00
|
|
|
--Remaining;
|
|
|
|
Callback(O);
|
2018-10-04 22:20:22 +08:00
|
|
|
});
|
2019-11-14 21:43:07 +08:00
|
|
|
return More || StaticHadMore;
|
2018-10-04 22:20:22 +08:00
|
|
|
}
|
2018-01-15 20:33:00 +08:00
|
|
|
|
2019-06-15 10:26:47 +08:00
|
|
|
void MergedIndex::relations(
|
|
|
|
const RelationsRequest &Req,
|
|
|
|
llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
|
|
|
|
uint32_t Remaining =
|
|
|
|
Req.Limit.getValueOr(std::numeric_limits<uint32_t>::max());
|
|
|
|
// Return results from both indexes but avoid duplicates.
|
|
|
|
// We might return stale relations from the static index;
|
|
|
|
// we don't currently have a good way of identifying them.
|
|
|
|
llvm::DenseSet<std::pair<SymbolID, SymbolID>> SeenRelations;
|
|
|
|
Dynamic->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
|
|
|
|
Callback(Subject, Object);
|
|
|
|
SeenRelations.insert(std::make_pair(Subject, Object.ID));
|
|
|
|
--Remaining;
|
|
|
|
});
|
|
|
|
if (Remaining == 0)
|
|
|
|
return;
|
|
|
|
Static->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
|
|
|
|
if (Remaining > 0 &&
|
|
|
|
!SeenRelations.count(std::make_pair(Subject, Object.ID))) {
|
|
|
|
--Remaining;
|
|
|
|
Callback(Subject, Object);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-02-11 23:05:29 +08:00
|
|
|
// Returns true if \p L is (strictly) preferred to \p R (e.g. by file paths). If
|
|
|
|
// neither is preferred, this returns false.
|
|
|
|
bool prefer(const SymbolLocation &L, const SymbolLocation &R) {
|
|
|
|
if (!L)
|
|
|
|
return false;
|
|
|
|
if (!R)
|
|
|
|
return true;
|
|
|
|
auto HasCodeGenSuffix = [](const SymbolLocation &Loc) {
|
|
|
|
constexpr static const char *CodegenSuffixes[] = {".proto"};
|
|
|
|
return std::any_of(std::begin(CodegenSuffixes), std::end(CodegenSuffixes),
|
|
|
|
[&](llvm::StringRef Suffix) {
|
|
|
|
return llvm::StringRef(Loc.FileURI).endswith(Suffix);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
return HasCodeGenSuffix(L) && !HasCodeGenSuffix(R);
|
|
|
|
}
|
|
|
|
|
2018-08-31 21:55:01 +08:00
|
|
|
Symbol mergeSymbol(const Symbol &L, const Symbol &R) {
|
2018-01-15 20:33:00 +08:00
|
|
|
assert(L.ID == R.ID);
|
2018-02-09 22:42:01 +08:00
|
|
|
// We prefer information from TUs that saw the definition.
|
|
|
|
// Classes: this is the def itself. Functions: hopefully the header decl.
|
|
|
|
// If both did (or both didn't), continue to prefer L over R.
|
|
|
|
bool PreferR = R.Definition && !L.Definition;
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 18:18:21 +08:00
|
|
|
// Merge include headers only if both have definitions or both have no
|
|
|
|
// definition; otherwise, only accumulate references of common includes.
|
2018-11-14 19:55:45 +08:00
|
|
|
assert(L.Definition.FileURI && R.Definition.FileURI);
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 18:18:21 +08:00
|
|
|
bool MergeIncludes =
|
2018-11-14 19:55:45 +08:00
|
|
|
bool(*L.Definition.FileURI) == bool(*R.Definition.FileURI);
|
2018-02-09 22:42:01 +08:00
|
|
|
Symbol S = PreferR ? R : L; // The target symbol we're merging into.
|
|
|
|
const Symbol &O = PreferR ? L : R; // The "other" less-preferred symbol.
|
|
|
|
|
2019-02-11 23:05:29 +08:00
|
|
|
// Only use locations in \p O if it's (strictly) preferred.
|
|
|
|
if (prefer(O.CanonicalDeclaration, S.CanonicalDeclaration))
|
2018-02-09 22:42:01 +08:00
|
|
|
S.CanonicalDeclaration = O.CanonicalDeclaration;
|
2019-02-11 23:05:29 +08:00
|
|
|
if (prefer(O.Definition, S.Definition))
|
|
|
|
S.Definition = O.Definition;
|
2018-03-12 22:49:09 +08:00
|
|
|
S.References += O.References;
|
2018-06-23 00:11:35 +08:00
|
|
|
if (S.Signature == "")
|
|
|
|
S.Signature = O.Signature;
|
|
|
|
if (S.CompletionSnippetSuffix == "")
|
|
|
|
S.CompletionSnippetSuffix = O.CompletionSnippetSuffix;
|
2019-11-11 17:34:29 +08:00
|
|
|
if (S.Documentation == "") {
|
|
|
|
// Don't accept documentation from bare forward class declarations, if there
|
|
|
|
// is a definition and it didn't provide one. S is often an undocumented
|
|
|
|
// class, and O is a non-canonical forward decl preceded by an irrelevant
|
|
|
|
// comment.
|
|
|
|
bool IsClass = S.SymInfo.Kind == index::SymbolKind::Class ||
|
|
|
|
S.SymInfo.Kind == index::SymbolKind::Struct ||
|
|
|
|
S.SymInfo.Kind == index::SymbolKind::Union;
|
|
|
|
if (!IsClass || !S.Definition)
|
|
|
|
S.Documentation = O.Documentation;
|
|
|
|
}
|
2018-08-31 21:55:01 +08:00
|
|
|
if (S.ReturnType == "")
|
|
|
|
S.ReturnType = O.ReturnType;
|
2018-12-20 21:05:46 +08:00
|
|
|
if (S.Type == "")
|
|
|
|
S.Type = O.Type;
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 18:18:21 +08:00
|
|
|
for (const auto &OI : O.IncludeHeaders) {
|
|
|
|
bool Found = false;
|
|
|
|
for (auto &SI : S.IncludeHeaders) {
|
|
|
|
if (SI.IncludeHeader == OI.IncludeHeader) {
|
|
|
|
Found = true;
|
|
|
|
SI.References += OI.References;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!Found && MergeIncludes)
|
|
|
|
S.IncludeHeaders.emplace_back(OI.IncludeHeader, OI.References);
|
|
|
|
}
|
2018-07-05 14:20:41 +08:00
|
|
|
|
2018-07-06 19:50:49 +08:00
|
|
|
S.Origin |= O.Origin | SymbolOrigin::Merge;
|
2018-09-07 02:52:26 +08:00
|
|
|
S.Flags |= O.Flags;
|
2018-01-15 20:33:00 +08:00
|
|
|
return S;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|