hokein updated this revision to Diff 161962.
hokein marked 6 inline comments as done.
hokein added a comment.

Address review comments.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50385

Files:
  clangd/index/FileIndex.cpp
  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,14 @@
 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 {
@@ -95,7 +103,7 @@
     assert(AST.hasValue());
     return SymbolCollector::shouldCollectSymbol(
         Qualified ? findDecl(*AST, Name) : findAnyDecl(*AST, Name),
-        AST->getASTContext(), SymbolCollector::Options());
+        AST->getASTContext());
   }
 
 protected:
@@ -162,8 +170,10 @@
 class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
 public:
   SymbolIndexActionFactory(SymbolCollector::Options COpts,
-                           CommentHandler *PragmaHandler)
-      : COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {}
+                           CommentHandler *PragmaHandler,
+                           index::IndexingOptions IndexOpts)
+      : COpts(std::move(COpts)), PragmaHandler(PragmaHandler),
+        IndexOpts(IndexOpts) {}
 
   clang::FrontendAction *create() override {
     class WrappedIndexAction : public WrapperFrontendAction {
@@ -186,18 +196,16 @@
       index::IndexingOptions IndexOpts;
       CommentHandler *PragmaHandler;
     };
-    index::IndexingOptions IndexOpts;
-    IndexOpts.SystemSymbolFilter =
-        index::IndexingOptions::SystemSymbolFilterKind::All;
-    IndexOpts.IndexFunctionLocals = false;
     Collector = std::make_shared<SymbolCollector>(COpts);
     return new WrappedIndexAction(Collector, std::move(IndexOpts),
                                   PragmaHandler);
   }
 
   std::shared_ptr<SymbolCollector> Collector;
   SymbolCollector::Options COpts;
   CommentHandler *PragmaHandler;
+
+  index::IndexingOptions IndexOpts;
 };
 
 class SymbolCollectorTest : public ::testing::Test {
@@ -210,13 +218,52 @@
     TestFileURI = URI::createFile(TestFileName).toString();
   }
 
+  bool collectSymbols(StringRef HeaderCode, StringRef MainCode,
+                      const std::vector<std::string> &ExtraArgs = {}) {
+    index::IndexingOptions IndexOpts;
+    IndexOpts.SystemSymbolFilter =
+        index::IndexingOptions::SystemSymbolFilterKind::All;
+    IndexOpts.IndexFunctionLocals = false;
+    CollectorOpts.SymOpts = &CollectSymOpts;
+    CollectorOpts.OccurrenceOpts = nullptr;
+    return runSymbolCollector(HeaderCode, MainCode, CollectorOpts, IndexOpts,
+                              ExtraArgs);
+  }
+
+  bool collectOccurrences(StringRef HeaderCode, StringRef MainCode,
+                          const std::vector<std::string> &ExtraArgs = {}) {
+    index::IndexingOptions IndexOpts;
+    IndexOpts.SystemSymbolFilter =
+        index::IndexingOptions::SystemSymbolFilterKind::All;
+    IndexOpts.IndexFunctionLocals = true;
+    CollectorOpts.SymOpts = nullptr;
+    CollectorOpts.OccurrenceOpts = &CollectOccurrenceOpts;
+    return runSymbolCollector(HeaderCode, MainCode, CollectorOpts, IndexOpts,
+                              ExtraArgs);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem;
+  std::string TestHeaderName;
+  std::string TestHeaderURI;
+  std::string TestFileName;
+  std::string TestFileURI;
+  SymbolSlab Symbols;
+  SymbolCollector::Options CollectorOpts;
+  SymbolCollector::Options::CollectSymbolOptions CollectSymOpts;
+  SymbolCollector::Options::CollectOccurrenceOptions CollectOccurrenceOpts;
+  std::unique_ptr<CommentHandler> PragmaHandler;
+
+private:
   bool runSymbolCollector(StringRef HeaderCode, StringRef MainCode,
+                          SymbolCollector::Options Opts,
+                          index::IndexingOptions IndexOpts,
                           const std::vector<std::string> &ExtraArgs = {}) {
     llvm::IntrusiveRefCntPtr<FileManager> Files(
         new FileManager(FileSystemOptions(), InMemoryFileSystem));
 
     auto Factory = llvm::make_unique<SymbolIndexActionFactory>(
-        CollectorOpts, PragmaHandler.get());
+        Opts, PragmaHandler.get(), std::move(IndexOpts));
 
     std::vector<std::string> Args = {
         "symbol_collector", "-fsyntax-only", "-xc++",
@@ -239,16 +286,6 @@
     Symbols = Factory->Collector->takeSymbols();
     return true;
   }
-
-protected:
-  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem;
-  std::string TestHeaderName;
-  std::string TestHeaderURI;
-  std::string TestFileName;
-  std::string TestFileURI;
-  SymbolSlab Symbols;
-  SymbolCollector::Options CollectorOpts;
-  std::unique_ptr<CommentHandler> PragmaHandler;
 };
 
 TEST_F(SymbolCollectorTest, CollectSymbols) {
@@ -300,7 +337,7 @@
     using bar::v2;
     } // namespace foo
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAreArray(
                   {AllOf(QName("Foo"), ForCodeCompletion(true)),
@@ -334,7 +371,7 @@
     extern template struct Tmpl<float>;
     template struct Tmpl<double>;
   )");
-  runSymbolCollector(Header.code(), /*Main=*/"");
+  collectSymbols(Header.code(), /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAreArray(
                   {AllOf(QName("Tmpl"), DeclRange(Header.range())),
@@ -369,7 +406,7 @@
     @end
   )";
   TestFileName = "test.m";
-  runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
+  collectSymbols(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(
                   QName("Person"), QName("Person::someMethodName:lastName:"),
@@ -398,7 +435,7 @@
     // Declared/defined in main only.
     int Y;
   )cpp");
-  runSymbolCollector(Header.code(), Main.code());
+  collectSymbols(Header.code(), Main.code());
   EXPECT_THAT(
       Symbols,
       UnorderedElementsAre(
@@ -431,17 +468,18 @@
     class Y{}; // definition doesn't count as a reference
     GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
   )";
-  CollectorOpts.CountReferences = true;
-  runSymbolCollector(Header, Main);
+
+  CollectSymOpts.CountReferences = true;
+  collectSymbols(Header, Main);
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(AllOf(QName("W"), Refs(1)),
                                    AllOf(QName("X"), Refs(1)),
                                    AllOf(QName("Y"), Refs(0)),
                                    AllOf(QName("Z"), Refs(0)), QName("y")));
 }
 
 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
-  runSymbolCollector("class Foo {};", /*Main=*/"");
+  collectSymbols("class Foo {};", /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
 }
@@ -451,7 +489,7 @@
   TestFileName = "x.cpp";
   TestHeaderURI = URI::createFile(testPath(TestHeaderName)).toString();
   CollectorOpts.FallbackDir = testRoot();
-  runSymbolCollector("class Foo {};", /*Main=*/"");
+  collectSymbols("class Foo {};", /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
 }
@@ -461,22 +499,22 @@
   CollectorOpts.URISchemes.insert(CollectorOpts.URISchemes.begin(), "unittest");
   TestHeaderName = testPath("x.h");
   TestFileName = testPath("x.cpp");
-  runSymbolCollector("class Foo {};", /*Main=*/"");
+  collectSymbols("class Foo {};", /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            AllOf(QName("Foo"), DeclURI("unittest:///x.h"))));
 }
 
 TEST_F(SymbolCollectorTest, InvalidURIScheme) {
   // Use test URI scheme from URITests.cpp
   CollectorOpts.URISchemes = {"invalid"};
-  runSymbolCollector("class Foo {};", /*Main=*/"");
+  collectSymbols("class Foo {};", /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(""))));
 }
 
 TEST_F(SymbolCollectorTest, FallbackToFileURI) {
   // Use test URI scheme from URITests.cpp
   CollectorOpts.URISchemes = {"invalid", "file"};
-  runSymbolCollector("class Foo {};", /*Main=*/"");
+  collectSymbols("class Foo {};", /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
 }
@@ -498,7 +536,7 @@
     };
     }
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(
                   AllOf(QName("Red"), ForCodeCompletion(true)),
@@ -516,7 +554,7 @@
       int a;
     } Foo;
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"),
                                             QName("(anonymous struct)::a")));
 }
