hokein created this revision.
hokein added reviewers: ilya-biryukov, ioeric.
Herald added subscribers: kadircet, arphaman, mgrang, jkorous, MaskRay, 
javed.absar, mgorny.

Depends on the AST callback interfaces.

- https://reviews.llvm.org/D50847
- https://reviews.llvm.org/D50889


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50958

Files:
  clangd/CMakeLists.txt
  clangd/ClangdLSPServer.cpp
  clangd/ClangdLSPServer.h
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/Protocol.cpp
  clangd/Protocol.h
  clangd/ProtocolHandlers.cpp
  clangd/ProtocolHandlers.h
  clangd/TUScheduler.cpp
  clangd/TUScheduler.h
  clangd/XRefs.cpp
  clangd/XRefs.h
  clangd/index/FileIndex.cpp
  clangd/index/FileIndex.h
  clangd/index/Index.cpp
  clangd/index/Index.h
  clangd/index/MemIndex.cpp
  clangd/index/Merge.cpp
  clangd/index/SymbolCollector.cpp
  clangd/index/SymbolCollector.h
  test/clangd/initialize-params-invalid.test
  test/clangd/initialize-params.test
  unittests/clangd/CMakeLists.txt
  unittests/clangd/TUSchedulerTests.cpp

Index: unittests/clangd/TUSchedulerTests.cpp
===================================================================
--- unittests/clangd/TUSchedulerTests.cpp
+++ unittests/clangd/TUSchedulerTests.cpp
@@ -17,10 +17,11 @@
 
 namespace clang {
 namespace clangd {
+namespace {
 
 using ::testing::_;
-using ::testing::Each;
 using ::testing::AnyOf;
+using ::testing::Each;
 using ::testing::Pair;
 using ::testing::Pointee;
 using ::testing::UnorderedElementsAre;
@@ -30,6 +31,18 @@
   handleAllErrors(std::move(Err), [](const llvm::ErrorInfoBase &) {});
 }
 
+class NoopParsingCallbacks : public ParsingCallbacks {
+public:
+  static NoopParsingCallbacks& instance() {
+    static NoopParsingCallbacks* Instance = new NoopParsingCallbacks;
+    return *Instance;
+  }
+
+  void onPreambleAST(PathRef Path, ASTContext &Ctx,
+                     std::shared_ptr<clang::Preprocessor> PP) override {}
+  void onMainAST(PathRef Path, ParsedAST &AST) override {}
+};
+
 class TUSchedulerTests : public ::testing::Test {
 protected:
   ParseInputs getInputs(PathRef File, std::string Contents) {
@@ -45,7 +58,7 @@
 TEST_F(TUSchedulerTests, MissingFiles) {
   TUScheduler S(getDefaultAsyncThreadsCount(),
                 /*StorePreamblesInMemory=*/true,
-                /*PreambleParsedCallback=*/nullptr,
+                NoopParsingCallbacks::instance(),
                 /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
                 ASTRetentionPolicy());
 
@@ -102,7 +115,7 @@
     TUScheduler S(
         getDefaultAsyncThreadsCount(),
         /*StorePreamblesInMemory=*/true,
-        /*PreambleParsedCallback=*/nullptr,
+        NoopParsingCallbacks::instance(),
         /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
         ASTRetentionPolicy());
     auto Path = testPath("foo.cpp");
@@ -129,11 +142,10 @@
 TEST_F(TUSchedulerTests, Debounce) {
   std::atomic<int> CallbackCount(0);
   {
-    TUScheduler S(getDefaultAsyncThreadsCount(),
-                  /*StorePreamblesInMemory=*/true,
-                  /*PreambleParsedCallback=*/nullptr,
-                  /*UpdateDebounce=*/std::chrono::seconds(1),
-                  ASTRetentionPolicy());
+    TUScheduler S(
+        getDefaultAsyncThreadsCount(),
+        /*StorePreamblesInMemory=*/true, NoopParsingCallbacks::instance(),
+        /*UpdateDebounce=*/std::chrono::seconds(1), ASTRetentionPolicy());
     // FIXME: we could probably use timeouts lower than 1 second here.
     auto Path = testPath("foo.cpp");
     S.update(Path, getInputs(Path, "auto (debounced)"), WantDiagnostics::Auto,
@@ -163,7 +175,7 @@
   {
     TUScheduler S(getDefaultAsyncThreadsCount(),
                   /*StorePreamblesInMemory=*/true,
-                  /*PreambleParsedCallback=*/nullptr,
+                  NoopParsingCallbacks::instance(),
                   /*UpdateDebounce=*/std::chrono::milliseconds(50),
                   ASTRetentionPolicy());
 
@@ -261,7 +273,7 @@
   Policy.MaxRetainedASTs = 2;
   TUScheduler S(
       /*AsyncThreadsCount=*/1, /*StorePreambleInMemory=*/true,
-      PreambleParsedCallback(),
+      NoopParsingCallbacks::instance(),
       /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), Policy);
 
   llvm::StringLiteral SourceContents = R"cpp(
@@ -358,7 +370,7 @@
   // the same time. All reads should get the same non-null preamble.
   TUScheduler S(
       /*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true,
-      PreambleParsedCallback(),
+      NoopParsingCallbacks::instance(),
       /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
       ASTRetentionPolicy());
   auto Foo = testPath("foo.cpp");
@@ -391,7 +403,7 @@
 TEST_F(TUSchedulerTests, NoopOnEmptyChanges) {
   TUScheduler S(
       /*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(),
-      /*StorePreambleInMemory=*/true, PreambleParsedCallback(),
+      /*StorePreambleInMemory=*/true, NoopParsingCallbacks::instance(),
       /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
       ASTRetentionPolicy());
 
@@ -444,7 +456,7 @@
 TEST_F(TUSchedulerTests, NoChangeDiags) {
   TUScheduler S(
       /*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(),
-      /*StorePreambleInMemory=*/true, PreambleParsedCallback(),
+      /*StorePreambleInMemory=*/true, NoopParsingCallbacks::instance(),
       /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
       ASTRetentionPolicy());
 
@@ -475,5 +487,6 @@
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1)));
 }
 
+} // namespace
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/CMakeLists.txt
===================================================================
--- unittests/clangd/CMakeLists.txt
+++ unittests/clangd/CMakeLists.txt
@@ -27,6 +27,7 @@
   QualityTests.cpp
   SourceCodeTests.cpp
   SymbolCollectorTests.cpp
+  SymbolOccurrenceCollectorTests.cpp
   SyncAPI.cpp
   TUSchedulerTests.cpp
   TestFS.cpp
Index: test/clangd/initialize-params.test
===================================================================
--- test/clangd/initialize-params.test
+++ test/clangd/initialize-params.test
@@ -29,6 +29,7 @@
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
 # CHECK-NEXT:      "hoverProvider": true,
+# CHECK-NEXT:      "referencesProvider": false,
 # CHECK-NEXT:      "renameProvider": true,
 # CHECK-NEXT:      "signatureHelpProvider": {
 # CHECK-NEXT:        "triggerCharacters": [
Index: test/clangd/initialize-params-invalid.test
===================================================================
--- test/clangd/initialize-params-invalid.test
+++ test/clangd/initialize-params-invalid.test
@@ -29,6 +29,7 @@
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
 # CHECK-NEXT:      "hoverProvider": true,
+# CHECK-NEXT:      "referencesProvider": false,
 # CHECK-NEXT:      "renameProvider": true,
 # CHECK-NEXT:      "signatureHelpProvider": {
 # CHECK-NEXT:        "triggerCharacters": [
Index: clangd/index/SymbolCollector.h
===================================================================
--- clangd/index/SymbolCollector.h
+++ clangd/index/SymbolCollector.h
@@ -18,6 +18,7 @@
 namespace clang {
 namespace clangd {
 
+
 /// \brief Collect declarations (symbols) from an AST.
 /// It collects most declarations except:
 /// - Implicit declarations
@@ -37,42 +38,48 @@
 class SymbolCollector : public index::IndexDataConsumer {
 public:
   struct Options {
-    /// 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;
+    struct CollectSymbolOptions {
+      /// 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;
+      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 {
+      // The symbol occurrence kind that will be collected.
+      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;
+    };
+
+    const CollectSymbolOptions* SymbolOpts;
+    const CollectOccurrenceOptions* OccurrenceOpts;
   };
 
-  SymbolCollector(Options Opts);
+  SymbolCollector(Options Opts, std::vector<std::string> URISchemes);
 
+  ~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 +96,22 @@
   void finish() override;
 
 private:
-  const Symbol *addDeclaration(const NamedDecl &, SymbolID);
-  void addDefinition(const NamedDecl &, const Symbol &DeclSymbol);
+  Options Opts;
+
+  /// 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"};
 
-  // 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;
+
+  ASTContext *ASTCtx;
+
+  class CollectSymbol;
+  class CollectOccurrence;
+  std::unique_ptr<CollectSymbol> CollectSym;
+  std::unique_ptr<CollectOccurrence> CollectOccu;
+  SymbolSlab::Builder Symbols;
 };
 
 } // namespace clangd
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -46,8 +46,10 @@
 //
 // The Path can be a path relative to the build directory, or retrieved from
 // the SourceManager.
-llvm::Optional<std::string> toURI(const SourceManager &SM, StringRef Path,
-                                  const SymbolCollector::Options &Opts) {
+llvm::Optional<std::string>
+toURI(const SourceManager &SM, StringRef Path,
+      ArrayRef<std::string> URISchemes,
+      llvm::StringRef FallbackDir) {
   llvm::SmallString<128> AbsolutePath(Path);
   if (std::error_code EC =
           SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
@@ -73,14 +75,14 @@
                               llvm::sys::path::filename(AbsolutePath.str()));
       AbsolutePath = AbsoluteFilename;
     }
-  } else if (!Opts.FallbackDir.empty()) {
-    llvm::sys::fs::make_absolute(Opts.FallbackDir, AbsolutePath);
+  } else if (!FallbackDir.empty()) {
+    llvm::sys::fs::make_absolute(FallbackDir, AbsolutePath);
   }
 
   llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true);
 
   std::string ErrMsg;
-  for (const auto &Scheme : Opts.URISchemes) {
+  for (const auto &Scheme : URISchemes) {
     auto U = URI::create(AbsolutePath, Scheme);
     if (U)
       return U->toString();
@@ -157,7 +159,9 @@
 /// Returns None if fails to get include header for \p Loc.
 llvm::Optional<std::string>
 getIncludeHeader(llvm::StringRef QName, const SourceManager &SM,
-                 SourceLocation Loc, const SymbolCollector::Options &Opts) {
+                 SourceLocation Loc,
+                 ArrayRef<std::string> URISchemes,
+                 const SymbolCollector::Options::CollectSymbolOptions &Opts) {
   std::vector<std::string> Headers;
   // Collect the #include stack.
   while (true) {
@@ -179,16 +183,17 @@
     if (Header.startswith("<") || Header.startswith("\""))
       return Header.str();
   }
-  return toURI(SM, Header, Opts);
+  return toURI(SM, Header, URISchemes, Opts.FallbackDir);
 }
 
 // Return the symbol location of the token at \p Loc.
 llvm::Optional<SymbolLocation>
 getTokenLocation(SourceLocation TokLoc, const SourceManager &SM,
-                 const SymbolCollector::Options &Opts,
+                 ArrayRef<std::string> URISchemes,
+                 llvm::StringRef FallbackDir,
                  const clang::LangOptions &LangOpts,
                  std::string &FileURIStorage) {
-  auto U = toURI(SM, SM.getFilename(TokLoc), Opts);
+  auto U = toURI(SM, SM.getFilename(TokLoc), URISchemes, FallbackDir);
   if (!U)
     return llvm::None;
   FileURIStorage = std::move(*U);
@@ -224,20 +229,155 @@
          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::CollectSymbol {
+ public:
+   CollectSymbol(const SymbolCollector::Options::CollectSymbolOptions &Opts,
+                 ArrayRef<std::string> URISchemes,
+                 SymbolSlab::Builder* Builder)
+       : Opts(Opts), URISchemes(URISchemes), Symbols(Builder) {
+       }
+
+   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::CollectSymbolOptions& Opts;
+  ArrayRef<std::string> URISchemes;
+
+  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 Opts,
+                                 std::vector<std::string> URISchemes)
+    : Opts(std::move(Opts)) {
+  if (!URISchemes.empty())
+    this->URISchemes = std::move(URISchemes);
+  if (Opts.SymbolOpts) {
+    CollectSym = llvm::make_unique<CollectSymbol>(*Opts.SymbolOpts,
+                                                  this->URISchemes, &Symbols);
+  }
+  if (Opts.OccurrenceOpts) {
+    CollectOccu = llvm::make_unique<CollectOccurrence>(
+        *Opts.OccurrenceOpts, this->URISchemes, &Symbols);
+  }
+}
+
+SymbolCollector::~SymbolCollector() {}
+
+class SymbolCollector::CollectOccurrence {
+public:
+  CollectOccurrence(
+      const SymbolCollector::Options::CollectOccurrenceOptions &Opts,
+      ArrayRef<std::string> URISchemes, SymbolSlab::Builder *Builder)
+      : Opts(Opts), URISchemes(URISchemes), Builder(Builder) {}
+
+  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;
+
+    if (!ASTCtx->getSourceManager().isInMainFile(Loc))
+      return;
+    std::string FileURI;
+    auto AddOccurrence = [&](SourceLocation L, const SymbolID &ID) {
+      if (auto Location =
+              getTokenLocation(Loc, ASTCtx->getSourceManager(), URISchemes, "",
+                               ASTCtx->getLangOpts(), FileURI)) {
+        log("add occurrences: {0}", *Location);
+        SymbolOccurrence Occurrence;
+        Occurrence.Location = *Location;
+        Occurrence.Kind = ToOccurrenceKind(Roles);
+        Builder->insert(ID, Occurrence);
+      }
+    };
+    if (static_cast<unsigned>(Opts.Filter) & Roles) {
+      if (auto ID = getSymbolID(D)) {
+        if (!Opts.IDs || llvm::is_contained(*Opts.IDs, *ID)) {
+          AddOccurrence(Loc, *ID);
+        }
+      }
+    }
+  }
+
+private:
+  const SymbolCollector::Options::CollectOccurrenceOptions& Opts;
+  ArrayRef<std::string> URISchemes;
+
+  SymbolSlab::Builder* Builder;
+  ASTContext *ASTCtx;
+};
+
 
 void SymbolCollector::initialize(ASTContext &Ctx) {
-  ASTCtx = &Ctx;
-  CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
-  CompletionTUInfo =
-      llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
+  if (CollectSym)
+    CollectSym->initialize(Ctx);
+  if (CollectOccu)
+    CollectOccu->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 +422,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,15 +435,15 @@
   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.
@@ -316,16 +456,16 @@
   // 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,24 +477,23 @@
 
   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) {
+void SymbolCollector::CollectSymbol::collectMacro(const IdentifierInfo *Name,
+                                                  const MacroInfo *MI,
+                                                  index::SymbolRoleSet Roles,
+                                                  SourceLocation Loc) {
   if (!Opts.CollectMacro)
-    return true;
+    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
@@ -367,26 +506,28 @@
   // 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);
   S.Name = Name->getName();
   S.IsIndexedForCodeCompletion = true;
   S.SymInfo = index::getSymbolInfoForMacro(*MI);
   std::string FileURI;
-  if (auto DeclLoc = getTokenLocation(MI->getDefinitionLoc(), SM, Opts,
-                                      PP->getLangOpts(), FileURI))
+
+  if (auto DeclLoc =
+          getTokenLocation(MI->getDefinitionLoc(), SM, URISchemes,
+                           Opts.FallbackDir, PP->getLangOpts(), FileURI))
     S.CanonicalDeclaration = *DeclLoc;
 
   CodeCompletionResult SymbolCompletion(Name);
@@ -398,27 +539,31 @@
 
   std::string Include;
   if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) {
-    if (auto Header =
-            getIncludeHeader(Name->getName(), SM,
-                             SM.getExpansionLoc(MI->getDefinitionLoc()), Opts))
+    if (auto Header = getIncludeHeader(
+            Name->getName(), SM, SM.getExpansionLoc(MI->getDefinitionLoc()),
+            URISchemes, Opts))
       Include = std::move(*Header);
   }
   S.Signature = Signature;
   S.CompletionSnippetSuffix = SnippetSuffix;
   Symbol::Details Detail;
   Detail.IncludeHeader = Include;
   S.Detail = &Detail;
-  Symbols.insert(S);
-  return true;
+  Symbols->insert(S);
 }
 
 void SymbolCollector::finish() {
+  if (CollectSym)
+    CollectSym->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) {
@@ -440,7 +585,7 @@
   ReferencedMacros.clear();
 }
 
-const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND,
+const Symbol *SymbolCollector::CollectSymbol::addDeclaration(const NamedDecl &ND,
                                               SymbolID ID) {
   auto &Ctx = ND.getASTContext();
   auto &SM = Ctx.getSourceManager();
@@ -455,8 +600,9 @@
   S.IsIndexedForCodeCompletion = isIndexedForCodeCompletion(ND, Ctx);
   S.SymInfo = index::getSymbolInfo(&ND);
   std::string FileURI;
-  if (auto DeclLoc = getTokenLocation(findNameLoc(&ND), SM, Opts,
-                                      ASTCtx->getLangOpts(), FileURI))
+  if (auto DeclLoc =
+          getTokenLocation(findNameLoc(&ND), SM, URISchemes, Opts.FallbackDir,
+                           ASTCtx->getLangOpts(), FileURI))
     S.CanonicalDeclaration = *DeclLoc;
 
   // Add completion info.
@@ -481,7 +627,7 @@
     // Use the expansion location to get the #include header since this is
     // where the symbol is exposed.
     if (auto Header = getIncludeHeader(
-            QName, SM, SM.getExpansionLoc(ND.getLocation()), Opts))
+            QName, SM, SM.getExpansionLoc(ND.getLocation()), URISchemes, Opts))
       Include = std::move(*Header);
   }
   S.Signature = Signature;
@@ -493,24 +639,45 @@
   S.Detail = &Detail;
 
   S.Origin = Opts.Origin;
-  Symbols.insert(S);
-  return Symbols.find(S.ID);
+  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.
   // This is not ideal, but avoids duplicating the "is this a definition" check
   // in clang::index. We should only see one definition.
   Symbol S = DeclSym;
   std::string FileURI;
-  if (auto DefLoc = getTokenLocation(findNameLoc(&ND),
-                                     ND.getASTContext().getSourceManager(),
-                                     Opts, ASTCtx->getLangOpts(), FileURI))
+  if (auto DefLoc = getTokenLocation(
+          findNameLoc(&ND), ND.getASTContext().getSourceManager(), URISchemes,
+          Opts.FallbackDir, 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 (CollectOccu)
+    CollectOccu->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;
 }
 
 } // namespace clangd
Index: clangd/index/Merge.cpp
===================================================================
--- clangd/index/Merge.cpp
+++ clangd/index/Merge.cpp
@@ -81,7 +81,15 @@
   void findOccurrences(const OccurrencesRequest &Req,
                        llvm::function_ref<void(const SymbolOccurrence &)>
                            Callback) const override {
-    log("findOccurrences is not implemented.");
+    llvm::DenseSet<llvm::StringRef> SeenURIs;
+    Dynamic->findOccurrences(Req, [&](const SymbolOccurrence &O) {
+      SeenURIs.insert(O.Location.FileURI);
+      Callback(O);
+    });
+    Static->findOccurrences(Req, [&](const SymbolOccurrence &O) {
+      if (!llvm::is_contained(SeenURIs, O.Location.FileURI))
+        Callback(O);
+    });
   }
 
 private:
Index: clangd/index/MemIndex.cpp
===================================================================
--- clangd/index/MemIndex.cpp
+++ clangd/index/MemIndex.cpp
@@ -90,7 +90,8 @@
 void MemIndex::findOccurrences(
     const OccurrencesRequest &Req,
     llvm::function_ref<void(const SymbolOccurrence &)> Callback) const {
-  log("findOccurrences is not implemented.");
+  assert(
+      "find Occurrences is not support in the index, and should not be called");
 }
 
 } // namespace clangd
Index: clangd/index/Index.h
===================================================================
--- clangd/index/Index.h
+++ clangd/index/Index.h
@@ -17,6 +17,7 @@
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/StringSaver.h"
 #include <array>
 #include <string>
 
@@ -30,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.
@@ -43,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).
@@ -233,6 +245,49 @@
 //        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));
+}
+raw_ostream &operator<<(raw_ostream &OS, SymbolOccurrenceKind K);
+
+// 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 {
@@ -250,7 +305,24 @@
   // 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;
+  }
+
+  std::vector<SymbolOccurrence> takeAllOccurences() {
+    std::vector<SymbolOccurrence> R;
+    for (auto &It : SymbolOccurrences) {
+      R.insert(R.end(), make_move_iterator(It.second.begin()),
+               make_move_iterator(It.second.end()));
+    }
+    return R;
   }
 
   // SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
@@ -267,6 +339,8 @@
       return I == SymbolIndex.end() ? nullptr : &Symbols[I->second];
     }
 
