malaperle-ericsson updated this revision to Diff 103405.
malaperle-ericsson marked 12 inline comments as done.
malaperle-ericsson added a comment.

Address comments


https://reviews.llvm.org/D34269

Files:
  clangd/CMakeLists.txt
  clangd/ClangdLSPServer.cpp
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/ClangdUnit.cpp
  clangd/ClangdUnit.h
  clangd/Protocol.cpp
  clangd/Protocol.h
  clangd/ProtocolHandlers.cpp
  clangd/ProtocolHandlers.h
  test/clangd/definitions.test
  test/clangd/formatting.test

Index: test/clangd/formatting.test
===================================================================
--- test/clangd/formatting.test
+++ test/clangd/formatting.test
@@ -4,14 +4,15 @@
 Content-Length: 125
 
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
-# CHECK: Content-Length: 424
+# CHECK: Content-Length: 462
 # CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
 # CHECK:   "textDocumentSync": 1,
 # CHECK:   "documentFormattingProvider": true,
 # CHECK:   "documentRangeFormattingProvider": true,
 # CHECK:   "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
 # CHECK:   "codeActionProvider": true,
-# CHECK:   "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}
+# CHECK:   "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},
+# CHECK:   "definitionProvider": true
 # CHECK: }}}
 #
 Content-Length: 193
Index: test/clangd/definitions.test
===================================================================
--- /dev/null
+++ test/clangd/definitions.test
@@ -0,0 +1,168 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s
+# It is absolutely vital that this file has CRLF line endings.
+#
+Content-Length: 125
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+
+Content-Length: 172
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}}
+# Go to local variable
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}}
+# Go to local variable, end of token
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}
+
+Content-Length: 214
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n  Foo bar = { x : 1 };\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}}
+# Go to field, GNU old-style field designator 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}
+
+Content-Length: 215
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n  Foo baz = { .x = 2 };\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}}
+# Go to field, field designator 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}
+
+Content-Length: 187
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int main() {\n   main();\n   return 0;\n}"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}}
+# Go to function declaration, function call 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]}
+
+Content-Length: 208
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct Foo {\n};\nint main() {\n   Foo bar;\n   return 0;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}}
+# Go to struct declaration, new struct instance 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 1, "character": 1}}}]}
+
+Content-Length: 231
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace n1 {\nstruct Foo {\n};\n}\nint main() {\n   n1::Foo bar;\n   return 0;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}}
+# Go to struct declaration, new struct instance, qualified name 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]}
+
+Content-Length: 215
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct Foo {\n  int x;\n};\nint main() {\n   Foo bar;\n   bar.x;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}
+# Go to field declaration, field reference 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 7}}}]}
+
+Content-Length: 220
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n  void x();\n};\nint main() {\n   Foo bar;\n   bar.x();\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}
+# Go to method declaration, method call 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 10}}}]}
+
+Content-Length: 240
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n};\ntypedef Foo TypedefFoo;\nint main() {\n   TypedefFoo bar;\n   return 0;\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}}
+# Go to typedef 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 0}, "end": {"line": 2, "character": 22}}}]}
+
+Content-Length: 254
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n   return 0;\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}}
+# Go to template type parameter. Fails until clangIndex is modified to handle those.
+# no-CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0, "character": 34}}}]}
+
+Content-Length: 256
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\nstatic void bar() {}\n};\n}\nint main() {\n   ns::Foo::bar();\n   return 0;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}}
+# Go to namespace, static method call 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 4, "character": 1}}}]}
+
+Content-Length: 265
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\n  int field;\n  Foo(int param) : field(param) {}\n};\n}\nint main() {\n   return 0;\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}}
+# Go to field, member initializer 
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 2}, "end": {"line": 2, "character": 11}}}]}
+
+Content-Length: 204
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define MY_MACRO 0\nint main() {\n  return MY_MACRO;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":9}}}
+# Go to macro
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 18}}}]}
+
+Content-Length: 217
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}
+# Go to macro, re-defined later
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 13}}}]}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}}
+# Go to macro, undefined later
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}
+# Go to macro, being undefined
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]}
+
+Content-Length: 44
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -46,6 +46,8 @@
                             JSONOutput &Out) = 0;
   virtual void onCompletion(TextDocumentPositionParams Params, StringRef ID,
                             JSONOutput &Out) = 0;