@@ -535,7 +573,7 @@
     FF2();
   )");
 
-  runSymbolCollector(Header.code(), /*Main=*/"");
+  collectSymbols(Header.code(), /*Main=*/"");
   EXPECT_THAT(
       Symbols,
       UnorderedElementsAre(
@@ -552,8 +590,8 @@
     #endif
   )");
 
-  runSymbolCollector(Header.code(), /*Main=*/"",
-                     /*ExtraArgs=*/{"-DNAME=name"});
+  collectSymbols(Header.code(), /*Main=*/"",
+                 /*ExtraArgs=*/{"-DNAME=name"});
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(AllOf(
                   QName("name"),
@@ -574,7 +612,7 @@
     void main_f() {} // ignore
     void f1() {}
   )";
-  runSymbolCollector(Header, Main);
+  collectSymbols(Header, Main);
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(QName("Foo"), QName("f1"), QName("f2")));
 }
@@ -593,7 +631,7 @@
     void Foo::g() {}
     void Foo::ssf() {}
   )";
-  runSymbolCollector(Header, Main);
+  collectSymbols(Header, Main);
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(QName("Foo"), QName("Foo::f"),
                                    QName("Foo::g"), QName("Foo::sf"),
@@ -609,7 +647,7 @@
     }
     }
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(QName("na"), QName("na::nb"),
                                    QName("na::Foo"), QName("na::nb::Bar")));
@@ -622,7 +660,7 @@
     extern "C" { class Bar {}; }
     }
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(QName("na"), QName("Foo"),
                                             QName("na::Bar")));
 }
@@ -641,7 +679,7 @@
     }
     }
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(QName("na"), QName("na::nb"),
                                    QName("na::Foo"), QName("na::Bar")));
@@ -654,7 +692,7 @@
     int ff(int x, double y) { return 0; }
     }
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(
       Symbols,
       UnorderedElementsAre(
@@ -669,7 +707,7 @@
     int ff(int x, double y) { return 0; }
     }
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(
                   QName("nx"),
@@ -749,35 +787,35 @@
 }
 
 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
-  CollectorOpts.CollectIncludePath = true;
-  runSymbolCollector("class Foo {};", /*Main=*/"");
+  CollectSymOpts.CollectIncludePath = true;
+  collectSymbols("class Foo {};", /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(TestHeaderURI),
                                          IncludeHeader(TestHeaderURI))));
 }
 
 #ifndef _WIN32
 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   CanonicalIncludes Includes;
   addSystemHeadersMapping(&Includes);
-  CollectorOpts.Includes = &Includes;
+  CollectSymOpts.Includes = &Includes;
   // bits/basic_string.h$ should be mapped to <string>
   TestHeaderName = "/nasty/bits/basic_string.h";
   TestFileName = "/nasty/bits/basic_string.cpp";
   TestHeaderURI = URI::createFile(TestHeaderName).toString();
-  runSymbolCollector("class string {};", /*Main=*/"");
+  collectSymbols("class string {};", /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("string"),
                                                   DeclURI(TestHeaderURI),
                                                   IncludeHeader("<string>"))));
 }
 #endif
 
 TEST_F(SymbolCollectorTest, STLiosfwd) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   CanonicalIncludes Includes;
   addSystemHeadersMapping(&Includes);
-  CollectorOpts.Includes = &Includes;
+  CollectSymOpts.Includes = &Includes;
   // Symbols from <iosfwd> should be mapped individually.
   TestHeaderName = testPath("iosfwd");
   TestFileName = testPath("iosfwd.cpp");
@@ -789,7 +827,7 @@
       class filebuf {};
     } // namespace std
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(
                   QName("std"),
@@ -800,101 +838,101 @@
 }
 
 TEST_F(SymbolCollectorTest, IWYUPragma) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   CanonicalIncludes Includes;
   PragmaHandler = collectIWYUHeaderMaps(&Includes);