+    void insert(const SymbolID& ID, SymbolOccurrence Occurrence);
+
     // Consumes the builder to finalize the slab.
     SymbolSlab build() &&;
 
@@ -277,48 +351,55 @@
     std::vector<Symbol> Symbols;
     // Values are indices into Symbols vector.
     llvm::DenseMap<SymbolID, size_t> SymbolIndex;
+    // FIXME: we can do more optimization to reduce memory usage, group by
+    // FileName, instead of storing a StringRef for each occurrence object.
+    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(SymbolOccurrences) {}
 
   llvm::BumpPtrAllocator Arena; // Owns Symbol data that the Symbols do not.
   std::vector<Symbol> Symbols;  // Sorted by SymbolID to allow lookup.
+  llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>> SymbolOccurrences;
 };
 
-// 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;
+// 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) {}
+
+   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 {
Index: clangd/index/Index.cpp
===================================================================
--- clangd/index/Index.cpp
+++ clangd/index/Index.cpp
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Index.h"
+#include "../Logger.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/SHA1.h"
 #include "llvm/Support/raw_ostream.h"
@@ -76,6 +77,19 @@
   return Symbols.end();
 }
 
+static void own(SymbolOccurrence &O, DenseSet<StringRef>& Strings,
+                BumpPtrAllocator &Arena) {
+  // Intern replaces V with a reference to the same string owned by the arena.
+  auto Intern = [&](StringRef &V) {
+    auto R = Strings.insert(V);
+    if (R.second) { // New entry added to the table, copy the string.
+      *R.first = V.copy(Arena);
+    }
+    V = *R.first;
+  };
+  Intern(O.Location.FileURI);
+}
+
 // Copy the underlying data of the symbol into the owned arena.
 static void own(Symbol &S, DenseSet<StringRef> &Strings,
                 BumpPtrAllocator &Arena) {
@@ -121,6 +135,11 @@
   }
 }
 
