hokein updated this revision to Diff 162611.
hokein marked 7 inline comments as done.
hokein added a comment.

Update the patch based on our new discussion

- SymbolOccurrenceSlab for storing underlying occurrence data
- reuse SymbolCollector to collect symbol occurrences


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50385

Files:
  clangd/index/Index.cpp
  clangd/index/Index.h
  clangd/index/SymbolCollector.cpp
  clangd/index/SymbolCollector.h
  unittests/clangd/SymbolCollectorTests.cpp

Index: unittests/clangd/SymbolCollectorTests.cpp
===================================================================
--- unittests/clangd/SymbolCollectorTests.cpp
+++ unittests/clangd/SymbolCollectorTests.cpp
@@ -74,6 +74,16 @@
 MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") {
   return arg.IsIndexedForCodeCompletion == IsIndexedForCodeCompletion;
 }
+MATCHER(OccurrenceRange, "") {
+  const clang::clangd::SymbolOccurrence& Pos = testing::get<0>(arg);
+  const clang::clangd::Range& Range = testing::get<1>(arg);
+  return std::tie(Pos.Location.Start.Line,
+                  Pos.Location.Start.Column,
+                  Pos.Location.End.Line,
+                  Pos.Location.End.Column) ==
+         std::tie(Range.start.line, Range.start.character, Range.end.line,
+                  Range.end.character);
+}
 
 namespace clang {
 namespace clangd {
@@ -237,6 +247,7 @@
                                 llvm::MemoryBuffer::getMemBuffer(MainCode));
     Invocation.run();
     Symbols = Factory->Collector->takeSymbols();
+    SymbolOccurrences = Factory->Collector->takeOccurrences();
     return true;
   }
 
@@ -247,6 +258,7 @@
   std::string TestFileName;
   std::string TestFileURI;
   SymbolSlab Symbols;
+  SymbolOccurrenceSlab SymbolOccurrences;
   SymbolCollector::Options CollectorOpts;
   std::unique_ptr<CommentHandler> PragmaHandler;
 };
@@ -413,6 +425,67 @@
           ));
 }
 
+TEST_F(SymbolCollectorTest, Occurrences) {
+  Annotations Header(R"(
+  class $foo[[Foo]] {
+  public:
+    $foo[[Foo]]() {}
+    $foo[[Foo]](int);
+  };
+  class $bar[[Bar]];
+  void $func[[func]]();
+  )");
+  Annotations Main(R"(
+  class $bar[[Bar]] {};
+
+  void $func[[func]]();
+
+  void fff() {
+    $foo[[Foo]] foo;
+    $bar[[Bar]] bar;
+    $func[[func]]();
+    int abc = 0;
+    $foo[[Foo]] foo2 = abc;
+  }
+  )");
+  Annotations SymbolsOnlyInMainCode(R"(
+  int a;
+  void b() {}
+  static const int c = 0;
+  class d {};
+  )");
+  CollectorOpts.OccurrenceFilter = SymbolOccurrenceKind::Declaration |
+                                   SymbolOccurrenceKind::Definition |
+                                   SymbolOccurrenceKind::Reference;
+  runSymbolCollector(Header.code(),
+                     (Main.code() + SymbolsOnlyInMainCode.code()).str());
+  auto HeaderSymbols = TestTU::withHeaderCode(Header.code()).headerSymbols();
+
+  EXPECT_THAT(
+      SymbolOccurrences.find(findSymbol(Symbols, "Foo").ID),
+      testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("foo")));
+  EXPECT_THAT(
+      SymbolOccurrences.find(findSymbol(Symbols, "Bar").ID),
+      testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("bar")));
+  EXPECT_THAT(
+      SymbolOccurrences.find(findSymbol(Symbols, "func").ID),
+      testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("func")));
+
+  // Retrieve IDs for symbols *only* in the main file, and verify these symbols
+  // are not collected.
+  auto MainSymbols =
+      TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols();
+  EXPECT_THAT(
+      SymbolOccurrences.find(findSymbol(MainSymbols, "a").ID),
+      testing::IsEmpty());
+  EXPECT_THAT(
+      SymbolOccurrences.find(findSymbol(MainSymbols, "b").ID),
+      testing::IsEmpty());
+  EXPECT_THAT(
+      SymbolOccurrences.find(findSymbol(MainSymbols, "c").ID),
+      testing::IsEmpty());
+}
+
 TEST_F(SymbolCollectorTest, References) {
   const std::string Header = R"(
     class W;
Index: clangd/index/SymbolCollector.h
===================================================================
--- clangd/index/SymbolCollector.h
+++ clangd/index/SymbolCollector.h
@@ -59,6 +59,10 @@
     /// collect macros. For example, `indexTopLevelDecls` will not index any
     /// macro even if this is true.
     bool CollectMacro = false;
+    /// The symbol occurrence kind that will be collected.
+    /// If not set (Unknown), SymbolCollector will not collect any symbol
+    /// occurrences.
+    SymbolOccurrenceKind OccurrenceFilter = SymbolOccurrenceKind::Unknown;
   };
 
   SymbolCollector(Options Opts);
