[clang-tools-extra] r341242 - [clangd] Implement findOccurrences interface in dynamic index.
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 31 12:53:38 PDT 2018
Author: hokein
Date: Fri Aug 31 12:53:37 2018
New Revision: 341242
URL: http://llvm.org/viewvc/llvm-project?rev=341242&view=rev
Log:
[clangd] Implement findOccurrences interface in dynamic index.
Summary:
Implement the interface in
- FileIndex
- MemIndex
- MergeIndex
Depends on https://reviews.llvm.org/D50385.
Reviewers: sammccall, ilya-biryukov
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51279
Modified:
clang-tools-extra/trunk/clangd/index/FileIndex.cpp
clang-tools-extra/trunk/clangd/index/FileIndex.h
clang-tools-extra/trunk/clangd/index/Index.h
clang-tools-extra/trunk/clangd/index/MemIndex.cpp
clang-tools-extra/trunk/clangd/index/MemIndex.h
clang-tools-extra/trunk/clangd/index/Merge.cpp
clang-tools-extra/trunk/clangd/index/Merge.h
clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp
clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp
clang-tools-extra/trunk/unittests/clangd/TestTU.cpp
Modified: clang-tools-extra/trunk/clangd/index/FileIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/FileIndex.cpp?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/FileIndex.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/FileIndex.cpp Fri Aug 31 12:53:37 2018
@@ -16,9 +16,10 @@
namespace clang {
namespace clangd {
-SymbolSlab indexAST(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
- llvm::Optional<llvm::ArrayRef<Decl *>> TopLevelDecls,
- llvm::ArrayRef<std::string> URISchemes) {
+std::pair<SymbolSlab, SymbolOccurrenceSlab>
+indexAST(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
+ llvm::Optional<llvm::ArrayRef<Decl *>> TopLevelDecls,
+ llvm::ArrayRef<std::string> URISchemes) {
SymbolCollector::Options CollectorOpts;
// FIXME(ioeric): we might also want to collect include headers. We would need
// to make sure all includes are canonicalized (with CanonicalIncludes), which
@@ -31,8 +32,6 @@ SymbolSlab indexAST(ASTContext &AST, std
CollectorOpts.URISchemes = URISchemes;
CollectorOpts.Origin = SymbolOrigin::Dynamic;
- SymbolCollector Collector(std::move(CollectorOpts));
- Collector.setPreprocessor(PP);
index::IndexingOptions IndexOpts;
// We only need declarations, because we don't count references.
IndexOpts.SystemSymbolFilter =
@@ -46,20 +45,45 @@ SymbolSlab indexAST(ASTContext &AST, std
DeclsToIndex.assign(AST.getTranslationUnitDecl()->decls().begin(),
AST.getTranslationUnitDecl()->decls().end());
+ // We only collect occurrences when indexing main AST.
+ // FIXME: this is a hacky way to detect whether we are indexing preamble AST
+ // or main AST, we should make it explicitly.
+ bool IsIndexMainAST = TopLevelDecls.hasValue();
+ if (IsIndexMainAST)
+ CollectorOpts.OccurrenceFilter = AllOccurrenceKinds;
+
+ SymbolCollector Collector(std::move(CollectorOpts));
+ Collector.setPreprocessor(PP);
index::indexTopLevelDecls(AST, DeclsToIndex, Collector, IndexOpts);
- return Collector.takeSymbols();
+ const auto &SM = AST.getSourceManager();
+ const auto *MainFileEntry = SM.getFileEntryForID(SM.getMainFileID());
+ std::string FileName = MainFileEntry ? MainFileEntry->getName() : "";
+
+ auto Syms = Collector.takeSymbols();
+ auto Occurrences = Collector.takeOccurrences();
+ vlog("index {0}AST for {1}: \n"
+ " symbol slab: {2} symbols, {3} bytes\n"
+ " occurrence slab: {4} symbols, {5} bytes",
+ IsIndexMainAST ? "Main" : "Preamble", FileName, Syms.size(),
+ Syms.bytes(), Occurrences.size(), Occurrences.bytes());
+ return {std::move(Syms), std::move(Occurrences)};
}
FileIndex::FileIndex(std::vector<std::string> URISchemes)
: URISchemes(std::move(URISchemes)) {}
-void FileSymbols::update(PathRef Path, std::unique_ptr<SymbolSlab> Slab) {
+void FileSymbols::update(PathRef Path, std::unique_ptr<SymbolSlab> Slab,
+ std::unique_ptr<SymbolOccurrenceSlab> Occurrences) {
std::lock_guard<std::mutex> Lock(Mutex);
if (!Slab)
FileToSlabs.erase(Path);
else
FileToSlabs[Path] = std::move(Slab);
+ if (!Occurrences)
+ FileToOccurrenceSlabs.erase(Path);
+ else
+ FileToOccurrenceSlabs[Path] = std::move(Occurrences);
}
std::shared_ptr<std::vector<const Symbol *>> FileSymbols::allSymbols() {
@@ -85,19 +109,47 @@ std::shared_ptr<std::vector<const Symbol
return {std::move(Snap), Pointers};
}
+std::shared_ptr<MemIndex::OccurrenceMap> FileSymbols::allOccurrences() const {
+ // The snapshot manages life time of symbol occurrence slabs and provides
+ // pointers to all occurrences in all occurrence slabs.
+ struct Snapshot {
+ MemIndex::OccurrenceMap Occurrences; // ID => {Occurrence}
+ std::vector<std::shared_ptr<SymbolOccurrenceSlab>> KeepAlive;
+ };
+
+ auto Snap = std::make_shared<Snapshot>();
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ for (const auto &FileAndSlab : FileToOccurrenceSlabs) {
+ Snap->KeepAlive.push_back(FileAndSlab.second);
+ for (const auto &IDAndOccurrences : *FileAndSlab.second) {
+ auto &Occurrences = Snap->Occurrences[IDAndOccurrences.first];
+ for (const auto &Occurrence : IDAndOccurrences.second)
+ Occurrences.push_back(&Occurrence);
+ }
+ }
+ }
+
+ return {std::move(Snap), &Snap->Occurrences};
+}
+
void FileIndex::update(PathRef Path, ASTContext *AST,
std::shared_ptr<Preprocessor> PP,
llvm::Optional<llvm::ArrayRef<Decl *>> TopLevelDecls) {
if (!AST) {
- FSymbols.update(Path, nullptr);
+ FSymbols.update(Path, nullptr, nullptr);
} else {
assert(PP);
auto Slab = llvm::make_unique<SymbolSlab>();
- *Slab = indexAST(*AST, PP, TopLevelDecls, URISchemes);
- FSymbols.update(Path, std::move(Slab));
+ auto OccurrenceSlab = llvm::make_unique<SymbolOccurrenceSlab>();
+ auto IndexResults = indexAST(*AST, PP, TopLevelDecls, URISchemes);
+ std::tie(*Slab, *OccurrenceSlab) =
+ indexAST(*AST, PP, TopLevelDecls, URISchemes);
+ FSymbols.update(Path, std::move(Slab), std::move(OccurrenceSlab));
}
auto Symbols = FSymbols.allSymbols();
- Index.build(std::move(Symbols));
+ Index.build(std::move(Symbols), FSymbols.allOccurrences());
}
bool FileIndex::fuzzyFind(
@@ -115,7 +167,7 @@ void FileIndex::lookup(
void FileIndex::findOccurrences(
const OccurrencesRequest &Req,
llvm::function_ref<void(const SymbolOccurrence &)> Callback) const {
- log("findOccurrences is not implemented.");
+ Index.findOccurrences(Req, Callback);
}
size_t FileIndex::estimateMemoryUsage() const {
Modified: clang-tools-extra/trunk/clangd/index/FileIndex.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/FileIndex.h?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/FileIndex.h (original)
+++ clang-tools-extra/trunk/clangd/index/FileIndex.h Fri Aug 31 12:53:37 2018
@@ -39,18 +39,25 @@ namespace clangd {
/// locking when we swap or obtain refereces to snapshots.
class FileSymbols {
public:
- /// \brief Updates all symbols in a file. If \p Slab is nullptr, symbols for
- /// \p Path will be removed.
- void update(PathRef Path, std::unique_ptr<SymbolSlab> Slab);
+ /// \brief Updates all symbols and occurrences in a file.
+ /// If \p Slab (Occurrence) is nullptr, symbols (occurrences) for \p Path
+ /// will be removed.
+ void update(PathRef Path, std::unique_ptr<SymbolSlab> Slab,
+ std::unique_ptr<SymbolOccurrenceSlab> Occurrences);
// The shared_ptr keeps the symbols alive
std::shared_ptr<std::vector<const Symbol *>> allSymbols();
+ /// Returns all symbol occurrences for all active files.
+ std::shared_ptr<MemIndex::OccurrenceMap> allOccurrences() const;
+
private:
mutable std::mutex Mutex;
/// \brief Stores the latest snapshots for all active files.
llvm::StringMap<std::shared_ptr<SymbolSlab>> FileToSlabs;
+ /// Stores the latest occurrence slabs for all active files.
+ llvm::StringMap<std::shared_ptr<SymbolOccurrenceSlab>> FileToOccurrenceSlabs;
};
/// \brief This manages symbls from files and an in-memory index on all symbols.
@@ -90,12 +97,12 @@ private:
std::vector<std::string> URISchemes;
};
-/// Retrieves namespace and class level symbols in \p AST.
+/// Retrieves symbols and symbol occurrences in \p AST.
/// Exposed to assist in unit tests.
/// If URISchemes is empty, the default schemes in SymbolCollector will be used.
/// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all top
/// level decls obtained from \p AST are indexed.
-SymbolSlab
+std::pair<SymbolSlab, SymbolOccurrenceSlab>
indexAST(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
llvm::Optional<llvm::ArrayRef<Decl *>> TopLevelDecls = llvm::None,
llvm::ArrayRef<std::string> URISchemes = {});
Modified: clang-tools-extra/trunk/clangd/index/Index.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/Index.h?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/Index.h (original)
+++ clang-tools-extra/trunk/clangd/index/Index.h Fri Aug 31 12:53:37 2018
@@ -344,6 +344,12 @@ public:
SymbolOccurrenceSlab() : UniqueStrings(Arena) {}
+ static SymbolOccurrenceSlab createEmpty() {
+ SymbolOccurrenceSlab Empty;
+ Empty.freeze();
+ return Empty;
+ }
+
// Define move semantics for the slab, allowing assignment from an rvalue.
// Implicit move assignment is deleted by the compiler because
// StringSaver has a reference type member.
@@ -360,6 +366,12 @@ public:
const_iterator begin() const { return Occurrences.begin(); }
const_iterator end() const { return Occurrences.end(); }
+ size_t bytes() const {
+ return sizeof(*this) + Arena.getTotalMemory() + Occurrences.getMemorySize();
+ }
+
+ size_t size() const { return Occurrences.size(); }
+
// Adds a symbol occurrence.
// This is a deep copy: underlying FileURI will be owned by the slab.
void insert(const SymbolID &SymID, const SymbolOccurrence &Occurrence);
Modified: clang-tools-extra/trunk/clangd/index/MemIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/MemIndex.cpp?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/MemIndex.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/MemIndex.cpp Fri Aug 31 12:53:37 2018
@@ -15,7 +15,27 @@
namespace clang {
namespace clangd {
-void MemIndex::build(std::shared_ptr<std::vector<const Symbol *>> Syms) {
+static std::shared_ptr<MemIndex::OccurrenceMap>
+getOccurrencesFromSlab(SymbolOccurrenceSlab OccurrencesSlab) {
+ struct Snapshot {
+ SymbolOccurrenceSlab Slab;
+ MemIndex::OccurrenceMap Occurrences;
+ };
+
+ auto Snap = std::make_shared<Snapshot>();
+ Snap->Slab = std::move(OccurrencesSlab);
+ for (const auto &IDAndOccurrences : Snap->Slab) {
+ auto &Occurrences = Snap->Occurrences[IDAndOccurrences.first];
+ for (const auto &Occurrence : IDAndOccurrences.second)
+ Occurrences.push_back(&Occurrence);
+ }
+ return {std::move(Snap), &Snap->Occurrences};
+}
+
+void MemIndex::build(std::shared_ptr<std::vector<const Symbol *>> Syms,
+ std::shared_ptr<OccurrenceMap> AllOccurrences) {
+ assert(Syms && "Syms must be set when build MemIndex");
+ assert(AllOccurrences && "Occurrences must be set when build MemIndex");
llvm::DenseMap<SymbolID, const Symbol *> TempIndex;
for (const Symbol *Sym : *Syms)
TempIndex[Sym->ID] = Sym;
@@ -25,15 +45,18 @@ void MemIndex::build(std::shared_ptr<std
std::lock_guard<std::mutex> Lock(Mutex);
Index = std::move(TempIndex);
Symbols = std::move(Syms); // Relase old symbols.
+ Occurrences = std::move(AllOccurrences);
}
vlog("Built MemIndex with estimated memory usage {0} bytes.",
estimateMemoryUsage());
}
-std::unique_ptr<SymbolIndex> MemIndex::build(SymbolSlab Slab) {
+std::unique_ptr<SymbolIndex> MemIndex::build(SymbolSlab Symbols,
+ SymbolOccurrenceSlab Occurrences) {
auto Idx = llvm::make_unique<MemIndex>();
- Idx->build(getSymbolsFromSlab(std::move(Slab)));
+ Idx->build(getSymbolsFromSlab(std::move(Symbols)),
+ getOccurrencesFromSlab(std::move(Occurrences)));
return std::move(Idx);
}
@@ -84,7 +107,16 @@ void MemIndex::lookup(const LookupReques
void MemIndex::findOccurrences(
const OccurrencesRequest &Req,
llvm::function_ref<void(const SymbolOccurrence &)> Callback) const {
- log("findOccurrences is not implemented.");
+ std::lock_guard<std::mutex> Lock(Mutex);
+ for (const auto &ReqID : Req.IDs) {
+ auto FoundOccurrences = Occurrences->find(ReqID);
+ if (FoundOccurrences == Occurrences->end())
+ continue;
+ for (const auto *O : FoundOccurrences->second) {
+ if (static_cast<int>(Req.Filter & O->Kind))
+ Callback(*O);
+ }
+ }
}
std::shared_ptr<std::vector<const Symbol *>>
Modified: clang-tools-extra/trunk/clangd/index/MemIndex.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/MemIndex.h?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/MemIndex.h (original)
+++ clang-tools-extra/trunk/clangd/index/MemIndex.h Fri Aug 31 12:53:37 2018
@@ -16,16 +16,24 @@
namespace clang {
namespace clangd {
-/// \brief This implements an index for a (relatively small) set of symbols that
-/// can be easily managed in memory.
+/// \brief This implements an index for a (relatively small) set of symbols (or
+/// symbol occurrences) that can be easily managed in memory.
class MemIndex : public SymbolIndex {
public:
- /// \brief (Re-)Build index for `Symbols`. All symbol pointers must remain
- /// accessible as long as `Symbols` is kept alive.
- void build(std::shared_ptr<std::vector<const Symbol *>> Symbols);
-
- /// \brief Build index from a symbol slab.
- static std::unique_ptr<SymbolIndex> build(SymbolSlab Slab);
+ /// Maps from a symbol ID to all corresponding symbol occurrences.
+ /// The map doesn't own occurrence objects.
+ using OccurrenceMap =
+ llvm::DenseMap<SymbolID, std::vector<const SymbolOccurrence *>>;
+
+ /// \brief (Re-)Build index for `Symbols` and update `Occurrences`.
+ /// All symbol pointers and symbol occurrence pointers must remain accessible
+ /// as long as `Symbols` and `Occurrences` are kept alive.
+ void build(std::shared_ptr<std::vector<const Symbol *>> Symbols,
+ std::shared_ptr<OccurrenceMap> Occurrences);
+
+ /// \brief Build index from a symbol slab and a symbol occurrence slab.
+ static std::unique_ptr<SymbolIndex> build(SymbolSlab Symbols,
+ SymbolOccurrenceSlab Occurrences);
bool
fuzzyFind(const FuzzyFindRequest &Req,
@@ -47,6 +55,8 @@ private:
// Index is a set of symbols that are deduplicated by symbol IDs.
// FIXME: build smarter index structure.
llvm::DenseMap<SymbolID, const Symbol *> Index;
+ // A map from symbol ID to symbol occurrences, support query by IDs.
+ std::shared_ptr<OccurrenceMap> Occurrences;
mutable std::mutex Mutex;
};
Modified: clang-tools-extra/trunk/clangd/index/Merge.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/Merge.cpp?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/Merge.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/Merge.cpp Fri Aug 31 12:53:37 2018
@@ -7,6 +7,8 @@
//
//===----------------------------------------------------------------------===//
+#include <set>
+
#include "Merge.h"
#include "../Logger.h"
#include "llvm/ADT/STLExtras.h"
@@ -79,7 +81,26 @@ class MergedIndex : public SymbolIndex {
void findOccurrences(const OccurrencesRequest &Req,
llvm::function_ref<void(const SymbolOccurrence &)>
Callback) const override {
- log("findOccurrences is not implemented.");
+ // We don't want duplicated occurrences from the static/dynamic indexes,
+ // and we can't reliably duplicate them because occurrence offsets may
+ // differ slightly.
+ // We consider the dynamic index authoritative and report all its
+ // occurrences, and only report static index occurrences from other files.
+ //
+ // FIXME: The heuristic fails if the dynamic index containts a file, but all
+ // occurrences were removed (we will report stale ones from the static
+ // index). Ultimately we should explicit check which index has the file
+ // instead.
+ std::set<std::string> DynamicIndexFileURIs;
+ Dynamic->findOccurrences(Req, [&](const SymbolOccurrence &O) {
+ DynamicIndexFileURIs.insert(O.Location.FileURI);
+ Callback(O);
+ });
+ Static->findOccurrences(Req, [&](const SymbolOccurrence &O) {
+ if (DynamicIndexFileURIs.count(O.Location.FileURI))
+ return;
+ Callback(O);
+ });
}
size_t estimateMemoryUsage() const override {
Modified: clang-tools-extra/trunk/clangd/index/Merge.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/Merge.h?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/Merge.h (original)
+++ clang-tools-extra/trunk/clangd/index/Merge.h Fri Aug 31 12:53:37 2018
@@ -24,6 +24,10 @@ Symbol mergeSymbol(const Symbol &L, cons
// - the Dynamic index covers few files, but is relatively up-to-date.
// - the Static index covers a bigger set of files, but is relatively stale.
// The returned index attempts to combine results, and avoid duplicates.
+//
+// FIXME: We don't have a mechanism in Index to track deleted symbols and
+// occurrences in dirty files, so the merged index may return stale symbols
+// and occurrences from Static index.
std::unique_ptr<SymbolIndex> mergeIndex(const SymbolIndex *Dynamic,
const SymbolIndex *Static);
Modified: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original)
+++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Fri Aug 31 12:53:37 2018
@@ -54,7 +54,8 @@ std::unique_ptr<SymbolIndex> buildStatic
SymsBuilder.insert(Sym);
return UseDex ? dex::DexIndex::build(std::move(SymsBuilder).build())
- : MemIndex::build(std::move(SymsBuilder).build());
+ : MemIndex::build(std::move(SymsBuilder).build(),
+ SymbolOccurrenceSlab::createEmpty());
}
} // namespace
Modified: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp Fri Aug 31 12:53:37 2018
@@ -76,7 +76,8 @@ std::unique_ptr<SymbolIndex> memIndex(st
SymbolSlab::Builder Slab;
for (const auto &Sym : Symbols)
Slab.insert(Sym);
- return MemIndex::build(std::move(Slab).build());
+ return MemIndex::build(std::move(Slab).build(),
+ SymbolOccurrenceSlab::createEmpty());
}
CodeCompleteResult completions(ClangdServer &Server, StringRef TestCode,
Modified: clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp Fri Aug 31 12:53:37 2018
@@ -7,18 +7,28 @@
//
//===----------------------------------------------------------------------===//
+#include "Annotations.h"
#include "ClangdUnit.h"
#include "TestFS.h"
#include "TestTU.h"
+#include "gmock/gmock.h"
#include "index/FileIndex.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CompilationDatabase.h"
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
using testing::UnorderedElementsAre;
+using testing::AllOf;
+
+MATCHER_P(OccurrenceRange, Range, "") {
+ return std::tie(arg.Location.Start.Line, arg.Location.Start.Column,
+ arg.Location.End.Line, arg.Location.End.Column) ==
+ std::tie(Range.start.line, Range.start.character, Range.end.line,
+ Range.end.character);
+}
+MATCHER_P(FileURI, F, "") { return arg.Location.FileURI == F; }
namespace clang {
namespace clangd {
@@ -39,6 +49,15 @@ std::unique_ptr<SymbolSlab> numSlab(int
return llvm::make_unique<SymbolSlab>(std::move(Slab).build());
}
+std::unique_ptr<SymbolOccurrenceSlab> occurrenceSlab(const SymbolID &ID,
+ llvm::StringRef Path) {
+ auto Slab = llvm::make_unique<SymbolOccurrenceSlab>();
+ SymbolOccurrence Occurrence;
+ Occurrence.Location.FileURI = Path;
+ Slab->insert(ID, Occurrence);
+ return Slab;
+}
+
std::vector<std::string>
getSymbolNames(const std::vector<const Symbol *> &Symbols) {
std::vector<std::string> Names;
@@ -47,19 +66,38 @@ getSymbolNames(const std::vector<const S
return Names;
}
+std::vector<std::string>
+getOccurrencePath(const std::vector<const SymbolOccurrence *> &Occurrences) {
+ std::vector<std::string> Paths;
+ for (const auto *O : Occurrences)
+ Paths.push_back(O->Location.FileURI);
+ return Paths;
+}
+
+std::unique_ptr<SymbolOccurrenceSlab> emptyOccurrence() {
+ auto EmptySlab = llvm::make_unique<SymbolOccurrenceSlab>();
+ EmptySlab->freeze();
+ return EmptySlab;
+}
+
TEST(FileSymbolsTest, UpdateAndGet) {
FileSymbols FS;
EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre());
+ EXPECT_TRUE(FS.allOccurrences()->empty());
- FS.update("f1", numSlab(1, 3));
+ SymbolID ID("1");
+ FS.update("f1", numSlab(1, 3), occurrenceSlab(ID, "f1.cc"));
EXPECT_THAT(getSymbolNames(*FS.allSymbols()),
UnorderedElementsAre("1", "2", "3"));
+ auto Occurrences = FS.allOccurrences();
+ EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]),
+ UnorderedElementsAre("f1.cc"));
}
TEST(FileSymbolsTest, Overlap) {
FileSymbols FS;
- FS.update("f1", numSlab(1, 3));
- FS.update("f2", numSlab(3, 5));
+ FS.update("f1", numSlab(1, 3), emptyOccurrence());
+ FS.update("f2", numSlab(3, 5), emptyOccurrence());
EXPECT_THAT(getSymbolNames(*FS.allSymbols()),
UnorderedElementsAre("1", "2", "3", "3", "4", "5"));
}
@@ -67,14 +105,22 @@ TEST(FileSymbolsTest, Overlap) {
TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
FileSymbols FS;
- FS.update("f1", numSlab(1, 3));
+ SymbolID ID("1");
+ FS.update("f1", numSlab(1, 3), occurrenceSlab(ID, "f1.cc"));
auto Symbols = FS.allSymbols();
EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3"));
+ auto Occurrences = FS.allOccurrences();
+ EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]),
+ UnorderedElementsAre("f1.cc"));
- FS.update("f1", nullptr);
+ FS.update("f1", nullptr, nullptr);
EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre());
EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3"));
+
+ EXPECT_TRUE(FS.allOccurrences()->empty());
+ EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]),
+ UnorderedElementsAre("f1.cc"));
}
std::vector<std::string> match(const SymbolIndex &I,
@@ -270,6 +316,52 @@ TEST(FileIndexTest, RebuildWithPreamble)
UnorderedElementsAre("ns_in_header", "ns_in_header::func_in_header"));
}
+TEST(FileIndexTest, Occurrences) {
+ const char *HeaderCode = "class Foo {};";
+ Annotations MainCode(R"cpp(
+ void f() {
+ $foo[[Foo]] foo;
+ }
+ )cpp");
+
+ auto Foo =
+ findSymbol(TestTU::withHeaderCode(HeaderCode).headerSymbols(), "Foo");
+
+ OccurrencesRequest Request;
+ Request.IDs = {Foo.ID};
+ Request.Filter = SymbolOccurrenceKind::Declaration |
+ SymbolOccurrenceKind::Definition |
+ SymbolOccurrenceKind::Reference;
+
+ FileIndex Index(/*URISchemes*/ {"unittest"});
+ // Add test.cc
+ TestTU Test;
+ Test.HeaderCode = HeaderCode;
+ Test.Code = MainCode.code();
+ Test.Filename = "test.cc";
+ auto AST = Test.build();
+ Index.update(Test.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(),
+ AST.getLocalTopLevelDecls());
+ // Add test2.cc
+ TestTU Test2;
+ Test2.HeaderCode = HeaderCode;
+ Test2.Code = MainCode.code();
+ Test2.Filename = "test2.cc";
+ AST = Test2.build();
+ Index.update(Test2.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(),
+ AST.getLocalTopLevelDecls());
+
+ std::vector<SymbolOccurrence> Results;
+ Index.findOccurrences(
+ Request, [&Results](const SymbolOccurrence &O) { Results.push_back(O); });
+
+ EXPECT_THAT(Results,
+ UnorderedElementsAre(AllOf(OccurrenceRange(MainCode.range("foo")),
+ FileURI("unittest:///test.cc")),
+ AllOf(OccurrenceRange(MainCode.range("foo")),
+ FileURI("unittest:///test2.cc"))));
+}
+
} // namespace
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp Fri Aug 31 12:53:37 2018
@@ -7,21 +7,36 @@
//
//===----------------------------------------------------------------------===//
+#include "Annotations.h"
#include "TestIndex.h"
+#include "TestTU.h"
+#include "gmock/gmock.h"
+#include "index/FileIndex.h"
#include "index/Index.h"
#include "index/MemIndex.h"
#include "index/Merge.h"
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
using testing::Pointee;
using testing::UnorderedElementsAre;
+using testing::AllOf;
namespace clang {
namespace clangd {
namespace {
+std::shared_ptr<MemIndex::OccurrenceMap> emptyOccurrences() {
+ return llvm::make_unique<MemIndex::OccurrenceMap>();
+}
+
MATCHER_P(Named, N, "") { return arg.Name == N; }
+MATCHER_P(OccurrenceRange, Range, "") {
+ return std::tie(arg.Location.Start.Line, arg.Location.Start.Column,
+ arg.Location.End.Line, arg.Location.End.Column) ==
+ std::tie(Range.start.line, Range.start.character, Range.end.line,
+ Range.end.character);
+}
+MATCHER_P(FileURI, F, "") { return arg.Location.FileURI == F; }
TEST(SymbolSlab, FindAndIterate) {
SymbolSlab::Builder B;
@@ -42,14 +57,14 @@ TEST(SymbolSlab, FindAndIterate) {
TEST(MemIndexTest, MemIndexSymbolsRecycled) {
MemIndex I;
std::weak_ptr<SlabAndPointers> Symbols;
- I.build(generateNumSymbols(0, 10, &Symbols));
+ I.build(generateNumSymbols(0, 10, &Symbols), emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "7";
EXPECT_THAT(match(I, Req), UnorderedElementsAre("7"));
EXPECT_FALSE(Symbols.expired());
// Release old symbols.
- I.build(generateNumSymbols(0, 0));
+ I.build(generateNumSymbols(0, 0), emptyOccurrences());
EXPECT_TRUE(Symbols.expired());
}
@@ -65,14 +80,14 @@ TEST(MemIndexTest, MemIndexDeduplicate)
FuzzyFindRequest Req;
Req.Query = "7";
MemIndex I;
- I.build(std::move(Symbols));
+ I.build(std::move(Symbols), emptyOccurrences());
auto Matches = match(I, Req);
EXPECT_EQ(Matches.size(), 1u);
}
TEST(MemIndexTest, MemIndexLimitedNumMatches) {
MemIndex I;
- I.build(generateNumSymbols(0, 100));
+ I.build(generateNumSymbols(0, 100), emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "5";
Req.MaxCandidateCount = 3;
@@ -85,7 +100,8 @@ TEST(MemIndexTest, MemIndexLimitedNumMat
TEST(MemIndexTest, FuzzyMatch) {
MemIndex I;
I.build(
- generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}));
+ generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}),
+ emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "lol";
Req.MaxCandidateCount = 2;
@@ -95,7 +111,7 @@ TEST(MemIndexTest, FuzzyMatch) {
TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) {
MemIndex I;
- I.build(generateSymbols({"a::y1", "b::y2", "y3"}));
+ I.build(generateSymbols({"a::y1", "b::y2", "y3"}), emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "y";
EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3"));
@@ -103,7 +119,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWi
TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) {
MemIndex I;
- I.build(generateSymbols({"a::y1", "b::y2", "y3"}));
+ I.build(generateSymbols({"a::y1", "b::y2", "y3"}), emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "y";
Req.Scopes = {""};
@@ -112,7 +128,8 @@ TEST(MemIndexTest, MatchQualifiedNamesWi
TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) {
MemIndex I;
- I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}));
+ I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}),
+ emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "y";
Req.Scopes = {"a::"};
@@ -121,7 +138,8 @@ TEST(MemIndexTest, MatchQualifiedNamesWi
TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) {
MemIndex I;
- I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}));
+ I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}),
+ emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "y";
Req.Scopes = {"a::", "b::"};
@@ -130,7 +148,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWi
TEST(MemIndexTest, NoMatchNestedScopes) {
MemIndex I;
- I.build(generateSymbols({"a::y1", "a::b::y2"}));
+ I.build(generateSymbols({"a::y1", "a::b::y2"}), emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "y";
Req.Scopes = {"a::"};
@@ -139,7 +157,7 @@ TEST(MemIndexTest, NoMatchNestedScopes)
TEST(MemIndexTest, IgnoreCases) {
MemIndex I;
- I.build(generateSymbols({"ns::ABC", "ns::abc"}));
+ I.build(generateSymbols({"ns::ABC", "ns::abc"}), emptyOccurrences());
FuzzyFindRequest Req;
Req.Query = "AB";
Req.Scopes = {"ns::"};
@@ -148,7 +166,7 @@ TEST(MemIndexTest, IgnoreCases) {
TEST(MemIndexTest, Lookup) {
MemIndex I;
- I.build(generateSymbols({"ns::abc", "ns::xyz"}));
+ I.build(generateSymbols({"ns::abc", "ns::xyz"}), emptyOccurrences());
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"));
@@ -159,8 +177,8 @@ TEST(MemIndexTest, Lookup) {
TEST(MergeIndexTest, Lookup) {
MemIndex I, J;
- I.build(generateSymbols({"ns::A", "ns::B"}));
- J.build(generateSymbols({"ns::B", "ns::C"}));
+ I.build(generateSymbols({"ns::A", "ns::B"}), emptyOccurrences());
+ J.build(generateSymbols({"ns::B", "ns::C"}), emptyOccurrences());
EXPECT_THAT(lookup(*mergeIndex(&I, &J), SymbolID("ns::A")),
UnorderedElementsAre("ns::A"));
EXPECT_THAT(lookup(*mergeIndex(&I, &J), SymbolID("ns::B")),
@@ -180,8 +198,8 @@ TEST(MergeIndexTest, Lookup) {
TEST(MergeIndexTest, FuzzyFind) {
MemIndex I, J;
- I.build(generateSymbols({"ns::A", "ns::B"}));
- J.build(generateSymbols({"ns::B", "ns::C"}));
+ I.build(generateSymbols({"ns::A", "ns::B"}), emptyOccurrences());
+ J.build(generateSymbols({"ns::B", "ns::C"}), emptyOccurrences());
FuzzyFindRequest Req;
Req.Scopes = {"ns::"};
EXPECT_THAT(match(*mergeIndex(&I, &J), Req),
@@ -234,6 +252,60 @@ TEST(MergeTest, PreferSymbolWithDefn) {
EXPECT_EQ(M.Name, "right");
}
+TEST(MergeIndexTest, FindOccurrences) {
+ FileIndex Dyn({"unittest"});
+ FileIndex StaticIndex({"unittest"});
+ auto MergedIndex = mergeIndex(&Dyn, &StaticIndex);
+
+ const char *HeaderCode = "class Foo;";
+ auto HeaderSymbols = TestTU::withHeaderCode("class Foo;").headerSymbols();
+ auto Foo = findSymbol(HeaderSymbols, "Foo");
+
+ // Build dynamic index for test.cc.
+ Annotations Test1Code(R"(class $Foo[[Foo]];)");
+ TestTU Test;
+ Test.HeaderCode = HeaderCode;
+ Test.Code = Test1Code.code();
+ Test.Filename = "test.cc";
+ auto AST = Test.build();
+ Dyn.update(Test.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(),
+ AST.getLocalTopLevelDecls());
+
+ // Build static index for test.cc.
+ Test.HeaderCode = HeaderCode;
+ Test.Code = "// static\nclass Foo {};";
+ Test.Filename = "test.cc";
+ auto StaticAST = Test.build();
+ // Add stale occurrences for test.cc.
+ StaticIndex.update(Test.Filename, &StaticAST.getASTContext(),
+ StaticAST.getPreprocessorPtr(),
+ StaticAST.getLocalTopLevelDecls());
+
+ // Add occcurrences for test2.cc
+ Annotations Test2Code(R"(class $Foo[[Foo]] {};)");
+ TestTU Test2;
+ Test2.HeaderCode = HeaderCode;
+ Test2.Code = Test2Code.code();
+ Test2.Filename = "test2.cc";
+ StaticAST = Test2.build();
+ StaticIndex.update(Test2.Filename, &StaticAST.getASTContext(),
+ StaticAST.getPreprocessorPtr(),
+ StaticAST.getLocalTopLevelDecls());
+
+ OccurrencesRequest Request;
+ Request.IDs = {Foo.ID};
+ Request.Filter = AllOccurrenceKinds;
+ std::vector<SymbolOccurrence> Results;
+ MergedIndex->findOccurrences(
+ Request, [&](const SymbolOccurrence &O) { Results.push_back(O); });
+
+ EXPECT_THAT(Results, UnorderedElementsAre(
+ AllOf(OccurrenceRange(Test1Code.range("Foo")),
+ FileURI("unittest:///test.cc")),
+ AllOf(OccurrenceRange(Test2Code.range("Foo")),
+ FileURI("unittest:///test2.cc"))));
+}
+
} // namespace
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/unittests/clangd/TestTU.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/TestTU.cpp?rev=341242&r1=341241&r2=341242&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/TestTU.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/TestTU.cpp Fri Aug 31 12:53:37 2018
@@ -45,11 +45,12 @@ ParsedAST TestTU::build() const {
SymbolSlab TestTU::headerSymbols() const {
auto AST = build();
- return indexAST(AST.getASTContext(), AST.getPreprocessorPtr());
+ return indexAST(AST.getASTContext(), AST.getPreprocessorPtr()).first;
}
std::unique_ptr<SymbolIndex> TestTU::index() const {
- return MemIndex::build(headerSymbols());
+ // FIXME: we should generate proper occurrences for TestTU.
+ return MemIndex::build(headerSymbols(), SymbolOccurrenceSlab::createEmpty());
}
const Symbol &findSymbol(const SymbolSlab &Slab, llvm::StringRef QName) {
More information about the cfe-commits
mailing list