+  virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
+                            JSONOutput &Out) = 0;
 };
 
 void regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -186,6 +186,24 @@
   ProtocolCallbacks &Callbacks;
 };
 
+struct GotoDefinitionHandler : Handler {
+  GotoDefinitionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    auto TDPP = TextDocumentPositionParams::parse(Params);
+    if (!TDPP) {
+      Output.log("Failed to decode TextDocumentPositionParams!\n");
+      return;
+    }
+
+    Callbacks.onGoToDefinition(*TDPP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
 } // namespace
 
 void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher,
@@ -219,4 +237,6 @@
   Dispatcher.registerHandler(
       "textDocument/completion",
       llvm::make_unique<CompletionHandler>(Out, Callbacks));
+  Dispatcher.registerHandler("textDocument/definition",
+      llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
 }
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -38,6 +38,18 @@
 
   static URI parse(llvm::yaml::ScalarNode *Param);
   static std::string unparse(const URI &U);
+
+  friend bool operator==(const URI &LHS, const URI &RHS) {
+    return LHS.uri == RHS.uri;
+  }
+
+  friend bool operator!=(const URI &LHS, const URI &RHS) {
+    return !(LHS == RHS);
+  }
+
+  friend bool operator<(const URI &LHS, const URI &RHS) {
+    return LHS.uri < RHS.uri;
+  }
 };
 
 struct TextDocumentIdentifier {
@@ -86,6 +98,26 @@
   static std::string unparse(const Range &P);
 };
 
+struct Location {
+  /// The text document's URI.
+  URI uri;
+  Range range;
+
+  friend bool operator==(const Location &LHS, const Location &RHS) {
+    return LHS.uri == RHS.uri && LHS.range == RHS.range;
+  }
+
+  friend bool operator!=(const Location &LHS, const Location &RHS) {
+    return !(LHS == RHS);
+  }
+
+  friend bool operator<(const Location &LHS, const Location &RHS) {
+    return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range);
+  }
+
+  static std::string unparse(const Location &P);
+};
+
 struct TextEdit {
   /// The range of the text document to be manipulated. To insert
   /// text into a document create a range where start === end.
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -54,7 +54,7 @@
 }
 
 std::string URI::unparse(const URI &U) {
-  return U.uri;
+  return "\"" + U.uri + "\"";
 }
 
 llvm::Optional<TextDocumentIdentifier>
@@ -162,6 +162,14 @@
   return Result;
 }
 
+std::string Location::unparse(const Location &P) {
+  std::string Result;
+  llvm::raw_string_ostream(Result) << llvm::format(
+      R"({"uri": %s, "range": %s})", URI::unparse(P.uri).c_str(),
+      Range::unparse(P.range).c_str());
+  return Result;
+}
+
 llvm::Optional<TextDocumentItem>
 TextDocumentItem::parse(llvm::yaml::MappingNode *Params) {
   TextDocumentItem Result;
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -59,6 +59,8 @@
   std::vector<CompletionItem>
   codeComplete(StringRef Contents, Position Pos,
                IntrusiveRefCntPtr<vfs::FileSystem> VFS);
+  /// Get definition of symbol at a specified \p Line and \p Column in \p File.
+  std::unique_ptr<std::vector<Location>> findDefinitions(Position Pos);
   /// Returns diagnostics and corresponding FixIts for each diagnostic that are
   /// located in the current file.
   std::vector<DiagWithFixIts> getLocalDiagnostics() const;
@@ -71,6 +73,8 @@
   Path FileName;
   std::unique_ptr<ASTUnit> Unit;
   std::shared_ptr<PCHContainerOperations> PCHs;
+
+  SourceLocation getBeginningOfIdentifier(const Position& Pos, const FileEntry* FE) const;
 };
 
 } // namespace clangd
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -8,13 +8,21 @@
 //===---------------------------------------------------------------------===//
 
 #include "ClangdUnit.h"
+
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/Utils.h"
+#include "clang/Index/IndexingAction.h"
+#include "clang/Index/IndexDataConsumer.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Preprocessor.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "llvm/Support/Format.h"
 
+#include <algorithm>
+
 using namespace clang::clangd;
 using namespace clang;
 
@@ -261,3 +269,146 @@
 void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
   Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
 }