-  CollectorOpts.Includes = &Includes;
+  CollectSymOpts.Includes = &Includes;
   const std::string Header = R"(
     // IWYU pragma: private, include the/good/header.h
     class Foo {};
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            AllOf(QName("Foo"), DeclURI(TestHeaderURI),
                                  IncludeHeader("\"the/good/header.h\""))));
 }
 
 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   CanonicalIncludes Includes;
   PragmaHandler = collectIWYUHeaderMaps(&Includes);
-  CollectorOpts.Includes = &Includes;
+  CollectSymOpts.Includes = &Includes;
   const std::string Header = R"(
     // IWYU pragma: private, include "the/good/header.h"
     class Foo {};
   )";
-  runSymbolCollector(Header, /*Main=*/"");
+  collectSymbols(Header, /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            AllOf(QName("Foo"), DeclURI(TestHeaderURI),
                                  IncludeHeader("\"the/good/header.h\""))));
 }
 
 TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   CanonicalIncludes Includes;
   Includes.addMapping(TestHeaderName, "<canonical>");
-  CollectorOpts.Includes = &Includes;
+  CollectSymOpts.Includes = &Includes;
   auto IncFile = testPath("test.inc");
   auto IncURI = URI::createFile(IncFile).toString();
   InMemoryFileSystem->addFile(IncFile, 0,
                               llvm::MemoryBuffer::getMemBuffer("class X {};"));
-  runSymbolCollector("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
-                     /*ExtraArgs=*/{"-I", testRoot()});
+  collectSymbols("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
+                 /*ExtraArgs=*/{"-I", testRoot()});
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
                                          IncludeHeader("<canonical>")),
                                    AllOf(QName("Y"), DeclURI(TestHeaderURI),
                                          IncludeHeader("<canonical>"))));
 }
 
 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   CanonicalIncludes Includes;
-  CollectorOpts.Includes = &Includes;
+  CollectSymOpts.Includes = &Includes;
   TestFileName = testPath("main.h");
   TestFileURI = URI::createFile(TestFileName).toString();
   auto IncFile = testPath("test.inc");
   auto IncURI = URI::createFile(IncFile).toString();
   InMemoryFileSystem->addFile(IncFile, 0,
                               llvm::MemoryBuffer::getMemBuffer("class X {};"));
-  runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
-                     /*ExtraArgs=*/{"-I", testRoot()});
+  collectSymbols("", /*Main=*/"#include \"test.inc\"",
+                 /*ExtraArgs=*/{"-I", testRoot()});
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
                                                   IncludeHeader(TestFileURI))));
 }
 
 TEST_F(SymbolCollectorTest, MainFileIsHeaderWithoutExtensionWhenSkipIncFile) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   CanonicalIncludes Includes;
-  CollectorOpts.Includes = &Includes;
+  CollectSymOpts.Includes = &Includes;
   TestFileName = testPath("no_ext_main");
   TestFileURI = URI::createFile(TestFileName).toString();
   auto IncFile = testPath("test.inc");
   auto IncURI = URI::createFile(IncFile).toString();
   InMemoryFileSystem->addFile(IncFile, 0,
                               llvm::MemoryBuffer::getMemBuffer("class X {};"));
-  runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
-                     /*ExtraArgs=*/{"-I", testRoot()});
+  collectSymbols("", /*Main=*/"#include \"test.inc\"",
+                 /*ExtraArgs=*/{"-I", testRoot()});
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
                                                   IncludeHeader(TestFileURI))));
 }
 
 TEST_F(SymbolCollectorTest, FallbackToIncFileWhenIncludingFileIsCC) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   CanonicalIncludes Includes;
-  CollectorOpts.Includes = &Includes;
+  CollectSymOpts.Includes = &Includes;
   auto IncFile = testPath("test.inc");
   auto IncURI = URI::createFile(IncFile).toString();
   InMemoryFileSystem->addFile(IncFile, 0,
                               llvm::MemoryBuffer::getMemBuffer("class X {};"));
-  runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
-                     /*ExtraArgs=*/{"-I", testRoot()});
+  collectSymbols("", /*Main=*/"#include \"test.inc\"",
+                 /*ExtraArgs=*/{"-I", testRoot()});
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
                                                   IncludeHeader(IncURI))));
 }
 
 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   Annotations Header(R"(
     // Forward declarations of TagDecls.
     class C;
@@ -906,7 +944,7 @@
     struct $sdecl[[S]] {};
     union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
   )");
-  runSymbolCollector(Header.code(), /*Main=*/"");
+  collectSymbols(Header.code(), /*Main=*/"");
   EXPECT_THAT(
       Symbols,
       UnorderedElementsAre(
@@ -928,17 +966,18 @@
 }
 
 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
-  CollectorOpts.CollectIncludePath = true;
-  runSymbolCollector(/*Header=*/"class X;", /*Main=*/"class X {};");
+  CollectSymOpts.CollectIncludePath = true;
+
+  collectSymbols(/*Header=*/"class X;", /*Main=*/"class X {};");
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
                            QName("X"), DeclURI(TestHeaderURI),
                            IncludeHeader(TestHeaderURI), DefURI(TestFileURI))));
 }
 
 TEST_F(SymbolCollectorTest, UTF16Character) {
   // ö is 2-bytes.
   Annotations Header(/*Header=*/"class [[pörk]] {};");
-  runSymbolCollector(Header.code(), /*Main=*/"");
+  collectSymbols(Header.code(), /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            AllOf(QName("pörk"), DeclRange(Header.range()))));
 }
@@ -957,7 +996,7 @@
       void $foo[[foo]]();
     }
   )");
-  runSymbolCollector(Header.code(), /*Main=*/"");
+  collectSymbols(Header.code(), /*Main=*/"");
 
   EXPECT_THAT(Symbols,
               UnorderedElementsAre(
@@ -979,21 +1018,21 @@
       friend class Y;
     };
   )";
