[clangd] Document highlights for clangd

Summary: Implementation of Document Highlights Request as described in
LSP.

Contributed by William Enright (nebiroth).

Reviewers: malaperle, krasimir, bkramer, ilya-biryukov

Reviewed By: malaperle

Subscribers: mgrang, sammccall, klimek, ioeric, rwols, cfe-commits, arphaman, ilya-biryukov

Differential Revision: https://reviews.llvm.org/D38425

llvm-svn: 320474
diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp
index 0a8661c..e369ea1 100644
--- a/clang-tools-extra/clangd/ClangdUnit.cpp
+++ b/clang-tools-extra/clangd/ClangdUnit.cpp
@@ -222,26 +222,34 @@
 }
 
 /// Finds declarations locations that a given source location refers to.
-class DeclarationLocationsFinder : public index::IndexDataConsumer {
-  std::vector<Location> DeclarationLocations;
+class DeclarationAndMacrosFinder : public index::IndexDataConsumer {
+  std::vector<const Decl *> Decls;
+  std::vector<const MacroInfo *> MacroInfos;
   const SourceLocation &SearchedLocation;
   const ASTContext &AST;
   Preprocessor &PP;
 
 public:
-  DeclarationLocationsFinder(raw_ostream &OS,
+  DeclarationAndMacrosFinder(raw_ostream &OS,
                              const SourceLocation &SearchedLocation,
                              ASTContext &AST, Preprocessor &PP)
       : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {}
 
-  std::vector<Location> takeLocations() {
-    // Don't keep the same location multiple times.
+  std::vector<const Decl *> takeDecls() {
+    // Don't keep the same declaration multiple times.
     // This can happen when nodes in the AST are visited twice.
-    std::sort(DeclarationLocations.begin(), DeclarationLocations.end());
-    auto last =
-        std::unique(DeclarationLocations.begin(), DeclarationLocations.end());
-    DeclarationLocations.erase(last, DeclarationLocations.end());
-    return std::move(DeclarationLocations);
+    std::sort(Decls.begin(), Decls.end());
+    auto Last = std::unique(Decls.begin(), Decls.end());
+    Decls.erase(Last, Decls.end());
+    return std::move(Decls);
+  }
+
+  std::vector<const MacroInfo *> takeMacroInfos() {
+    // Don't keep the same Macro info multiple times.
+    std::sort(MacroInfos.begin(), MacroInfos.end());
+    auto Last = std::unique(MacroInfos.begin(), MacroInfos.end());
+    MacroInfos.erase(Last, MacroInfos.end());
+    return std::move(MacroInfos);
   }
 
   bool
@@ -249,9 +257,8 @@
                       ArrayRef<index::SymbolRelation> Relations, FileID FID,
                       unsigned Offset,
                       index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
-    if (isSearchedLocation(FID, Offset)) {
-      addDeclarationLocation(D->getSourceRange());
-    }
+    if (isSearchedLocation(FID, Offset))
+      Decls.push_back(D);
     return true;
   }
 
@@ -262,31 +269,6 @@
            SourceMgr.getFileID(SearchedLocation) == FID;
   }
 
-  void addDeclarationLocation(const SourceRange &ValSourceRange) {
-    const SourceManager &SourceMgr = AST.getSourceManager();
-    const LangOptions &LangOpts = AST.getLangOpts();
-    SourceLocation LocStart = ValSourceRange.getBegin();
-    SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(),
-                                                       0, SourceMgr, LangOpts);
-    Position Begin;
-    Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
-    Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
-    Position End;
-    End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
-    End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
-    Range R = {Begin, End};
-    Location L;
-    if (const FileEntry *F =
-            SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart))) {
-      StringRef FilePath = F->tryGetRealPathName();
-      if (FilePath.empty())
-        FilePath = F->getName();
-      L.uri = URI::fromFile(FilePath);
-      L.range = R;
-      DeclarationLocations.push_back(L);
-    }
-  }
-
   void finish() override {
     // Also handle possible macro at the searched location.
     Token Result;
@@ -309,16 +291,111 @@
             PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation);
         MacroInfo *MacroInf = MacroDef.getMacroInfo();
         if (MacroInf) {
-          addDeclarationLocation(SourceRange(MacroInf->getDefinitionLoc(),
-                                             MacroInf->getDefinitionEndLoc()));
+          MacroInfos.push_back(MacroInf);
         }
       }
     }
   }
 };
 