+void SymbolSlab::Builder::insert(const SymbolID &ID, SymbolOccurrence Occurrence) {
+  own(Occurrence, Strings, Arena);
+  SymbolOccurrences[ID].push_back(std::move(Occurrence));
+}
+
 SymbolSlab SymbolSlab::Builder::build() && {
   Symbols = {Symbols.begin(), Symbols.end()}; // Force shrink-to-fit.
   // Sort symbols so the slab can binary search over them.
@@ -131,7 +150,71 @@
   DenseSet<StringRef> Strings;
   for (auto &S : Symbols)
     own(S, Strings, NewArena);
-  return SymbolSlab(std::move(NewArena), std::move(Symbols));
+
+  int count = 0;
+  // We may have duplicated symbol occurrences. Deduplicate them.
+  for (auto &IDAndOccurrence : SymbolOccurrences) {
+    auto &Occurrence = IDAndOccurrence.getSecond();
+    std::sort(Occurrence.begin(), Occurrence.end(),
+              [](const SymbolOccurrence &L, const SymbolOccurrence &R) {
+                return L < R;
+              });
+    Occurrence.erase(
+        std::unique(Occurrence.begin(), Occurrence.end(),
+                    [](const SymbolOccurrence &L, const SymbolOccurrence &R) {
+                      return L == R;
+                    }),
+        Occurrence.end());
+
+    for (auto &O : Occurrence)
+      own(O, Strings, NewArena);
+    count += Occurrence.size();
+  }
+  log("ReferencesInfo: {0} different symbols, {1} total occurrences.",
+      SymbolOccurrences.size(), count);
+  log("SymbolInfo: {0} differenct symbols.", Symbols.size());
+
+  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;
+}
+
+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() {
+  // We may have duplicated symbol occurrences. Deduplicate them.
+  for (auto &IDAndOccurrence : Occurrences) {
+    auto &Occurrence = IDAndOccurrence.getSecond();
+    std::sort(Occurrence.begin(), Occurrence.end(),
+              [](const SymbolOccurrence &L, const SymbolOccurrence &R) {
+                return L < R;
+              });
+    Occurrence.erase(
+        std::unique(Occurrence.begin(), Occurrence.end(),
+                    [](const SymbolOccurrence &L, const SymbolOccurrence &R) {
+                      return L == R;
+                    }),
+        Occurrence.end());
+  }
+  Frozen = true;
 }
 
 } // namespace clangd
