[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