malaperle created this revision.
Herald added subscribers: cfe-commits, arphaman, mgrang, jkorous, MaskRay, 
ioeric, ilya-biryukov.

We do not have a global index of references but we can find the references
of local symbols within the AST in the mean time. Also, since we will not
record local symbol references in the index, we will need that logic anyway.

Signed-off-by: Marc-Andre Laperle <marc-andre.lape...@ericsson.com>


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D49920

Files:
  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/XRefs.cpp
  clangd/XRefs.h
  test/clangd/initialize-params-invalid.test
  test/clangd/initialize-params.test

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": true,
 # 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": true,
 # CHECK-NEXT:      "renameProvider": true,
 # CHECK-NEXT:      "signatureHelpProvider": {
 # CHECK-NEXT:        "triggerCharacters": [
Index: clangd/XRefs.h
===================================================================
--- clangd/XRefs.h
+++ clangd/XRefs.h
@@ -30,6 +30,9 @@
 std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
                                                       Position Pos);
 
+std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
+                                     bool IncludeDeclaration);
+
 /// Get the hover information when hovering at \p Pos.
 llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos);
 
Index: clangd/XRefs.cpp
===================================================================
--- clangd/XRefs.cpp
+++ clangd/XRefs.cpp
@@ -211,6 +211,58 @@
   return SymbolID(USR);
 }
 
+/// Finds declarations locations that a given source Decl refers to, in the main
+/// file.
+class ReferenceLocationsFinder : public index::IndexDataConsumer {
+  std::vector<Location> ReferenceLocations;
+  ParsedAST &AST;
+  const Decl *ReferencedDecl;
+  index::SymbolRoleSet InterestingRoleSet;
+
+public:
+  ReferenceLocationsFinder(ParsedAST &AST, const Decl *D,
+                           bool IncludeDeclaration)
+      : AST(AST), ReferencedDecl(D),
+        InterestingRoleSet(
+            static_cast<index::SymbolRoleSet>(index::SymbolRole::Reference)) {
+    if (IncludeDeclaration)
+      InterestingRoleSet |=
+          static_cast<index::SymbolRoleSet>(index::SymbolRole::Declaration) |
+          static_cast<index::SymbolRoleSet>(index::SymbolRole::Definition);
+  }
+
+  std::vector<Location> takeLocations() {
+    // Don't keep the same location multiple times.
+    // This can happen when nodes in the AST are visited twice.
+    std::sort(ReferenceLocations.begin(), ReferenceLocations.end());
+    auto last =
+        std::unique(ReferenceLocations.begin(), ReferenceLocations.end());
+    ReferenceLocations.erase(last, ReferenceLocations.end());
+    return std::move(ReferenceLocations);
+  }
+
+  bool
+  handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
+                      ArrayRef<index::SymbolRelation> Relations,
+                      SourceLocation Loc,
+                      index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
+    const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+    if (D != ReferencedDecl || SourceMgr.isWrittenInMainFile(Loc)) {
+      return true;
+    }
+
+    // The end loc is adjusted in makeLocation with getLocForEndOfToken.
+    SourceRange Range(Loc, Loc);
+
+    if (Roles & InterestingRoleSet) {
+      auto L = makeLocation(AST, Range);
+      if (L)
+        ReferenceLocations.push_back(*L);
+    }
+    return true;
+  }
+};
+
 } // namespace
 
 std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
@@ -324,6 +376,45 @@
   return Result;
 }
 