Index: clangd/index/FileIndex.h
===================================================================
--- clangd/index/FileIndex.h
+++ clangd/index/FileIndex.h
@@ -46,6 +46,8 @@
   // The shared_ptr keeps the symbols alive
   std::shared_ptr<std::vector<const Symbol *>> allSymbols();
 
+  std::vector<std::shared_ptr<SymbolSlab>> allSlabs() const;
+
 private:
   mutable std::mutex Mutex;
 
@@ -64,7 +66,11 @@
   /// nullptr, this removes all symbols in the file.
   /// If \p AST is not null, \p PP cannot be null and it should be the
   /// preprocessor that was used to build \p AST.
-  void update(PathRef Path, ASTContext *AST, std::shared_ptr<Preprocessor> PP);
+  /// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all
+  /// top level decls obtained from \p AST are indexed.
+  void
+  update(PathRef Path, ASTContext *AST, std::shared_ptr<Preprocessor> PP,
+         llvm::Optional<llvm::ArrayRef<Decl *>> TopLevelDecls = llvm::None);
 
   bool
   fuzzyFind(const FuzzyFindRequest &Req,
@@ -86,8 +92,12 @@
 /// Retrieves namespace and class level symbols in \p AST.
 /// Exposed to assist in unit tests.
 /// If URISchemes is empty, the default schemes in SymbolCollector will be used.
-SymbolSlab indexAST(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
-                    llvm::ArrayRef<std::string> URISchemes = {});
+/// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all top
+/// level decls obtained from \p AST are indexed.
+SymbolSlab
+indexAST(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
+         llvm::Optional<llvm::ArrayRef<Decl *>> TopLevelDecls = llvm::None,
+         llvm::ArrayRef<std::string> URISchemes = {});
 
 } // namespace clangd
 } // namespace clang
