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 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> 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 CompileCommandsDir, Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -136,6 +136,11 @@ void findDefinitions(PathRef File, Position Pos, Callback> 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> CB); + /// Helper function that returns a path to the corresponding source file when /// given a header file and vice versa. llvm::Optional 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> CB) { + auto FS = FSProvider.getFileSystem(); + auto Action = [this, Pos, FS, + IncludeDeclaration](Callback> CB, + llvm::Expected 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> CB) { auto Action = [](Callback> CB, 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/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/XRefs.h =================================================================== --- clangd/XRefs.h +++ clangd/XRefs.h @@ -30,6 +30,9 @@ std::vector findDocumentHighlights(ParsedAST &AST, Position Pos); +std::vector findReferences(ParsedAST &AST, Position Pos, + bool IncludeDeclaration); + /// Get the hover information when hovering at \p Pos. llvm::Optional 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 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::SymbolRole::Reference)) { + if (IncludeDeclaration) + InterestingRoleSet |= + static_cast(index::SymbolRole::Declaration) | + static_cast(index::SymbolRole::Definition); + } + + std::vector 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 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 findDefinitions(ParsedAST &AST, Position Pos, @@ -324,6 +376,45 @@ return Result; } +std::vector 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 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(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: 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: 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": [