+
+namespace {
+/// Finds declarations locations that a given source location refers to.
+class DeclarationLocationsFinder : public index::IndexDataConsumer {
+  std::unique_ptr<std::vector<Location>> DeclarationLocations;
+  const SourceLocation &SearchedLocation;
+  ASTUnit &Unit;
+public:
+  DeclarationLocationsFinder(raw_ostream &OS,
+      const SourceLocation &SearchedLocation, ASTUnit &Unit) :
+      SearchedLocation(SearchedLocation), Unit(Unit) {
+    DeclarationLocations.reset(new std::vector<Location>);
+  }
+
+  std::unique_ptr<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(DeclarationLocations->begin(), DeclarationLocations->end());
+    auto last = std::unique(DeclarationLocations->begin(), DeclarationLocations->end());
+    DeclarationLocations->erase(last, DeclarationLocations->end());
+    return std::move(DeclarationLocations);
+  }
+
+  bool handleDeclOccurence(const Decl* D, index::SymbolRoleSet Roles,
+      ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
+      index::IndexDataConsumer::ASTNodeInfo ASTNode) override
+      {
+    if (IsSearchedLocation(FID, Offset)) {
+      AddDeclarationLocation(D->getSourceRange());
+    }
+    return true;
+  }
+
+private:
+  bool IsSearchedLocation(FileID FID, unsigned Offset) const {
+    const SourceManager &SourceMgr = Unit.getSourceManager();
+    return SourceMgr.getFileOffset(SearchedLocation) == Offset
+        && SourceMgr.getFileID(SearchedLocation) == FID;
+  }
+
+  void AddDeclarationLocation(const SourceRange& ValSourceRange) {
+    const SourceManager& SourceMgr = Unit.getSourceManager();
+    const LangOptions& LangOpts = Unit.getLangOpts();
+    SourceLocation LocStart = ValSourceRange.getBegin();
+    SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(),
+        0, SourceMgr, LangOpts);
+    Position P1;
+    P1.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
+    P1.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+    Position P2;
+    P2.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
+    P2.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+    Range R = { P1, P2 };
+    Location L;
+    L.uri = URI::fromFile(
+        SourceMgr.getFilename(SourceMgr.getSpellingLoc(LocStart)));
+    L.range = R;
+    DeclarationLocations->push_back(L);
+  }
+
+  void finish() override
+  {
+    // Also handle possible macro at the searched location.
+    Token Result;
+    if (!Lexer::getRawToken(SearchedLocation, Result, Unit.getSourceManager(),
+        Unit.getASTContext().getLangOpts(), false)) {
+      if (Result.is(tok::raw_identifier)) {
+        Unit.getPreprocessor().LookUpIdentifierInfo(Result);
+      }
+      IdentifierInfo* IdentifierInfo = Result.getIdentifierInfo();
+      if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) {
+        std::pair<FileID, unsigned int> DecLoc =
+            Unit.getSourceManager().getDecomposedExpansionLoc(SearchedLocation);
+        // Get the definition just before the searched location so that a macro
+        // referenced in a '#undef MACRO' can still be found.
+        SourceLocation BeforeSearchedLocation = Unit.getLocation(
+            Unit.getSourceManager().getFileEntryForID(DecLoc.first),
+            DecLoc.second - 1);
+        MacroDefinition MacroDef =
+            Unit.getPreprocessor().getMacroDefinitionAtLoc(IdentifierInfo,
+                BeforeSearchedLocation);
+        MacroInfo* MacroInf = MacroDef.getMacroInfo();
+        if (MacroInf) {
+          AddDeclarationLocation(
+              SourceRange(MacroInf->getDefinitionLoc(),
+                  MacroInf->getDefinitionEndLoc()));
+        }
+      }
+    }
+  }
+};
+} // namespace
+
+std::unique_ptr<std::vector<Location>> ClangdUnit::findDefinitions(Position Pos) {
+  const FileEntry *FE = Unit->getFileManager().getFile(Unit->getMainFileName());
+  if (!FE)
+    return {};
+
+  SourceLocation SourceLocationBeg = getBeginningOfIdentifier(Pos, FE);
+
+  auto DeclLocationsFinder = std::make_shared<DeclarationLocationsFinder>(llvm::errs(),
+      SourceLocationBeg, *Unit);
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+  index::indexASTUnit(*Unit, DeclLocationsFinder, IndexOpts);
+
+  return DeclLocationsFinder->takeLocations();
+}
+
+SourceLocation ClangdUnit::getBeginningOfIdentifier(const Position &Pos,
+    const FileEntry *FE) const {
+  // The language server protocol uses zero-based line and column numbers.
+  // Clang uses one-based numbers.
+  SourceLocation InputLocation = Unit->getLocation(FE, Pos.line + 1,
+      Pos.character + 1);
+
+  if (Pos.character == 0) {
+    return InputLocation;
+  }
+
+  // This handle cases where the position is in the middle of a token or right
+  // after the end of a token. In theory we could just use GetBeginningOfToken
+  // to find the start of the token at the input position, but this doesn't
+  // work when right after the end, i.e. foo|.
+  // So try to go back by one and see if we're still inside the an identifier
+  // token. If so, Take the beginning of this token.
+  // (It should be the same identifier because you can't have two adjacent
+  // identifiers without another token in between.)
+  SourceLocation PeekBeforeLocation = Unit->getLocation(FE, Pos.line + 1,
+      Pos.character);
+  const SourceManager &SourceMgr = Unit->getSourceManager();
+  Token Result;
+  Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr,
+      Unit->getASTContext().getLangOpts(), false);
+  if (Result.is(tok::raw_identifier)) {
+    return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr,
+        Unit->getASTContext().getLangOpts());
+  }
+
+  return InputLocation;
+}
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -174,6 +174,8 @@
   Tagged<std::vector<CompletionItem>>
   codeComplete(PathRef File, Position Pos,
                llvm::Optional<StringRef> OverridenContents = llvm::None);