-  CollectorOpts.CountReferences = true;
-  runSymbolCollector(Header, Main);
+  CollectSymOpts.CountReferences = true;
+  collectSymbols(Header, Main);
   EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), Refs(1)),
                                             AllOf(QName("Y"), Refs(1))));
 }
 
 TEST_F(SymbolCollectorTest, Origin) {
-  CollectorOpts.Origin = SymbolOrigin::Static;
-  runSymbolCollector("class Foo {};", /*Main=*/"");
+  CollectSymOpts.Origin = SymbolOrigin::Static;
+  collectSymbols("class Foo {};", /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            Field(&Symbol::Origin, SymbolOrigin::Static)));
 }
 
 TEST_F(SymbolCollectorTest, CollectMacros) {
-  CollectorOpts.CollectIncludePath = true;
+  CollectSymOpts.CollectIncludePath = true;
   Annotations Header(R"(
     #define X 1
     #define $mac[[MAC]](x) int x
@@ -1005,9 +1044,9 @@
     #define MAIN 1  // not indexed
     USED(t);
   )";
-  CollectorOpts.CountReferences = true;
-  CollectorOpts.CollectMacro = true;
-  runSymbolCollector(Header.code(), Main);
+  CollectSymOpts.CountReferences = true;
+  CollectSymOpts.CollectMacro = true;
+  collectSymbols(Header.code(), Main);
   EXPECT_THAT(
       Symbols,
       UnorderedElementsAre(
@@ -1018,6 +1057,59 @@
           AllOf(Labeled("USED(y)"), Refs(1), DeclRange(Header.range("used")))));
 }
 
+TEST_F(SymbolCollectorTest, CollectReference) {
+  const std::string Header(R"(
+  class Foo {
+  public:
+    Foo() {}
+    Foo(int);
+  };
+  class Bar;
+  void 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;
+  })");
+
+  auto H = TestTU::withHeaderCode(Header);
+  auto HeaderSymbols = H.headerSymbols();
+  auto Foo = findSymbol(HeaderSymbols, "Foo");
+  auto Bar = findSymbol(HeaderSymbols, "Bar");
+  auto Func = findSymbol(HeaderSymbols, "func");
+
+  CollectOccurrenceOpts.Filter = SymbolOccurrenceKind::Declaration |
+                                 SymbolOccurrenceKind::Definition |
+                                 SymbolOccurrenceKind::Reference;
+  CollectOccurrenceOpts.IDs = llvm::None;
+  collectOccurrences(Header, Main.code());
+  EXPECT_THAT(
+      Symbols.findOccurrences(Foo.ID),
+      testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("foo")));
+  EXPECT_THAT(
+      Symbols.findOccurrences(Bar.ID),
+      testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("bar")));
+  EXPECT_THAT(
+      Symbols.findOccurrences(Func.ID),
+      testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("func")));
+
+  CollectOccurrenceOpts.IDs = {Foo.ID};
+  collectOccurrences(Header, Main.code());
+  EXPECT_THAT(
+      Symbols.findOccurrences(Foo.ID),
+      testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("foo")));
+  EXPECT_THAT(Symbols.findOccurrences(Bar.ID), testing::IsEmpty());
+  EXPECT_THAT(Symbols.findOccurrences(Func.ID), testing::IsEmpty());
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/index/SymbolCollector.h
===================================================================
--- clangd/index/SymbolCollector.h
+++ clangd/index/SymbolCollector.h
@@ -37,42 +37,56 @@
 class SymbolCollector : public index::IndexDataConsumer {
 public:
   struct Options {
+    struct CollectSymbolOptions {
+      bool CollectIncludePath = false;
+      /// If set, this is used to map symbol #include path to a potentially
+      /// different #include path.
+      const CanonicalIncludes *Includes = nullptr;
+      // Populate the Symbol.References field.
+      bool CountReferences = false;
+      // Every symbol collected will be stamped with this origin.
+      SymbolOrigin Origin = SymbolOrigin::Unknown;
+      /// Collect macros.
+      /// Note that SymbolCollector must be run with preprocessor in order to
+      /// collect macros. For example, `indexTopLevelDecls` will not index any
+      /// macro even if this is true.
+      bool CollectMacro = false;
+    };
+    struct CollectOccurrenceOptions {
+      SymbolOccurrenceKind Filter;
+      // A whitelist symbols which will be collected.
+      // If none, all symbol occurrences will be collected.
+      llvm::Optional<llvm::DenseSet<SymbolID>> IDs = llvm::None;
+    };
+
+    /// Specifies URI schemes that can be used to generate URIs for file paths
+    /// in symbols. The list of schemes will be tried in order until a working
+    /// scheme is found. If no scheme works, symbol location will be dropped.
+    std::vector<std::string> URISchemes = {"file"};
+
     /// When symbol paths cannot be resolved to absolute paths (e.g. files in
     /// VFS that does not have absolute path), combine the fallback directory
     /// with symbols' paths to get absolute paths. This must be an absolute
     /// path.
     std::string FallbackDir;
-    /// Specifies URI schemes that can be used to generate URIs for file paths
-    /// in symbols. The list of schemes will be tried in order until a working
-    /// scheme is found. If no scheme works, symbol location will be dropped.
-    std::vector<std::string> URISchemes = {"file"};
-    bool CollectIncludePath = false;
-    /// If set, this is used to map symbol #include path to a potentially
-    /// different #include path.
-    const CanonicalIncludes *Includes = nullptr;
-    // Populate the Symbol.References field.
-    bool CountReferences = false;
-    // Every symbol collected will be stamped with this origin.
-    SymbolOrigin Origin = SymbolOrigin::Unknown;
-    /// Collect macros.
-    /// Note that SymbolCollector must be run with preprocessor in order to
-    /// collect macros. For example, `indexTopLevelDecls` will not index any
-    /// macro even if this is true.
-    bool CollectMacro = false;
+
+    // If not null, SymbolCollector will collect symbols.
+    const CollectSymbolOptions *SymOpts;
+    // If not null, SymbolCollector will collect symbol occurrences.
+    const CollectOccurrenceOptions *OccurrenceOpts;
   };
 
   SymbolCollector(Options Opts);
 
+  ~SymbolCollector();
+
   /// Returns true is \p ND should be collected.
   /// AST matchers require non-const ASTContext.
-  static bool shouldCollectSymbol(const NamedDecl &ND, ASTContext &ASTCtx,
-                                  const Options &Opts);
+  static bool shouldCollectSymbol(const NamedDecl &ND, ASTContext &ASTCtx);
 
   void initialize(ASTContext &Ctx) override;
 
-  void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {
-    this->PP = std::move(PP);
-  }
+  void setPreprocessor(std::shared_ptr<Preprocessor> PP) override;
 
   bool
   handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
@@ -89,25 +103,16 @@
   void finish() override;
 
 private:
-  const Symbol *addDeclaration(const NamedDecl &, SymbolID);
-  void addDefinition(const NamedDecl &, const Symbol &DeclSymbol);
+  Options Opts;
 
-  // All Symbols collected from the AST.
-  SymbolSlab::Builder Symbols;
-  ASTContext *ASTCtx;
   std::shared_ptr<Preprocessor> PP;
-  std::shared_ptr<GlobalCodeCompletionAllocator> CompletionAllocator;
-  std::unique_ptr<CodeCompletionTUInfo> CompletionTUInfo;
-  Options Opts;
-  // Symbols referenced from the current TU, flushed on finish().
-  llvm::DenseSet<const NamedDecl *> ReferencedDecls;
-  llvm::DenseSet<const IdentifierInfo *> ReferencedMacros;
-  // Maps canonical declaration provided by clang to canonical declaration for
-  // an index symbol, if clangd prefers a different declaration than that
-  // provided by clang. For example, friend declaration might be considered
-  // canonical by clang but should not be considered canonical in the index
-  // unless it's a definition.
-  llvm::DenseMap<const Decl *, const Decl *> CanonicalDecls;
+
+  class CollectSymbol;
+  class CollectOccurrence;
+  std::unique_ptr<CollectSymbol> CollectSym;
+  std::unique_ptr<CollectOccurrence> CollectOccur;
+  // All symbols and symbol occurrences collected from the AST.
+  SymbolSlab::Builder Symbols;
 };
 
 } // namespace clangd
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -174,8 +174,9 @@
   if (Headers.empty())
     return llvm::None;
   llvm::StringRef Header = Headers[0];
