hokein created this revision.
hokein added a reviewer: sammccall.
Herald added subscribers: kadircet, arphaman, jkorous, MaskRay, ioeric, 
ilya-biryukov.

Implement the interface in

- FileIndex
- MemIndex
- MergeIndex

Depends on https://reviews.llvm.org/D50385.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D51279

Files:
  clangd/index/FileIndex.cpp
  clangd/index/FileIndex.h
  clangd/index/Index.h
  clangd/index/MemIndex.cpp
  clangd/index/MemIndex.h
  clangd/index/Merge.cpp
  unittests/clangd/FileIndexTests.cpp
  unittests/clangd/IndexTests.cpp
  unittests/clangd/TestTU.cpp
  unittests/clangd/TestTU.h

Index: unittests/clangd/TestTU.h
===================================================================
--- unittests/clangd/TestTU.h
+++ unittests/clangd/TestTU.h
@@ -38,6 +38,16 @@
     return TU;
   }
 
+  static TestTU withAllCode(llvm::StringRef HeaderCode, llvm::StringRef Code,
+                            llvm::StringRef Filename = "") {
+    TestTU TU;
+    TU.HeaderCode = HeaderCode;
+    TU.Code = Code;
+    if (!Filename.empty())
+     TU.Filename = Filename;
+    return TU;
+  }
+
   // The code to be compiled.
   std::string Code;
   std::string Filename = "TestTU.cpp";