+  /// Get definition of symbol at a specified \p Line and \p Column in \p File.
+  Tagged<std::unique_ptr<std::vector<Location>>> findDefinitions(PathRef File, Position Pos);
 
   /// Run formatting for \p Rng inside \p File.
   std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -264,3 +264,17 @@
   });
   return DumpFuture.get();
 }
+
+Tagged<std::unique_ptr<std::vector<Location>>>
+ClangdServer::findDefinitions(PathRef File, Position Pos) {
+  auto FileContents = DraftMgr.getDraft(File);
+  assert(FileContents.Draft && "findDefinitions is called for non-added document");
+
+  std::unique_ptr<std::vector<Location>> Result;
+  auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+  Units.runOnUnit(
+      File, *FileContents.Draft, CDB, PCHs, TaggedFS.Value, [&](ClangdUnit &Unit) {
+        Result = Unit.findDefinitions(Pos);
+      });
+  return make_tagged(std::move(Result), TaggedFS.Tag);
+}
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -69,6 +69,8 @@
                     JSONOutput &Out) override;
   void onCompletion(TextDocumentPositionParams Params, StringRef ID,
                     JSONOutput &Out) override;
+  void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
+                            JSONOutput &Out) override;
 
 private:
   ClangdLSPServer &LangServer;
@@ -84,7 +86,8 @@
           "documentRangeFormattingProvider": true,
           "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
           "codeActionProvider": true,
-          "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}
+          "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},
+          "definitionProvider": true
         }}})");
 }
 
@@ -191,6 +194,25 @@
       R"(,"result":[)" + Completions + R"(]})");
 }
 
+void ClangdLSPServer::LSPProtocolCallbacks::onGoToDefinition(
+    TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) {
+
+  auto Items = LangServer.Server.findDefinitions(
+      Params.textDocument.uri.file,
+      Position{Params.position.line, Params.position.character}).Value;
+
+  std::string Locations;
+  for (const auto &Item : *Items) {
+    Locations += Location::unparse(Item);
+    Locations += ",";
+  }
+  if (!Locations.empty())
+    Locations.pop_back();
+  Out.writeMessage(
+      R"({"jsonrpc":"2.0","id":)" + ID.str() +
+      R"(,"result":[)" + Locations + R"(]})");
+}
+
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously)
     : Out(Out), DiagConsumer(*this),
       Server(CDB, DiagConsumer, FSProvider, RunSynchronously) {}
Index: clangd/CMakeLists.txt
===================================================================
--- clangd/CMakeLists.txt
+++ clangd/CMakeLists.txt
@@ -18,6 +18,7 @@
   clangBasic
   clangFormat
   clangFrontend
+  clangIndex
   clangSema
   clangTooling
   clangToolingCore
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to