+/// Finds document highlights that a given list of declarations refers to.
+class DocumentHighlightsFinder : public index::IndexDataConsumer {
+  std::vector<const Decl *> &Decls;
+  std::vector<DocumentHighlight> DocumentHighlights;
+  const ASTContext &AST;
+
+public:
+  DocumentHighlightsFinder(raw_ostream &OS, ASTContext &AST, Preprocessor &PP,
+                           std::vector<const Decl *> &Decls)
+      : Decls(Decls), AST(AST) {}
+  std::vector<DocumentHighlight> takeHighlights() {
+    // Don't keep the same highlight multiple times.
+    // This can happen when nodes in the AST are visited twice.
+    std::sort(DocumentHighlights.begin(), DocumentHighlights.end());
+    auto Last =
+        std::unique(DocumentHighlights.begin(), DocumentHighlights.end());
+    DocumentHighlights.erase(Last, DocumentHighlights.end());
+    return std::move(DocumentHighlights);
+  }
+
+  bool
+  handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
+                      ArrayRef<index::SymbolRelation> Relations, FileID FID,
+                      unsigned Offset,
+                      index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
+    const SourceManager &SourceMgr = AST.getSourceManager();
+    if (SourceMgr.getMainFileID() != FID ||
+        std::find(Decls.begin(), Decls.end(), D) == Decls.end()) {
+      return true;
+    }
+    SourceLocation Begin, End;
+    const LangOptions &LangOpts = AST.getLangOpts();
+    SourceLocation StartOfFileLoc = SourceMgr.getLocForStartOfFile(FID);
+    SourceLocation HightlightStartLoc = StartOfFileLoc.getLocWithOffset(Offset);
+    End =
+        Lexer::getLocForEndOfToken(HightlightStartLoc, 0, SourceMgr, LangOpts);
+    SourceRange SR(HightlightStartLoc, End);
+
+    DocumentHighlightKind Kind = DocumentHighlightKind::Text;
+    if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Write) & Roles)
+      Kind = DocumentHighlightKind::Write;
+    else if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Read) & Roles)
+      Kind = DocumentHighlightKind::Read;
+
+    DocumentHighlights.push_back(getDocumentHighlight(SR, Kind));
+    return true;
+  }
+
+private:
+  DocumentHighlight getDocumentHighlight(SourceRange SR,
+                                         DocumentHighlightKind Kind) {
+    const SourceManager &SourceMgr = AST.getSourceManager();
+    SourceLocation LocStart = SR.getBegin();
+    Position Begin;
+    Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
+    Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+    Position End;
+    End.line = SourceMgr.getSpellingLineNumber(SR.getEnd()) - 1;
+    End.character = SourceMgr.getSpellingColumnNumber(SR.getEnd()) - 1;
+    Range R = {Begin, End};
+    DocumentHighlight DH;
+    DH.range = R;
+    DH.kind = Kind;
+    return DH;
+  }
+};
+
 } // namespace
 
+llvm::Optional<Location>
+getDeclarationLocation(ParsedAST &AST, const SourceRange &ValSourceRange) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
+  SourceLocation LocStart = ValSourceRange.getBegin();
+
+  const FileEntry *F =
+      SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart));
+  if (!F)
+    return llvm::None;
+  SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0,
+                                                     SourceMgr, LangOpts);
+  Position Begin;
+  Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
+  Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+  Position End;
+  End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
+  End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+  Range R = {Begin, End};
+  Location L;
+
+  StringRef FilePath = F->tryGetRealPathName();
+  if (FilePath.empty())
+    FilePath = F->getName();
+  L.uri = URI::fromFile(FilePath);
+  L.range = R;
+  return L;
+}
+
 std::vector<Location> clangd::findDefinitions(ParsedAST &AST, Position Pos,
                                               clangd::Logger &Logger) {
   const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
@@ -328,7 +405,7 @@
 
   SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE);
 
-  auto DeclLocationsFinder = std::make_shared<DeclarationLocationsFinder>(
+  auto DeclMacrosFinder = std::make_shared<DeclarationAndMacrosFinder>(
       llvm::errs(), SourceLocationBeg, AST.getASTContext(),
       AST.getPreprocessor());
   index::IndexingOptions IndexOpts;
@@ -337,9 +414,60 @@
   IndexOpts.IndexFunctionLocals = true;
 
   indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
-                     DeclLocationsFinder, IndexOpts);
+                     DeclMacrosFinder, IndexOpts);
 
-  return DeclLocationsFinder->takeLocations();
+  std::vector<const Decl *> Decls = DeclMacrosFinder->takeDecls();
+  std::vector<const MacroInfo *> MacroInfos =
+      DeclMacrosFinder->takeMacroInfos();
+  std::vector<Location> Result;
+
+  for (auto Item : Decls) {
+    auto L = getDeclarationLocation(AST, Item->getSourceRange());
+    if (L)
+      Result.push_back(*L);
+  }
+
+  for (auto Item : MacroInfos) {
+    SourceRange SR(Item->getDefinitionLoc(), Item->getDefinitionEndLoc());
+    auto L = getDeclarationLocation(AST, SR);
+    if (L)
+      Result.push_back(*L);
+  }
+
+  return Result;
+}
+
+std::vector<DocumentHighlight>
+clangd::findDocumentHighlights(ParsedAST &AST, Position Pos,
+                               clangd::Logger &Logger) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+  if (!FE)
+    return {};
+
+  SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE);
+
+  auto DeclMacrosFinder = std::make_shared<DeclarationAndMacrosFinder>(
+      llvm::errs(), SourceLocationBeg, AST.getASTContext(),
+      AST.getPreprocessor());
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+
+  // Macro occurences are not currently handled.
+  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+                     DeclMacrosFinder, IndexOpts);
+
+  std::vector<const Decl *> SelectedDecls = DeclMacrosFinder->takeDecls();
+
+  auto DocHighlightsFinder = std::make_shared<DocumentHighlightsFinder>(
+      llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls);
+
+  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+                     DocHighlightsFinder, IndexOpts);
+
+  return DocHighlightsFinder->takeHighlights();
 }
 
 void ParsedAST::ensurePreambleDeclsDeserialized() {