-  if (Opts.Includes) {
-    Header = Opts.Includes->mapHeader(Headers, QName);
+  assert(Opts.SymOpts && "SymbolOptions must be set.");
+  if (Opts.SymOpts->Includes) {
+    Header = Opts.SymOpts->Includes->mapHeader(Headers, QName);
     if (Header.startswith("<") || Header.startswith("\""))
       return Header.str();
   }
@@ -224,20 +225,144 @@
          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)) {}
+class SymbolCollector::CollectOccurrence {
+public:
+  CollectOccurrence(const SymbolCollector::Options &CollectorOpts,
+                    SymbolSlab::Builder *Builder)
+      : Opts(CollectorOpts), Builder(Builder) {
+    assert(Opts.OccurrenceOpts && "Occurrence options must be set.");
+  }
+
+  void initialize(ASTContext &Ctx) { ASTCtx = &Ctx; }
+
+  void collectDecl(const Decl *D, index::SymbolRoleSet Roles,
+                   ArrayRef<index::SymbolRelation> Relations,
+                   SourceLocation Loc,
+                   index::IndexDataConsumer::ASTNodeInfo ASTNode) {
+    assert(ASTCtx && "ASTContext must be set.");
+    if (D->isImplicit())
+      return;
+
+    // We only collect symbol occurrences in current main file.
+    if (!ASTCtx->getSourceManager().isInMainFile(Loc))
+      return;
+    std::string FileURI;
+    auto AddOccurrence = [&](SourceLocation L, const SymbolID &ID) {
+      if (auto Location =
+              getTokenLocation(Loc, ASTCtx->getSourceManager(), Opts,
+                               ASTCtx->getLangOpts(), FileURI)) {
+        SymbolOccurrence Occurrence;
+        Occurrence.Location = *Location;
+        Occurrence.Kind = ToOccurrenceKind(Roles);
+        Builder->insert(ID, Occurrence);
+      }
+    };
+    if (!(static_cast<unsigned>(Opts.OccurrenceOpts->Filter) & Roles))
+      return;
+
+    if (auto ID = getSymbolID(D)) {
+      if (!Opts.OccurrenceOpts->IDs ||
+          llvm::is_contained(*Opts.OccurrenceOpts->IDs, *ID))
+        AddOccurrence(Loc, *ID);
+    }
+  }
+
+private:
+  const SymbolCollector::Options &Opts;
+
+  SymbolSlab::Builder *Builder;
+  ASTContext *ASTCtx;
+};
+
+class SymbolCollector::CollectSymbol {
+public:
+  CollectSymbol(const SymbolCollector::Options &CollectorOpts,
+                SymbolSlab::Builder *Builder)
+      : Opts(CollectorOpts), Symbols(Builder) {
+    assert(Opts.SymOpts && "Symbol option must be set.");
+  }
+
+  void collectDecl(const Decl *D, index::SymbolRoleSet Roles,
+                   ArrayRef<index::SymbolRelation> Relations,
+                   SourceLocation Loc,
+                   index::IndexDataConsumer::ASTNodeInfo ASTNode);
+
+  void collectMacro(const IdentifierInfo *Name, const MacroInfo *MI,
+                    index::SymbolRoleSet Roles, SourceLocation Loc);
+
+  void initialize(ASTContext &Ctx) {
+    ASTCtx = &Ctx;
+    CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
+    CompletionTUInfo =
+        llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
+  }
+
+  void setPreprocessor(std::shared_ptr<Preprocessor> PP) {
+    this->PP = std::move(PP);
+  }
+
+  void finish();
+
+private:
+  const Symbol *addDeclaration(const NamedDecl &, SymbolID);
+  void addDefinition(const NamedDecl &ND, const Symbol &DeclSym);
+
+  const SymbolCollector::Options &Opts;
+
+  SymbolSlab::Builder *Symbols;
+  ASTContext *ASTCtx;
+
+  std::shared_ptr<Preprocessor> PP;
+  std::shared_ptr<GlobalCodeCompletionAllocator> CompletionAllocator;
+  std::unique_ptr<CodeCompletionTUInfo> CompletionTUInfo;
+  // Symbols referenced from the current TU, flushed on finish().
+  llvm::DenseSet<const NamedDecl *> ReferencedDecls;
+  llvm::DenseSet<const IdentifierInfo *> ReferencedMacros;
+  // Maps canonical declaration provided by clang to canonical declaration for
+  // an index symbol, if clangd prefers a different declaration than that
+  // provided by clang. For example, friend declaration might be considered
+  // canonical by clang but should not be considered canonical in the index
+  // unless it's a definition.
+  llvm::DenseMap<const Decl *, const Decl *> CanonicalDecls;
+};
+
+SymbolCollector::SymbolCollector(Options CollectorOpts)
+    : Opts(std::move(CollectorOpts)) {
+  if (this->Opts.SymOpts)
+    CollectSym = llvm::make_unique<CollectSymbol>(Opts, &Symbols);
+  if (this->Opts.OccurrenceOpts)
+    CollectOccur = llvm::make_unique<CollectOccurrence>(Opts, &Symbols);
+}
+
+SymbolCollector::~SymbolCollector() {}
 
 void SymbolCollector::initialize(ASTContext &Ctx) {
-  ASTCtx = &Ctx;
-  CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
-  CompletionTUInfo =
-      llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
+  if (CollectSym)
+    CollectSym->initialize(Ctx);
+  if (CollectOccur)
+    CollectOccur->initialize(Ctx);
+}
+
+void SymbolCollector::setPreprocessor(std::shared_ptr<Preprocessor> PP) {
+  if (CollectSym)
+    CollectSym->setPreprocessor(PP);
 }
 
 bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND,