@@ -86,6 +90,10 @@
 
   SymbolSlab takeSymbols() { return std::move(Symbols).build(); }
 
+  SymbolOccurrenceSlab takeOccurrences() {
+    return std::move(SymbolOccurrences);
+  }
+
   void finish() override;
 
 private:
@@ -108,6 +116,13 @@
   // canonical by clang but should not be considered canonical in the index
   // unless it's a definition.
   llvm::DenseMap<const Decl *, const Decl *> CanonicalDecls;
+
+  using DeclOccurrence = std::pair<SourceLocation, index::SymbolRoleSet>;
+  llvm::DenseMap<const NamedDecl*, std::vector<DeclOccurrence>> DeclOccurrences;
+  // All symbol occurrences collected from the AST, assembled on finish().
+  // Only symbols declared in preamble (from #inclues) and references from the
+  // main file will be included.
+  SymbolOccurrenceSlab SymbolOccurrences;
 };
 
 } // namespace clangd
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -224,6 +224,17 @@
          match(decl(isExpansionInMainFile()), ND, ND.getASTContext()).empty();
 }
 
+SymbolOccurrenceKind ToOccurrenceKind(index::SymbolRoleSet Roles) {
+  SymbolOccurrenceKind Kind;
+  for (auto Mask :
+       {SymbolOccurrenceKind::Declaration, SymbolOccurrenceKind::Definition,
+        SymbolOccurrenceKind::Reference}) {
+    if (Roles & static_cast<unsigned>(Mask))
+      Kind |= Mask;
+  }
+  return Kind;
+}
+
 } // namespace
 
 SymbolCollector::SymbolCollector(Options Opts) : Opts(std::move(Opts)) {}
@@ -313,6 +324,10 @@
       SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
     ReferencedDecls.insert(ND);
 
+  if ((static_cast<unsigned>(Opts.OccurrenceFilter) & Roles) &&
+      SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
+    DeclOccurrences[ND].emplace_back(Loc, Roles);
+
   // Don't continue indexing if this is a mere reference.
   if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
         Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
@@ -436,8 +451,28 @@
           IncRef(SymbolID(USR));
     }
   }
+
+  for (auto& It : DeclOccurrences) {
+    if (auto ID = getSymbolID(It.first)) {
+      if (Symbols.find(*ID)) {
+        for (const auto& LocAndRole: It.second) {
+          std::string FileURI;
+          if (auto DeclLoc = getTokenLocation(LocAndRole.first,
+                                              ASTCtx->getSourceManager(), Opts,
+                                              ASTCtx->getLangOpts(), FileURI)) {
+            SymbolOccurrence Occurrence;
+            Occurrence.Location = *DeclLoc;
+            Occurrence.Kind = ToOccurrenceKind(LocAndRole.second);
+            SymbolOccurrences.insert(*ID, Occurrence);
+          }
+        }
+      }
+    }
+  }
+  SymbolOccurrences.freeze();
   ReferencedDecls.clear();
   ReferencedMacros.clear();
+  DeclOccurrences.clear();
 }
 
 const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND,
Index: clangd/index/Index.h
===================================================================
--- clangd/index/Index.h
+++ clangd/index/Index.h
@@ -31,9 +31,6 @@
     uint32_t Line = 0; // 0-based
     // Using UTF-16 code units.
     uint32_t Column = 0; // 0-based
-    bool operator==(const Position& P) const {
-      return Line == P.Line && Column == P.Column;
-    }
   };
 
   // The URI of the source file where a symbol occurs.
@@ -44,11 +41,25 @@
   Position End;
 
   explicit operator bool() const { return !FileURI.empty(); }
-  bool operator==(const SymbolLocation& Loc) const {
-    return std::tie(FileURI, Start, End) ==
-           std::tie(Loc.FileURI, Loc.Start, Loc.End);
-  }
 };