Index: clangd/index/FileIndex.cpp
===================================================================
--- clangd/index/FileIndex.cpp
+++ clangd/index/FileIndex.cpp
@@ -8,42 +8,69 @@
 //===----------------------------------------------------------------------===//
 
 #include "FileIndex.h"
-#include "SymbolCollector.h"
 #include "../Logger.h"
+#include "SymbolCollector.h"
 #include "clang/Index/IndexingAction.h"
 #include "clang/Lex/Preprocessor.h"
 
 namespace clang {
 namespace clangd {
 
 SymbolSlab indexAST(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
+                    llvm::Optional<llvm::ArrayRef<Decl *>> TopLevelDecls,
                     llvm::ArrayRef<std::string> URISchemes) {
-  SymbolCollector::Options CollectorOpts;
+  log("index ast for {0}", TopLevelDecls ? "Main AST" : "preamble AST");
+  if (TopLevelDecls) { // Index main AST.
+    // Collect references.
+    SymbolCollector::Options::CollectOccurrenceOptions Opts;
+    Opts.Filter = SymbolOccurrenceKind::Declaration |
+                  SymbolOccurrenceKind::Definition |
+                  SymbolOccurrenceKind::Reference;
+    SymbolCollector Collector({nullptr, &Opts}, URISchemes);
+
+    Collector.setPreprocessor(PP);
+    index::IndexingOptions IndexOpts;
+    // We only need declarations, because we don't count references.
+    IndexOpts.SystemSymbolFilter =
+        index::IndexingOptions::SystemSymbolFilterKind::None;
+    IndexOpts.IndexFunctionLocals = false;
+
+    index::indexTopLevelDecls(AST, *TopLevelDecls, Collector, IndexOpts);
+    auto S = Collector.takeSymbols();
+    log("references slab size: {0} bytes", S.bytes());
+    return S;
+  }
+
+  SymbolCollector::Options::CollectSymbolOptions CollectorOpts;
   // 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;
-  if (!URISchemes.empty())
-    CollectorOpts.URISchemes = URISchemes;
   CollectorOpts.Origin = SymbolOrigin::Dynamic;
 
-  SymbolCollector Collector(std::move(CollectorOpts));
+  SymbolCollector::Options Opts = { &CollectorOpts, nullptr};
+  SymbolCollector Collector(std::move(Opts), URISchemes);
   Collector.setPreprocessor(PP);
   index::IndexingOptions IndexOpts;
   // We only need declarations, because we don't count references.
   IndexOpts.SystemSymbolFilter =
       index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly;
   IndexOpts.IndexFunctionLocals = false;
 
-  std::vector<const Decl *> TopLevelDecls(
-      AST.getTranslationUnitDecl()->decls().begin(),
-      AST.getTranslationUnitDecl()->decls().end());
-  index::indexTopLevelDecls(AST, TopLevelDecls, Collector, IndexOpts);
+  std::vector<Decl *> Storage;
+  if (!TopLevelDecls) {
+    Storage.assign(AST.getTranslationUnitDecl()->decls().begin(),
+                   AST.getTranslationUnitDecl()->decls().end());
+    TopLevelDecls = Storage;
+  }
+  index::indexTopLevelDecls(AST, *TopLevelDecls, Collector, IndexOpts);
 
-  return Collector.takeSymbols();
+  auto S = Collector.takeSymbols();
+  log("symbol slab size: {0} bytes", S.bytes());
+  return S;
 }
 
 FileIndex::FileIndex(std::vector<std::string> URISchemes)
@@ -80,14 +107,25 @@
   return {std::move(Snap), Pointers};
 }
 
+std::vector<std::shared_ptr<SymbolSlab>> FileSymbols::allSlabs() const {
+  std::lock_guard<std::mutex> Lock(Mutex);
+
+  std::vector<std::shared_ptr<SymbolSlab>> Slabs;
+  for (const auto &FileAndSlab : FileToSlabs) {
+    Slabs.push_back(FileAndSlab.second);
+  }
+  return Slabs;
+}
+
 void FileIndex::update(PathRef Path, ASTContext *AST,
-                       std::shared_ptr<Preprocessor> PP) {
+                       std::shared_ptr<Preprocessor> PP,
+                       llvm::Optional<llvm::ArrayRef<Decl *>> TopLevelDecls) {
   if (!AST) {
     FSymbols.update(Path, nullptr);
   } else {
     assert(PP);
     auto Slab = llvm::make_unique<SymbolSlab>();
-    *Slab = indexAST(*AST, PP, URISchemes);
+    *Slab = indexAST(*AST, PP, TopLevelDecls, URISchemes);
     FSymbols.update(Path, std::move(Slab));
   }
   auto Symbols = FSymbols.allSymbols();
@@ -109,7 +147,19 @@
 void FileIndex::findOccurrences(
     const OccurrencesRequest &Req,
     llvm::function_ref<void(const SymbolOccurrence &)> Callback) const {
-  log("findOccurrences is not implemented.");
+  auto Snapshot = FSymbols.allSlabs();
+
+  log("fileIndex: findOccurrences: {0}", Snapshot.size());
+  for (const auto &Slab : Snapshot) {
+    for (const auto &ID : Req.IDs) {
+      for (const auto &Occurrence : Slab->findOccurrences(ID)) {
+        log("fileIndx: find occ: ", Occurrence);
+        if (static_cast<int>(Req.Filter & Occurrence.Kind)) {
+          Callback(Occurrence);
+        }
+      }
+    }
+  }
 }
 
 } // namespace clangd
Index: clangd/XRefs.h
===================================================================
--- clangd/XRefs.h
+++ clangd/XRefs.h
@@ -34,6 +34,11 @@
 /// Get the hover information when hovering at \p Pos.
 llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos);
 
+/// Get references of symbol at a \p Pos.
+std::vector<Location> references(ParsedAST &AST, Position Pos,
+                                 bool includeDeclaration,
+                                 const SymbolIndex *Index = nullptr);
+
 } // namespace clangd
 } // namespace clang
 
Index: clangd/XRefs.cpp
===================================================================
--- clangd/XRefs.cpp
+++ clangd/XRefs.cpp
@@ -11,12 +11,14 @@
 #include "Logger.h"
 #include "SourceCode.h"
 #include "URI.h"