+std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
+                                     bool IncludeDeclaration) {
+  SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  SourceLocation SourceLocationBeg =
+      getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
+
+  DeclarationAndMacrosFinder DeclMacrosFinder(llvm::errs(), SourceLocationBeg,
+                                              AST.getASTContext(),
+                                              AST.getPreprocessor());
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+  indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(),
+                     DeclMacrosFinder, IndexOpts);
+  std::vector<const Decl *> Decls = DeclMacrosFinder.takeDecls();
+  if (Decls.empty())
+    return {};
+
+  // Make CXXConstructorDecl lower priority. For example:
+  // MyClass Obj;
+  // We likely want to find the references to Obj not MyClass()
+  std::sort(Decls.begin(), Decls.end(), [](const Decl *&D1, const Decl *&D2) {
+    return !dyn_cast<CXXConstructorDecl>(D1);
+  });
+
+  const Decl *D = Decls[0];
+
+  if (!index::isFunctionLocalSymbol(D)) {
+    return {};
+  }
+
+  ReferenceLocationsFinder ReferencesFinder(AST, D, IncludeDeclaration);
+  indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(),
+                     ReferencesFinder, IndexOpts);
+
+  return ReferencesFinder.takeLocations();
+}
+
 namespace {
 
 /// Finds document highlights that a given list of declarations refers to.
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 onReferences(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::onReferences);
 }
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -633,6 +633,17 @@
 };
 llvm::json::Value toJSON(const Hover &H);
 
+struct ReferenceContext {
+  /// Include the declaration of the current symbol.
+  bool includeDeclaration;
+};
+bool fromJSON(const llvm::json::Value &Params, ReferenceContext &R);
+
+struct ReferenceParams : public TextDocumentPositionParams {
+  ReferenceContext context;
+};
+bool fromJSON(const llvm::json::Value &Params, ReferenceParams &R);
+
 /// The kind of a completion entry.
 enum class CompletionItemKind {
   Missing = 0,
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -509,6 +509,17 @@
   return std::move(Result);
 }
 
+bool fromJSON(const json::Value &Params, ReferenceContext &R) {
+  json::ObjectMapper O(Params);
+  return O && O.map("includeDeclaration", R.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);
+}
+
 llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const CompletionItem &I) {
   O << I.label << " - " << toJSON(I);
   return O;
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -136,6 +136,11 @@
   void findDefinitions(PathRef File, Position Pos,
                        Callback<std::vector<Location>> CB);
 
+  /// Get references of symbol at a specified \p Line and
+  /// \p Column in \p File.
+  void findReferences(PathRef File, Position Pos, bool IncludeDeclaration,
+                      Callback<std::vector<Location>> CB);
+
   /// Helper function that returns a path to the corresponding source file when
   /// given a header file and vice versa.
   llvm::Optional<Path> switchSourceHeader(PathRef Path);
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -452,6 +452,21 @@
                                  RootPath ? *RootPath : ""));
 }
 
+void ClangdServer::findReferences(PathRef File, Position Pos,
+                                  bool IncludeDeclaration,
+                                  Callback<std::vector<Location>> CB) {
+  auto FS = FSProvider.getFileSystem();
+  auto Action = [this, Pos, FS,
+                 IncludeDeclaration](Callback<std::vector<Location>> CB,
+                                     llvm::Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::findReferences(InpAST->AST, Pos, IncludeDeclaration));
+  };
+
+  WorkScheduler.runWithAST("Definitions", File, Bind(Action, std::move(CB)));
+}
+
 void ClangdServer::documentSymbols(
     StringRef File, Callback<std::vector<SymbolInformation>> CB) {
   auto Action = [](Callback<std::vector<SymbolInformation>> CB,
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 onReferences(ReferenceParams &Params) override;
 
   std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D);
 
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -119,6 +119,7 @@
              json::Object{
                  {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
              }},
+            {"referencesProvider", true},
         }}}});
 }
 
@@ -413,6 +414,18 @@
   }
 }
 
+void ClangdLSPServer::onReferences(ReferenceParams &Params) {
+  Server.findReferences(Params.textDocument.uri.file(), Params.position,
+                        Params.context.includeDeclaration,
+                        [](llvm::Expected<std::vector<Location>> Items) {
+                          if (!Items)
+                            return replyError(
+                                ErrorCode::InvalidParams,
+                                llvm::toString(Items.takeError()));
+                          reply(json::Array(*Items));
+                        });
+}
+
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out,
                                  const clangd::CodeCompleteOptions &CCOpts,
                                  llvm::Optional<Path> CompileCommandsDir,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D49920: [clangd... Marc-Andre Laperle via Phabricator via cfe-commits

Reply via email to