-                                          ASTContext &ASTCtx,
-                                          const Options &Opts) {
+                                          ASTContext &ASTCtx) {
   using namespace clang::ast_matchers;
   if (ND.isImplicit())
     return false;
@@ -282,7 +407,7 @@
 }
 
 // Always return true to continue indexing.
-bool SymbolCollector::handleDeclOccurence(
+void SymbolCollector::CollectSymbol::collectDecl(
     const Decl *D, index::SymbolRoleSet Roles,
     ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
     index::IndexDataConsumer::ASTNodeInfo ASTNode) {
@@ -295,37 +420,37 @@
   if ((ASTNode.OrigD->getFriendObjectKind() !=
        Decl::FriendObjectKind::FOK_None) &&
       !(Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
-    return true;
+    return;
   // A declaration created for a friend declaration should not be used as the
   // canonical declaration in the index. Use OrigD instead, unless we've already
   // picked a replacement for D
   if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
     D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second;
   const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D);
   if (!ND)
-    return true;
+    return;
 
   // Mark D as referenced if this is a reference coming from the main file.
   // D may not be an interesting symbol, but it's cheaper to check at the end.
   auto &SM = ASTCtx->getSourceManager();
-  if (Opts.CountReferences &&
+  if (Opts.SymOpts->CountReferences &&
       (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
       SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
     ReferencedDecls.insert(ND);
 
   // 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)))
-    return true;
-  if (!shouldCollectSymbol(*ND, *ASTCtx, Opts))
-    return true;
+    return;
+  if (!shouldCollectSymbol(*ND, *ASTCtx))
+    return;
 
   auto ID = getSymbolID(ND);
   if (!ID)
-    return true;
+    return;
 
   const NamedDecl &OriginalDecl = *cast<NamedDecl>(ASTNode.OrigD);
-  const Symbol *BasicSymbol = Symbols.find(*ID);
+  const Symbol *BasicSymbol = Symbols->find(*ID);
   if (!BasicSymbol) // Regardless of role, ND is the canonical declaration.
     BasicSymbol = addDeclaration(*ND, std::move(*ID));
   else if (isPreferredDeclaration(OriginalDecl, Roles))
@@ -337,47 +462,46 @@
 
   if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
     addDefinition(OriginalDecl, *BasicSymbol);
-  return true;
 }
 
-bool SymbolCollector::handleMacroOccurence(const IdentifierInfo *Name,
-                                           const MacroInfo *MI,
-                                           index::SymbolRoleSet Roles,
-                                           SourceLocation Loc) {
-  if (!Opts.CollectMacro)
-    return true;
+void SymbolCollector::CollectSymbol::collectMacro(const IdentifierInfo *Name,
+                                                  const MacroInfo *MI,
+                                                  index::SymbolRoleSet Roles,
+                                                  SourceLocation Loc) {
+  if (!Opts.SymOpts->CollectMacro)
+    return;
   assert(PP.get());
 
   const auto &SM = PP->getSourceManager();
   if (SM.isInMainFile(SM.getExpansionLoc(MI->getDefinitionLoc())))
-    return true;
+    return;
   // Header guards are not interesting in index. Builtin macros don't have
   // useful locations and are not needed for code completions.
   if (MI->isUsedForHeaderGuard() || MI->isBuiltinMacro())
-    return true;
+    return;
 
   // Mark the macro as referenced if this is a reference coming from the main
   // file. The macro may not be an interesting symbol, but it's cheaper to check
   // at the end.
-  if (Opts.CountReferences &&
+  if (Opts.SymOpts->CountReferences &&
       (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
       SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
     ReferencedMacros.insert(Name);
   // Don't continue indexing if this is a mere reference.
   // FIXME: remove macro with ID if it is undefined.
   if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
         Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
-    return true;
+    return;
 
   llvm::SmallString<128> USR;
   if (index::generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), SM,
                                  USR))
-    return true;
+    return;
   SymbolID ID(USR);
 
   // Only collect one instance in case there are multiple.
-  if (Symbols.find(ID) != nullptr)
-    return true;
+  if (Symbols->find(ID) != nullptr)
+    return;
 
   Symbol S;
   S.ID = std::move(ID);
@@ -397,7 +521,8 @@
   getSignature(*CCS, &Signature, &SnippetSuffix);
 
   std::string Include;
-  if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) {
+  if (Opts.SymOpts->CollectIncludePath &&
+      shouldCollectIncludePath(S.SymInfo.Kind)) {
     if (auto Header =
             getIncludeHeader(Name->getName(), SM,
                              SM.getExpansionLoc(MI->getDefinitionLoc()), Opts))
@@ -408,25 +533,24 @@
   Symbol::Details Detail;
   Detail.IncludeHeader = Include;
   S.Detail = &Detail;
-  Symbols.insert(S);
-  return true;
+  Symbols->insert(S);
 }
 
-void SymbolCollector::finish() {
+void SymbolCollector::CollectSymbol::finish() {
   // At the end of the TU, add 1 to the refcount of all referenced symbols.
   auto IncRef = [this](const SymbolID &ID) {
-    if (const auto *S = Symbols.find(ID)) {
+    if (const auto *S = Symbols->find(ID)) {
       Symbol Inc = *S;
       ++Inc.References;
-      Symbols.insert(Inc);
+      Symbols->insert(Inc);
     }
   };
   for (const NamedDecl *ND : ReferencedDecls) {
     if (auto ID = getSymbolID(ND)) {
       IncRef(*ID);
     }
   }
-  if (Opts.CollectMacro) {
+  if (Opts.SymOpts->CollectMacro) {
     assert(PP);
     for (const IdentifierInfo *II : ReferencedMacros) {
       llvm::SmallString<128> USR;
@@ -440,8 +564,9 @@
   ReferencedMacros.clear();
 }
 
-const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND,
-                                              SymbolID ID) {
+const Symbol *
+SymbolCollector::CollectSymbol::addDeclaration(const NamedDecl &ND,
+                                               SymbolID ID) {
   auto &Ctx = ND.getASTContext();
   auto &SM = Ctx.getSourceManager();
 
@@ -477,7 +602,8 @@
   std::string ReturnType = getReturnType(*CCS);
 
   std::string Include;
-  if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) {
+  if (Opts.SymOpts->CollectIncludePath &&
+      shouldCollectIncludePath(S.SymInfo.Kind)) {
     // Use the expansion location to get the #include header since this is
     // where the symbol is exposed.
     if (auto Header = getIncludeHeader(
@@ -492,13 +618,13 @@
   Detail.IncludeHeader = Include;
   S.Detail = &Detail;
 
-  S.Origin = Opts.Origin;
-  Symbols.insert(S);
-  return Symbols.find(S.ID);
+  S.Origin = Opts.SymOpts->Origin;
+  Symbols->insert(S);
+  return Symbols->find(S.ID);
 }
 
-void SymbolCollector::addDefinition(const NamedDecl &ND,
-                                    const Symbol &DeclSym) {
+void SymbolCollector::CollectSymbol::addDefinition(const NamedDecl &ND,
+                                                   const Symbol &DeclSym) {
   if (DeclSym.Definition)
     return;
   // If we saw some forward declaration, we end up copying the symbol.
@@ -510,7 +636,32 @@
                                      ND.getASTContext().getSourceManager(),
                                      Opts, ASTCtx->getLangOpts(), FileURI))
     S.Definition = *DefLoc;
-  Symbols.insert(S);
+  Symbols->insert(S);
+}
+
+bool SymbolCollector::handleDeclOccurence(
+    const Decl *D, index::SymbolRoleSet Roles,
+    ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
+    index::IndexDataConsumer::ASTNodeInfo ASTNode) {
+  if (CollectSym)
+    CollectSym->collectDecl(D, Roles, Relations, Loc, ASTNode);
+  if (CollectOccur)
+    CollectOccur->collectDecl(D, Roles, Relations, Loc, ASTNode);
+  return true;
+}
+
+bool SymbolCollector::handleMacroOccurence(const IdentifierInfo *Name,
+                                           const MacroInfo *MI,
+                                           index::SymbolRoleSet Roles,
+                                           SourceLocation Loc) {
+  if (CollectSym)
+    CollectSym->collectMacro(Name, MI, Roles, Loc);
+  return true;
+}
+
+void SymbolCollector::finish() {
+  if (CollectSym)
+    CollectSym->finish();
 }
 
 } // namespace clangd
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,23 @@
   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).
@@ -234,6 +243,48 @@
 //        and signals -> score, so it can be reused for Sema completions.
 double quality(const Symbol &S);
 
+// Describes the kind of a symbol occurrence.
+//
+// This is a bitfield which can be combined from different kinds.
+enum class SymbolOccurrenceKind : uint8_t {
+  Unknown = 0,
+  Declaration = static_cast<uint8_t>(index::SymbolRole::Declaration),
+  Definition = static_cast<uint8_t>(index::SymbolRole::Definition),
+  Reference = static_cast<uint8_t>(index::SymbolRole::Reference),
+};
+inline SymbolOccurrenceKind operator|(SymbolOccurrenceKind L,
+                                      SymbolOccurrenceKind R) {
+  return static_cast<SymbolOccurrenceKind>(static_cast<uint8_t>(L) |
+                                           static_cast<uint8_t>(R));
+}
+inline SymbolOccurrenceKind &operator|=(SymbolOccurrenceKind &L,
+                                        SymbolOccurrenceKind R) {
+  return L = L | R;
+}
+inline SymbolOccurrenceKind operator&(SymbolOccurrenceKind A,
+                                      SymbolOccurrenceKind B) {
+  return static_cast<SymbolOccurrenceKind>(static_cast<uint8_t>(A) &
+                                           static_cast<uint8_t>(B));
+}
+
+// Represents a symbol occurrence in the source file. It could be a
+// declaration/definition/reference occurrence.
+//
+// WARNING: Location does not own the underlying data - Copies are shallow.
+struct SymbolOccurrence {
+  // The location of the occurrence.
+  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 immutable symbol container that stores a set of symbols.
 // The container will maintain the lifetime of the symbols.
 class SymbolSlab {
@@ -251,7 +302,15 @@
   // Estimates the total memory usage.
   size_t bytes() const {
     return sizeof(*this) + Arena.getTotalMemory() +
-           Symbols.capacity() * sizeof(Symbol);
+           Symbols.capacity() * sizeof(Symbol) +
+           SymbolOccurrences.getMemorySize();
+  }
+
+  llvm::ArrayRef<SymbolOccurrence> findOccurrences(const SymbolID &ID) const {
+    auto It = SymbolOccurrences.find(ID);
+    if (It == SymbolOccurrences.end())
+      return {};
+    return It->second;
   }
 
   // SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
@@ -264,6 +323,10 @@
     // This is a deep copy: underlying strings will be owned by the slab.
     void insert(const Symbol &S);
 
+    // Adds a symbol occurrence.
+    // This is a deep copy: underlying strings will be owned by the slab.
+    void insert(const SymbolID &ID, SymbolOccurrence Occurrence);
+
     // Returns the symbol with an ID, if it exists. Valid until next insert().
     const Symbol *find(const SymbolID &ID) {
       auto I = SymbolIndex.find(ID);
@@ -280,48 +343,19 @@
     std::vector<Symbol> Symbols;
     // Values are indices into Symbols vector.
     llvm::DenseMap<SymbolID, size_t> SymbolIndex;
+    llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>> SymbolOccurrences;
   };
 
 private:
-  SymbolSlab(llvm::BumpPtrAllocator Arena, std::vector<Symbol> Symbols)
-      : Arena(std::move(Arena)), Symbols(std::move(Symbols)) {}
+  SymbolSlab(
+      llvm::BumpPtrAllocator Arena, std::vector<Symbol> Symbols,
+      llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>> SymbolOccurrences)
+      : Arena(std::move(Arena)), Symbols(std::move(Symbols)),
+        SymbolOccurrences(std::move(SymbolOccurrences)) {}
 
   llvm::BumpPtrAllocator Arena; // Owns Symbol data that the Symbols do not.
   std::vector<Symbol> Symbols;  // Sorted by SymbolID to allow lookup.
-};
-
-// Describes the kind of a symbol occurrence.
-//
-// This is a bitfield which can be combined from different kinds.
-enum class SymbolOccurrenceKind : uint8_t {
-  Unknown = 0,
-  Declaration = static_cast<uint8_t>(index::SymbolRole::Declaration),
-  Definition = static_cast<uint8_t>(index::SymbolRole::Definition),
-  Reference = static_cast<uint8_t>(index::SymbolRole::Reference),
-};
-inline SymbolOccurrenceKind operator|(SymbolOccurrenceKind L,
-                                      SymbolOccurrenceKind R) {
-  return static_cast<SymbolOccurrenceKind>(static_cast<uint8_t>(L) |
-                                           static_cast<uint8_t>(R));
-}
-inline SymbolOccurrenceKind &operator|=(SymbolOccurrenceKind &L,
-                                        SymbolOccurrenceKind R) {
-  return L = L | R;
-}
-inline SymbolOccurrenceKind operator&(SymbolOccurrenceKind A,
-                                      SymbolOccurrenceKind B) {
-  return static_cast<SymbolOccurrenceKind>(static_cast<uint8_t>(A) &
-                                           static_cast<uint8_t>(B));
-}
-
-// Represents a symbol occurrence in the source file. It could be a
-// declaration/definition/reference occurrence.
-//
-// WARNING: Location does not own the underlying data - Copies are shallow.
-struct SymbolOccurrence {
-  // The location of the occurrence.
-  SymbolLocation Location;
-  SymbolOccurrenceKind Kind = SymbolOccurrenceKind::Unknown;
+  llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>> SymbolOccurrences;
 };
 
 struct FuzzyFindRequest {
Index: clangd/index/Index.cpp
===================================================================
--- clangd/index/Index.cpp
+++ clangd/index/Index.cpp
@@ -114,6 +114,11 @@
     own(Copy, UniqueStrings, Arena);
   }
 }
+void SymbolSlab::Builder::insert(const SymbolID &ID,
+                                 SymbolOccurrence Occurrence) {
+  Occurrence.Location.FileURI = UniqueStrings.save(Occurrence.Location.FileURI);
+  SymbolOccurrences[ID].push_back(std::move(Occurrence));
+}
 
 SymbolSlab SymbolSlab::Builder::build() && {
   Symbols = {Symbols.begin(), Symbols.end()}; // Force shrink-to-fit.
@@ -125,7 +130,32 @@
   llvm::UniqueStringSaver Strings(NewArena);
   for (auto &S : Symbols)
     own(S, Strings, NewArena);
-  return SymbolSlab(std::move(NewArena), std::move(Symbols));
+
+  // We may have duplicated symbol occurrences (as some AST nodes have been
+  // visited multiple times). Deduplicate them.
+  for (auto &IDAndOccurrences : SymbolOccurrences) {
+    auto &Occurrences = IDAndOccurrences.getSecond();
+    std::sort(Occurrences.begin(), Occurrences.end());
+    Occurrences.erase(std::unique(Occurrences.begin(), Occurrences.end()),
+                      Occurrences.end());
+
+    for (auto &O : Occurrences)
+      O.Location.FileURI = UniqueStrings.save(O.Location.FileURI);
+  }
+
+  return SymbolSlab(std::move(NewArena), std::move(Symbols),
+                    std::move(SymbolOccurrences));
+}
+
+raw_ostream &operator<<(raw_ostream &OS, SymbolOccurrenceKind K) {
+  OS << static_cast<unsigned>(K);
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+                              const SymbolOccurrence &Occurrence) {
+  OS << Occurrence.Location << ":" << Occurrence.Kind;
+  return OS;
 }
 
 } // namespace clangd
Index: clangd/index/FileIndex.cpp
===================================================================
--- clangd/index/FileIndex.cpp
+++ clangd/index/FileIndex.cpp
@@ -19,16 +19,18 @@
 SymbolSlab indexAST(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
                     llvm::ArrayRef<std::string> URISchemes) {
   SymbolCollector::Options CollectorOpts;
+  SymbolCollector::Options::CollectSymbolOptions SymbolOpts;
   // FIXME(ioeric): we might also want to collect include headers. We would need
   // to make sure all includes are canonicalized (with CanonicalIncludes), which
   // is not trivial given the current way of collecting symbols: we only have
   // AST at this point, but we also need preprocessor callbacks (e.g.
   // CommentHandler for IWYU pragma) to canonicalize includes.
-  CollectorOpts.CollectIncludePath = false;
-  CollectorOpts.CountReferences = false;
+  SymbolOpts.CollectIncludePath = false;
+  SymbolOpts.CountReferences = false;
   if (!URISchemes.empty())
     CollectorOpts.URISchemes = URISchemes;
-  CollectorOpts.Origin = SymbolOrigin::Dynamic;
+  SymbolOpts.Origin = SymbolOrigin::Dynamic;
+  CollectorOpts.SymOpts = &SymbolOpts;
 
   SymbolCollector Collector(std::move(CollectorOpts));
   Collector.setPreprocessor(PP);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to