+#include "index/SymbolCollector.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/IndexingAction.h"
 #include "clang/Index/USRGeneration.h"
 #include "llvm/Support/Path.h"
+
 namespace clang {
 namespace clangd {
 using namespace llvm;
@@ -660,5 +662,70 @@
   return None;
 }
 
+std::vector<Location> references(ParsedAST &AST, Position Pos,
+                                 bool IncludeDeclaration,
+                                 const SymbolIndex *Index) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  SourceLocation SourceLocationBeg =
+      getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
+  // Identified symbols at a specific position.
+  auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
+  // FIXME: support macros.
+  llvm::DenseSet<SymbolID> IDs;
+  llvm::DenseSet<SymbolID> NonLocalIDs;
+  for (const auto *D : Symbols.Decls) {
+    if (auto ID = getSymbolID(D)) {
+      llvm::errs() << "ID: " << *ID << ": "
+                   << (index::isFunctionLocalSymbol(D) ? "Local\n"
+                                                       : "Non-local\n");
+      if (!clang::index::isFunctionLocalSymbol(D))
+        NonLocalIDs.insert(*ID);
+      IDs.insert(*ID);
+    }
+  }
+  SymbolOccurrenceKind Filter =
+      SymbolOccurrenceKind::Reference | SymbolOccurrenceKind::Definition;
+  if (IncludeDeclaration)
+    Filter |= SymbolOccurrenceKind::Declaration;
+  SymbolCollector::Options::CollectOccurrenceOptions Opts;
+  Opts.Filter = Filter;
+  Opts.IDs = std::move(IDs);
+
+  SymbolCollector Collector({nullptr, &Opts}, {});
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+  // Only find references for the current main file.
+  indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(),
+                     Collector, IndexOpts);
+
+  std::vector<Location> Results;
+  llvm::DenseSet<StringRef> SeenURIs;
+  auto Storage = Collector.takeSymbols();
+  for (auto &O : Storage.takeAllOccurences()) {
+    if (auto LSPLoc = toLSPLocation(O.Location, "")) {
+      Results.push_back(*LSPLoc);
+      SeenURIs.insert(LSPLoc->uri.uri());
+    }
+  }
+
+  // Query index for non-local symbols.
+  if (Index && !NonLocalIDs.empty()) {
+    log("query index for references");
+    OccurrencesRequest R;
+    R.IDs = std::move(NonLocalIDs);
+    R.Filter = Filter;
+    Index->findOccurrences(R, [&](const SymbolOccurrence &O) {
+      log("index: find occ: {0}", O.Location);
+      if (auto LSPLoc = toLSPLocation(O.Location, "")) {
+        if (!llvm::is_contained(SeenURIs, O.Location.FileURI))
+          Results.push_back(*LSPLoc);
+      }
+    });
+  }
+  return Results;
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/TUScheduler.h
===================================================================
--- clangd/TUScheduler.h
+++ clangd/TUScheduler.h
@@ -51,6 +51,21 @@
   unsigned MaxRetainedASTs = 3;
 };
 
+class ParsingCallbacks {
+public:
+  virtual ~ParsingCallbacks() = default;
+
+  /// Called on the AST that was built for emitting the preamble. The built AST
+  /// contains only AST nodes from the #include directives at the start of the
+  /// file. AST node in the current file should be observed on onMainAST call.
+  virtual void onPreambleAST(PathRef Path, ASTContext &Ctx,
+                          std::shared_ptr<clang::Preprocessor> PP) = 0;
+  /// Called on the AST built for the file itself. Note that preamble AST nodes
+  /// are not deserialized and should be processed in the onPreambleAST call
+  /// instead.
+  virtual void onMainAST(PathRef Path, ParsedAST &AST) = 0;
+};
+
 /// Handles running tasks for ClangdServer and managing the resources (e.g.,
 /// preambles and ASTs) for opened files.
 /// TUScheduler is not thread-safe, only one thread should be providing updates
@@ -61,7 +76,7 @@
 class TUScheduler {
 public:
   TUScheduler(unsigned AsyncThreadsCount, bool StorePreamblesInMemory,
-              PreambleParsedCallback PreambleCallback,
+              ParsingCallbacks& ASTCallbacks,
               std::chrono::steady_clock::duration UpdateDebounce,
               ASTRetentionPolicy RetentionPolicy);
   ~TUScheduler();
@@ -132,7 +147,7 @@
 private:
   const bool StorePreamblesInMemory;
   const std::shared_ptr<PCHContainerOperations> PCHOps;
-  const PreambleParsedCallback PreambleCallback;
+  ParsingCallbacks& Callbacks;
   Semaphore Barrier;
   llvm::StringMap<std::unique_ptr<FileData>> Files;
   std::unique_ptr<ASTCache> IdleASTs;
Index: clangd/TUScheduler.cpp
===================================================================
--- clangd/TUScheduler.cpp
+++ clangd/TUScheduler.cpp
@@ -158,8 +158,7 @@
             Semaphore &Barrier, bool RunSync,
             steady_clock::duration UpdateDebounce,
             std::shared_ptr<PCHContainerOperations> PCHs,
-            bool StorePreamblesInMemory,
-            PreambleParsedCallback PreambleCallback);
+            bool StorePreamblesInMemory, ParsingCallbacks &Callbacks);
 
 public:
   /// Create a new ASTWorker and return a handle to it.
@@ -173,7 +172,7 @@
                                 steady_clock::duration UpdateDebounce,
                                 std::shared_ptr<PCHContainerOperations> PCHs,
                                 bool StorePreamblesInMemory,
-                                PreambleParsedCallback PreambleCallback);
+                                ParsingCallbacks& Callbacks);
   ~ASTWorker();
 
   void update(ParseInputs Inputs, WantDiagnostics,
@@ -228,8 +227,8 @@
   const Path FileName;
   /// Whether to keep the built preambles in memory or on disk.
   const bool StorePreambleInMemory;
-  /// Callback, passed to the preamble builder.
-  const PreambleParsedCallback PreambleCallback;
+  /// Callback, invoked when preamble or main file AST is built.
+  ParsingCallbacks& Callbacks;
   /// Helper class required to build the ASTs.
   const std::shared_ptr<PCHContainerOperations> PCHs;
 
@@ -299,10 +298,10 @@
                                   steady_clock::duration UpdateDebounce,
                                   std::shared_ptr<PCHContainerOperations> PCHs,
                                   bool StorePreamblesInMemory,
-                                  PreambleParsedCallback PreambleCallback) {
+                                  ParsingCallbacks& Callbacks) {
   std::shared_ptr<ASTWorker> Worker(new ASTWorker(
       FileName, IdleASTs, Barrier, /*RunSync=*/!Tasks, UpdateDebounce,
-      std::move(PCHs), StorePreamblesInMemory, std::move(PreambleCallback)));
+      std::move(PCHs), StorePreamblesInMemory, Callbacks));
   if (Tasks)
     Tasks->runAsync("worker:" + llvm::sys::path::filename(FileName),
                     [Worker]() { Worker->run(); });