+inline bool operator==(const SymbolLocation::Position& L,
+                       const SymbolLocation::Position& R) {
+  return std::tie(L.Line, L.Column) == std::tie(R.Line, R.Column);
+}
+inline bool operator<(const SymbolLocation::Position &L,
+                      const SymbolLocation::Position &R){
+  return std::tie(L.Line, L.Column) < std::tie(R.Line, R.Column);
+}
+inline bool operator==(const SymbolLocation&L,
+                       const SymbolLocation&R){
+  return std::tie(L.FileURI, L.Start, L.End) ==
+         std::tie(R.FileURI, R.Start, R.End);
+}
+inline bool operator<(const SymbolLocation&L,
+                      const SymbolLocation&R){
+  return std::tie(L.FileURI, L.Start, L.End) <
+         std::tie(R.FileURI, R.Start, R.End);
+}
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolLocation &);
 
 // The class identifies a particular C++ symbol (class, function, method, etc).
@@ -323,6 +334,60 @@
   SymbolLocation Location;
   SymbolOccurrenceKind Kind = SymbolOccurrenceKind::Unknown;
 };
+inline bool operator<(const SymbolOccurrence &L, const SymbolOccurrence &R) {
+  return std::tie(L.Location, L.Kind) < std::tie(R.Location, R.Kind);
+}
+inline bool operator==(const SymbolOccurrence &L, const SymbolOccurrence &R) {
+  return std::tie(L.Location, L.Kind) == std::tie(R.Location, R.Kind);
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+                              const SymbolOccurrence &Occurrence);
+
+// An efficient structure of storing large set of symbol occurrences in memory.
+// Filenames are deduplicated.
+class SymbolOccurrenceSlab {
+ public:
+   using const_iterator =
+       llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>>::const_iterator;
+   using iterator = const_iterator;
+
+   SymbolOccurrenceSlab() : UniqueStrings(Arena) {}
+
+   // 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.
+   SymbolOccurrenceSlab(SymbolOccurrenceSlab &&Slab) = default;
+   SymbolOccurrenceSlab& operator=(SymbolOccurrenceSlab&& RHS) {
+     assert(RHS.Frozen &&
+            "SymbolOcucrrenceSlab must be frozen when move assigned!");
+     Arena = std::move(RHS.Arena);
+     Frozen = true;
+     Occurrences = std::move(RHS.Occurrences);
+     return *this;
+   }
+
+   const_iterator begin() const { return Occurrences.begin(); }
+   const_iterator end() const { return Occurrences.end(); }
+
+   // 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);
+
+   llvm::ArrayRef<SymbolOccurrence> find(const SymbolID &ID) const {
+     auto It = Occurrences.find(ID);
+     if (It == Occurrences.end())
+       return {};
+     return It->second;
+  }
+
+  void freeze();
+
+private:
+  bool Frozen = false;
+  llvm::BumpPtrAllocator Arena;
+  llvm::UniqueStringSaver UniqueStrings;
+  llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>> Occurrences;
+};
 
 struct FuzzyFindRequest {
   /// \brief A query string for the fuzzy find. This is matched against symbols'
Index: clangd/index/Index.cpp
===================================================================
--- clangd/index/Index.cpp
+++ clangd/index/Index.cpp
@@ -128,5 +128,48 @@
   return SymbolSlab(std::move(NewArena), std::move(Symbols));
 }
 
+raw_ostream &operator<<(raw_ostream &OS, SymbolOccurrenceKind K) {
+  if (K == SymbolOccurrenceKind::Unknown)
+    return OS << "Unknown";
+  static const std::vector<const char *> Messages = {"Decl", "Def", "Ref"};
+  bool VisitedOnce = false;
+  for (unsigned I = 0; I < Messages.size(); ++I) {
+    if (static_cast<uint8_t>(K) & 1u << I) {
+      if (VisitedOnce)
+        OS << ", ";
+      OS << Messages[I];
+      VisitedOnce = true;
+    }
+  }
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+                              const SymbolOccurrence &Occurrence) {
+  OS << Occurrence.Location << ":" << Occurrence.Kind;
+  return OS;
+}
+
+void SymbolOccurrenceSlab::insert(const SymbolID &SymID,
+                                  const SymbolOccurrence &Occurrence) {
+  assert(!Frozen &&
+         "Can't insert a symbol occurrence after the slab has been frozen!");
+  auto& SymOccurrences = Occurrences[SymID];
+  SymOccurrences.push_back(Occurrence);
+  SymOccurrences.back().Location.FileURI =
+      UniqueStrings.save(Occurrence.Location.FileURI);
+}
+
+void SymbolOccurrenceSlab::freeze() {
+  // Deduplicate symbol occurrenes.
+  for (auto &IDAndOccurrence : Occurrences) {
+    auto &Occurrence = IDAndOccurrence.getSecond();
+    std::sort(Occurrence.begin(), Occurrence.end());
+    Occurrence.erase(std::unique(Occurrence.begin(), Occurrence.end()),
+                     Occurrence.end());
+  }
+  Frozen = true;
+}
+
 } // namespace clangd
 } // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to