Index: unittests/clangd/TestTU.cpp
===================================================================
--- unittests/clangd/TestTU.cpp
+++ unittests/clangd/TestTU.cpp
@@ -45,7 +45,7 @@
 
 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 {
Index: unittests/clangd/IndexTests.cpp
===================================================================
--- unittests/clangd/IndexTests.cpp
+++ unittests/clangd/IndexTests.cpp
@@ -8,20 +8,33 @@
 //===----------------------------------------------------------------------===//
 
 #include "TestIndex.h"
+#include "TestTU.h"
+#include "Annotations.h"
 #include "index/Index.h"
 #include "index/MemIndex.h"
 #include "index/Merge.h"
+#include "index/FileIndex.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 using testing::Pointee;
 using testing::UnorderedElementsAre;
+using testing::AllOf;
 
 namespace clang {
 namespace clangd {
 namespace {
 
 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;
@@ -243,6 +256,55 @@
   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.
+  Annotations Test1Code(R"(class $Foo[[Foo]];)");
+  auto AST =
+      TestTU::withAllCode(HeaderCode, Test1Code.code(), "test.cc").build();
+  Dyn.update("test.cc", &AST.getASTContext(), AST.getPreprocessorPtr(),
+             AST.getLocalTopLevelDecls());
+
+  // Build static index.
+  auto StaticAST =
+      TestTU::withAllCode(HeaderCode, "// static\nclass Foo {};", "test.cc")
+          .build();
+  // Add stale occurrences for test.cc.
+  StaticIndex.update("test.cc", &StaticAST.getASTContext(),
+                     StaticAST.getPreprocessorPtr(),
+                     StaticAST.getLocalTopLevelDecls());
+
+  // Add occcurrences for test2.cc
+  Annotations Test2Code(R"(class $Foo[[Foo]] {};)");
+  StaticAST =
+      TestTU::withAllCode(HeaderCode, Test2Code.code(), "test2.cc").build();
+  StaticIndex.update("test2.cc", &StaticAST.getASTContext(),
+                     StaticAST.getPreprocessorPtr(),
+                     StaticAST.getLocalTopLevelDecls());
+
+  OccurrencesRequest Request;
+  Request.IDs = {Foo.ID};
+  Request.Filter = SymbolOccurrenceKind::Declaration |
+                   SymbolOccurrenceKind::Definition |
+                   SymbolOccurrenceKind::Reference;
+  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
Index: unittests/clangd/FileIndexTests.cpp
===================================================================
--- unittests/clangd/FileIndexTests.cpp
+++ unittests/clangd/FileIndexTests.cpp
@@ -7,6 +7,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "Annotations.h"
 #include "ClangdUnit.h"
 #include "TestFS.h"
 #include "TestTU.h"
@@ -19,6 +20,19 @@
 #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 {
@@ -270,6 +284,46 @@
       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
+  auto AST =
+      TestTU::withAllCode(HeaderCode, MainCode.code(), "test.cc").build();
+  Index.update("test.cc", &AST.getASTContext(), AST.getPreprocessorPtr(),
+               AST.getLocalTopLevelDecls());
+  // Add test2.cc
+  AST = TestTU::withAllCode(HeaderCode, MainCode.code(), "test2.cc").build();
+  Index.update("test2.cc", &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
Index: clangd/index/Merge.cpp
===================================================================
--- clangd/index/Merge.cpp
+++ clangd/index/Merge.cpp
@@ -81,7 +81,21 @@
   void findOccurrences(const OccurrencesRequest &Req,
                        llvm::function_ref<void(const SymbolOccurrence &)>
                            Callback) const override {
-    log("findOccurrences is not implemented.");
+    // Store all seen files by dynamic index.
+    llvm::DenseSet<llvm::StringRef> SeenFiles;
+    // Emit all dynamic occurrences, and static occurrences that are not
+    // in seen files.
+    // FIXME: If a file has been updated, and there are no occurrences indexed
+    // in dynamic index, stale results are still returned (from static index).
+    Dynamic->findOccurrences(Req, [&](const SymbolOccurrence &O) {
+      SeenFiles.insert(O.Location.FileURI);
+      Callback(O);
+    });
+    Static->findOccurrences(Req, [&](const SymbolOccurrence& O) {
+      if (llvm::is_contained(SeenFiles, O.Location.FileURI))
+        return;
+      Callback(O);
+    });
   }
 
 private:
Index: clangd/index/MemIndex.h
===================================================================
--- clangd/index/MemIndex.h
+++ clangd/index/MemIndex.h
@@ -22,7 +22,9 @@
 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);
+  void
+  build(std::shared_ptr<std::vector<const Symbol *>> Symbols,
+        std::vector<std::shared_ptr<SymbolOccurrenceSlab>> OccurrenceSlabs = {});
 
   /// \brief Build index from a symbol slab.
   static std::unique_ptr<SymbolIndex> build(SymbolSlab Slab);
@@ -44,6 +46,8 @@
   // Index is a set of symbols that are deduplicated by symbol IDs.
   // FIXME: build smarter index structure.
   llvm::DenseMap<SymbolID, const Symbol *> Index;
+
+  std::vector<std::shared_ptr<SymbolOccurrenceSlab>> OccurrenceSlabs;
   mutable std::mutex Mutex;
 };
 
Index: clangd/index/MemIndex.cpp
===================================================================
--- clangd/index/MemIndex.cpp
+++ clangd/index/MemIndex.cpp
@@ -15,7 +15,9 @@
 namespace clang {
 namespace clangd {
 
-void MemIndex::build(std::shared_ptr<std::vector<const Symbol *>> Syms) {
+void MemIndex::build(
+    std::shared_ptr<std::vector<const Symbol *>> Syms,
+    std::vector<std::shared_ptr<SymbolOccurrenceSlab>> Occurrences) {
   llvm::DenseMap<SymbolID, const Symbol *> TempIndex;
   for (const Symbol *Sym : *Syms)
     TempIndex[Sym->ID] = Sym;
@@ -25,12 +27,13 @@
     std::lock_guard<std::mutex> Lock(Mutex);
     Index = std::move(TempIndex);
     Symbols = std::move(Syms); // Relase old symbols.
+    OccurrenceSlabs = std::move(Occurrences);
   }
 }
 
 std::unique_ptr<SymbolIndex> MemIndex::build(SymbolSlab Slab) {
   auto Idx = llvm::make_unique<MemIndex>();
-  Idx->build(getSymbolsFromSlab(std::move(Slab)));
+  Idx->build(getSymbolsFromSlab(std::move(Slab)), {});
   return std::move(Idx);
 }
 
@@ -81,7 +84,15 @@
 void MemIndex::findOccurrences(
     const OccurrencesRequest &Req,
     llvm::function_ref<void(const SymbolOccurrence &)> Callback) const {
-  log("findOccurrences is not implemented.");
+  for (const auto &Slab : OccurrenceSlabs) {
+    for (const auto &ID : Req.IDs) {
+      for (const auto &Occurrence : Slab->find(ID)) {
+        if (static_cast<int>(Req.Filter & Occurrence.Kind)) {
+          Callback(Occurrence);
+        }
+      }
+    }
+  }
 }
 
 std::shared_ptr<std::vector<const Symbol *>>
Index: clangd/index/Index.h
===================================================================
--- clangd/index/Index.h
+++ clangd/index/Index.h
@@ -369,6 +369,15 @@
    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);
Index: clangd/index/FileIndex.h
===================================================================
--- clangd/index/FileIndex.h
+++ clangd/index/FileIndex.h
@@ -41,16 +41,21 @@
 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);
+  void update(PathRef Path, std::unique_ptr<SymbolSlab> Slab,
+              std::unique_ptr<SymbolOccurrenceSlab> Occurrences = nullptr);
 
   // The shared_ptr keeps the symbols alive
   std::shared_ptr<std::vector<const Symbol *>> allSymbols();
 
+  std::vector<std::shared_ptr<SymbolOccurrenceSlab>> allOccurrenceSlabs() const;
+
 private:
   mutable std::mutex Mutex;
 
   /// \brief Stores the latest snapshots for all active files.
   llvm::StringMap<std::shared_ptr<SymbolSlab>> FileToSlabs;
+  /// \breif 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.
@@ -92,7 +97,7 @@
 /// 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 = {});
Index: clangd/index/FileIndex.cpp
===================================================================
--- clangd/index/FileIndex.cpp
+++ clangd/index/FileIndex.cpp
@@ -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 @@
     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,41 @@
     DeclsToIndex.assign(AST.getTranslationUnitDecl()->decls().begin(),
                         AST.getTranslationUnitDecl()->decls().end());
 
+  if (TopLevelDecls) { // index main AST, set occurrence flag.
+    CollectorOpts.OccurrenceFilter = SymbolOccurrenceKind::Declaration |
+                                     SymbolOccurrenceKind::Definition |
+                                     SymbolOccurrenceKind::Reference;
+  }
+
+  SymbolCollector Collector(std::move(CollectorOpts));
+  Collector.setPreprocessor(PP);
   index::indexTopLevelDecls(AST, DeclsToIndex, Collector, IndexOpts);
 
-  return Collector.takeSymbols();
+  vlog("index for AST: ");
+  auto Syms = Collector.takeSymbols();
+  auto Occurrences = Collector.takeOccurrences();
+  vlog("  symbol slab: {0} symbols, {1} bytes", Syms.size(), Syms.bytes());
+  vlog("  occurrence slab: {0} symbols, {1} 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)
+  if (!Slab) {
     FileToSlabs.erase(Path);
-  else
+  } 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 +105,32 @@
   return {std::move(Snap), Pointers};
 }
 
+std::vector<std::shared_ptr<SymbolOccurrenceSlab>>
+FileSymbols::allOccurrenceSlabs() const{
+  std::vector<std::shared_ptr<SymbolOccurrenceSlab>> Slabs;
+  std::lock_guard<std::mutex> Lock(Mutex);
+
+  for (const auto &FileAndSlab : FileToOccurrenceSlabs)
+    Slabs.push_back(FileAndSlab.second);
+  return Slabs;
+}
+
 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 IndexResults = indexAST(*AST, PP, TopLevelDecls, URISchemes);
+    *Slab = std::move(IndexResults.first);
+    auto OccurrenceSlab = llvm::make_unique<SymbolOccurrenceSlab>();
+    *OccurrenceSlab = std::move(IndexResults.second);
+    FSymbols.update(Path, std::move(Slab), std::move(OccurrenceSlab));
   }
   auto Symbols = FSymbols.allSymbols();
-  Index.build(std::move(Symbols));
+  Index.build(std::move(Symbols), FSymbols.allOccurrenceSlabs());
 }
 
 bool FileIndex::fuzzyFind(
@@ -115,7 +148,7 @@
 void FileIndex::findOccurrences(
     const OccurrencesRequest &Req,
     llvm::function_ref<void(const SymbolOccurrence &)> Callback) const {
-  log("findOccurrences is not implemented.");
+  Index.findOccurrences(Req, Callback);
 }
 
 } // namespace clangd
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to