@@ -315,10 +314,10 @@
                      steady_clock::duration UpdateDebounce,
                      std::shared_ptr<PCHContainerOperations> PCHs,
                      bool StorePreamblesInMemory,
-                     PreambleParsedCallback PreambleCallback)
+                     ParsingCallbacks& Callbacks)
     : IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce),
       FileName(FileName), StorePreambleInMemory(StorePreamblesInMemory),
-      PreambleCallback(std::move(PreambleCallback)), PCHs(std::move(PCHs)),
+      Callbacks(Callbacks), PCHs(std::move(PCHs)),
       Barrier(Barrier), Done(false) {}
 
 ASTWorker::~ASTWorker() {
@@ -365,7 +364,11 @@
         getPossiblyStalePreamble();
     std::shared_ptr<const PreambleData> NewPreamble =
         buildPreamble(FileName, *Invocation, OldPreamble, OldCommand, Inputs,
-                      PCHs, StorePreambleInMemory, PreambleCallback);
+                      PCHs, StorePreambleInMemory,
+                      [this](PathRef Path, ASTContext &Ctx,
+                             std::shared_ptr<clang::Preprocessor> PP) {
+                        Callbacks.onPreambleAST(FileName, Ctx, std::move(PP));
+                      });
 
     bool CanReuseAST = InputsAreTheSame && (OldPreamble == NewPreamble);
     {
@@ -415,6 +418,7 @@
     // Note *AST can be still be null if buildAST fails.
     if (*AST) {
       OnUpdated((*AST)->getDiagnostics());
+      Callbacks.onMainAST(FileName, **AST);
       DiagsWereReported = true;
     }
     // Stash the AST in the cache for further use.
@@ -627,12 +631,12 @@
 
 TUScheduler::TUScheduler(unsigned AsyncThreadsCount,
                          bool StorePreamblesInMemory,
-                         PreambleParsedCallback PreambleCallback,
+                         ParsingCallbacks &Callbacks,
                          std::chrono::steady_clock::duration UpdateDebounce,
                          ASTRetentionPolicy RetentionPolicy)
     : StorePreamblesInMemory(StorePreamblesInMemory),
-      PCHOps(std::make_shared<PCHContainerOperations>()),
-      PreambleCallback(std::move(PreambleCallback)), Barrier(AsyncThreadsCount),
+      PCHOps(std::make_shared<PCHContainerOperations>()), Callbacks(Callbacks),
+      Barrier(AsyncThreadsCount),
       IdleASTs(llvm::make_unique<ASTCache>(RetentionPolicy.MaxRetainedASTs)),
       UpdateDebounce(UpdateDebounce) {
   if (0 < AsyncThreadsCount) {
@@ -670,8 +674,7 @@
     // Create a new worker to process the AST-related tasks.
     ASTWorkerHandle Worker = ASTWorker::create(
         File, *IdleASTs, WorkerThreads ? WorkerThreads.getPointer() : nullptr,
-        Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory,
-        PreambleCallback);
+        Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory, Callbacks);
     FD = std::unique_ptr<FileData>(new FileData{
         Inputs.Contents, Inputs.CompileCommand, std::move(Worker)});
   } else {
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -55,6 +55,7 @@
   virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0;
   virtual void onHover(TextDocumentPositionParams &Params) = 0;
   virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0;
+  virtual void onReference(ReferenceParams &Params) = 0;
 };
 
 void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -75,4 +75,5 @@
   Register("workspace/didChangeConfiguration",
            &ProtocolCallbacks::onChangeConfiguration);
   Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol);
+  Register("textDocument/references", &ProtocolCallbacks::onReference);
 }
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -867,6 +867,17 @@
 llvm::json::Value toJSON(const DocumentHighlight &DH);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DocumentHighlight &);
 
+struct ReferenceContext {
+  // Include the declaration of the current symbol.
+  bool includeDeclaration;
+};
+bool fromJSON(const llvm::json::Value &, ReferenceContext &);
+
+struct ReferenceParams : public TextDocumentPositionParams {
+  ReferenceContext context;
+};
+bool fromJSON(const llvm::json::Value &, ReferenceParams &);
+
 } // namespace clangd
 } // namespace clang
 
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -615,5 +615,16 @@
          O.map("compilationDatabaseChanges", CCPC.compilationDatabaseChanges);
 }
 
+bool fromJSON(const json::Value &Params, ReferenceContext &RC) {
+  json::ObjectMapper O(Params);
+  return O && O.map("includeDeclaration", RC.includeDeclaration);
+}
+
+bool fromJSON(const json::Value &Params, ReferenceParams &R) {
+  json::ObjectMapper O(Params);
+  return O && O.map("context", R.context) &&
+         O.map("textDocument", R.textDocument) && O.map("position", R.position);
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -154,6 +154,10 @@
   void documentSymbols(StringRef File,
                        Callback<std::vector<SymbolInformation>> CB);
 
+  /// Retrieve locations for symbol references.
+  void references(PathRef File, Position Pos, bool includeDeclaration,
+                  Callback<std::vector<Location>> CB);
+
   /// Run formatting for \p Rng inside \p File with content \p Code.
   llvm::Expected<tooling::Replacements> formatRange(StringRef Code,
                                                     PathRef File, Range Rng);
@@ -207,6 +211,27 @@
 
   tooling::CompileCommand getCompileCommand(PathRef File);
 
+  /// Manages dynamic index for open files. Each file might contribute two sets
+  /// of symbols to the dynamic index: symbols from the preamble and symbols
+  /// from the file itself. Those have different lifetimes and we merge results from both 
+  class DynamicIndex : public ParsingCallbacks {
+  public:
+    DynamicIndex(std::vector<std::string> URISchemes);
+
+    SymbolIndex &index() const;
+
+    void onPreambleAST(PathRef Path, ASTContext &Ctx,
+                          std::shared_ptr<clang::Preprocessor> PP) override;
+    void onMainAST(PathRef Path, ParsedAST &AST) override;
+
+  private:
+    FileIndex PreambleIdx;
+    FileIndex MainFileIdx;
+    /// Merged view into both indexes. Merges are performed in a similar manner
+    /// to the merges of dynamic and static index.
+    std::unique_ptr<SymbolIndex> MergedIndex;
+  };
+
   GlobalCompilationDatabase &CDB;
   DiagnosticsConsumer &DiagConsumer;
   FileSystemProvider &FSProvider;
@@ -221,8 +246,8 @@
   //   - the static index passed to the constructor
   //   - a merged view of a static and dynamic index (MergedIndex)
   SymbolIndex *Index;
-  // If present, an up-to-date of symbols in open files. Read via Index.
-  std::unique_ptr<FileIndex> FileIdx;
+  /// If present, an up-to-date of symbols in open files. Read via Index.
+  std::unique_ptr<DynamicIndex> FileIdx;
   // If present, a merged view of FileIdx and an external index. Read via Index.
   std::unique_ptr<SymbolIndex> MergedIndex;
   // If set, this represents the workspace path.
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -66,6 +66,19 @@
   Optional<Expected<tooling::AtomicChanges>> Result;
 };
 
+// FIXME(ibiryukov): this should be a generic helper instead.
+class NoopCallbacks : public ParsingCallbacks {
+public:
+  static ParsingCallbacks &instance() {
+    static ParsingCallbacks *Instance = new NoopCallbacks;
+    return *Instance;
+  }
+
+  void onPreambleAST(PathRef Path, ASTContext &Ctx,
+                     std::shared_ptr<clang::Preprocessor> PP) override {}
+  void onMainAST(PathRef Path, ParsedAST &AST) override {}
+};
+
 } // namespace
 
 ClangdServer::Options ClangdServer::optsForTest() {
@@ -83,27 +96,22 @@
     : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider),
       ResourceDir(Opts.ResourceDir ? Opts.ResourceDir->str()
                                    : getStandardResourceDir()),
-      FileIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.URISchemes)
+      FileIdx(Opts.BuildDynamicSymbolIndex ? new DynamicIndex(Opts.URISchemes)
                                            : nullptr),
       PCHs(std::make_shared<PCHContainerOperations>()),
       // Pass a callback into `WorkScheduler` to extract symbols from a newly
       // parsed file and rebuild the file index synchronously each time an AST
       // is parsed.
       // FIXME(ioeric): this can be slow and we may be able to index on less
       // critical paths.
-      WorkScheduler(
-          Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory,
-          FileIdx
-              ? [this](PathRef Path, ASTContext &AST,
-                       std::shared_ptr<Preprocessor>
-                           PP) { FileIdx->update(Path, &AST, std::move(PP)); }
-              : PreambleParsedCallback(),
-          Opts.UpdateDebounce, Opts.RetentionPolicy) {
+      WorkScheduler(Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory,
+                    FileIdx ? *FileIdx : NoopCallbacks::instance(),
+                    Opts.UpdateDebounce, Opts.RetentionPolicy) {
   if (FileIdx && Opts.StaticIndex) {
-    MergedIndex = mergeIndex(FileIdx.get(), Opts.StaticIndex);
+    MergedIndex = mergeIndex(&FileIdx->index(), Opts.StaticIndex);
     Index = MergedIndex.get();
   } else if (FileIdx)
-    Index = FileIdx.get();
+    Index = &FileIdx->index();
   else if (Opts.StaticIndex)
     Index = Opts.StaticIndex;
   else
@@ -465,6 +473,19 @@
                            Bind(Action, std::move(CB)));
 }
 
+void ClangdServer::references(PathRef File, Position Pos,
+                              bool includeDeclaration,
+                              Callback<std::vector<Location>> CB) {
+  auto Action = [Pos, includeDeclaration, this](
+      Callback<std::vector<Location>> CB, llvm::Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::references(InpAST->AST, Pos, includeDeclaration, Index));
+  };
+
+  WorkScheduler.runWithAST("References", File, Bind(Action, std::move(CB)));
+}
+
 std::vector<std::pair<Path, std::size_t>>
 ClangdServer::getUsedBytesPerFile() const {
   return WorkScheduler.getUsedBytesPerFile();
@@ -474,3 +495,19 @@
 ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) {
   return WorkScheduler.blockUntilIdle(timeoutSeconds(TimeoutSeconds));
 }
+
+ClangdServer::DynamicIndex::DynamicIndex(std::vector<std::string> URISchemes)
+    : PreambleIdx(URISchemes), MainFileIdx(URISchemes),
+      MergedIndex(mergeIndex(&MainFileIdx, &PreambleIdx)) {}
+
+SymbolIndex &ClangdServer::DynamicIndex::index() const { return *MergedIndex; }
+
+void ClangdServer::DynamicIndex::onPreambleAST(
+    PathRef Path, ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP) {
+  PreambleIdx.update(Path, &Ctx, PP, /*TopLevelDecls=*/llvm::None);
+}
+
+void ClangdServer::DynamicIndex::onMainAST(PathRef Path, ParsedAST &AST) {
+  MainFileIdx.update(Path, &AST.getASTContext(), AST.getPreprocessorPtr(),
+                     AST.getLocalTopLevelDecls());
+}
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -75,6 +75,7 @@
   void onRename(RenameParams &Parames) override;
   void onHover(TextDocumentPositionParams &Params) override;
   void onChangeConfiguration(DidChangeConfigurationParams &Params) override;
+  void onReference(ReferenceParams &Params) override;
 
   std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D);
 
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -101,8 +101,7 @@
             {"documentRangeFormattingProvider", true},
             {"documentOnTypeFormattingProvider",
              json::Object{
-                 {"firstTriggerCharacter", "}"},
-                 {"moreTriggerCharacter", {}},
+                 {"firstTriggerCharacter", "}"}, {"moreTriggerCharacter", {}},
              }},
             {"codeActionProvider", true},
             {"completionProvider",
@@ -120,6 +119,7 @@
             {"renameProvider", true},
             {"documentSymbolProvider", true},
             {"workspaceSymbolProvider", true},
+            {"referencesProvider", true},
             {"executeCommandProvider",
              json::Object{
                  {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
@@ -436,6 +436,18 @@
   applyConfiguration(Params.settings);
 }
 
+void ClangdLSPServer::onReference(ReferenceParams &Params) {
+  Server.references(Params.textDocument.uri.file(), Params.position,
+                    Params.context.includeDeclaration,
+                    [](llvm::Expected<std::vector<Location>> Locations) {
+                      if (!Locations)
+                        return replyError(
+                            ErrorCode::InternalError,
+                            llvm::toString(Locations.takeError()));
+                      reply(llvm::json::Array(*Locations));
+                    });
+}
+
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out,
                                  const clangd::CodeCompleteOptions &CCOpts,
                                  llvm::Optional<Path> CompileCommandsDir,
Index: clangd/CMakeLists.txt
===================================================================
--- clangd/CMakeLists.txt
+++ clangd/CMakeLists.txt
@@ -41,6 +41,7 @@
   index/MemIndex.cpp
   index/Merge.cpp
   index/SymbolCollector.cpp
+  index/SymbolOccurrenceCollector.cpp
   index/SymbolYAML.cpp
 
   index/dex/Iterator.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to