jkorous created this revision.
jkorous added reviewers: sammccall, arphaman, benlangmuir.
Herald added subscribers: cfe-commits, kadircet, dexonsmith, MaskRay, ioeric, 
ilya-biryukov.

Hi,

I implemented `textDocument/cursorInfo` method based on consensus in 
https://reviews.llvm.org/D54529.
I'd like to ask for early feedback - what's still missing is relevant client 
capability.

Couple things that I'd love to hear opinions about are:

- conditional return in `getCursorInfo` - Should we return for example data 
with empty `USR`?
- `containerName` of local variables - It's currently empty even if semantic 
parent has a name (say it's a function). (Please search for local.cpp in the 
test.) Is that what we want?
- For now I used `getSymbolAtPosition()` as it's simpler and fits the context 
better. However I assume I could use this optimization from 
`tooling::getNamedDeclAt()` (in a separate patch): 
https://github.com/llvm-mirror/clang/blob/master/lib/Tooling/Refactoring/Rename/USRFinder.cpp#L82
- One thing I am wondering about is whether we could use (and whether it's a 
significant improvement) some early return in `indexTopLevelDecls` (using 
`DeclarationAndMacrosFinder`) also for hover and definition use-cases. Is it 
correct to assume that at a given `Location` there can be maximum of one 
declaration and one definition?

P. S. Alex and Ben have thanksgiving break now so they'll probably add any 
feedback next week.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D54799

Files:
  ClangdLSPServer.cpp
  ClangdLSPServer.h
  ClangdServer.cpp
  ClangdServer.h
  Protocol.cpp
  Protocol.h
  XRefs.cpp
  XRefs.h
  clangd/cursor-info.test

Index: clangd/cursor-info.test
===================================================================
--- /dev/null
+++ clangd/cursor-info.test
@@ -0,0 +1,46 @@
+# RUN: clangd -lit-test < %s | FileCheck %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///simple.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///simple.cpp"},"position":{"line":0,"character":27}}}
+#      CHECK:    "containerName": "",
+# CHECK-NEXT:    "id": "CA2EBE44A1D76D2A1547D47BC6D51EBF",
+# CHECK-NEXT:    "name": "foo",
+# CHECK-NEXT:    "usr": "c:@F@foo#"
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///nested-decl.cpp","languageId":"cpp","version":1,"text":"namespace nnn { struct aaa {}; void foo() { aaa a; }; }"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///nested-decl.cpp"},"position":{"line":0,"character":46}}}
+#      CHECK:    "containerName": "nnn::",
+# CHECK-NEXT:    "id": "20237FF18EB405D842456DC5D578426D",
+# CHECK-NEXT:    "name": "aaa",
+# CHECK-NEXT:    "usr": "c:@N@nnn@S@aaa"
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///reference.cpp","languageId":"cpp","version":1,"text":"int value; void foo(int) {} void bar() { foo(value); }"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///reference.cpp"},"position":{"line":0,"character":48}}}
+#      CHECK:    "containerName": "",
+# CHECK-NEXT:    "id": "844613FB2393C9D40A2AFF25D5D316A1",
+# CHECK-NEXT:    "name": "value",
+# CHECK-NEXT:    "usr": "c:@value"
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///local.cpp","languageId":"cpp","version":1,"text":"void foo() { int aaa; int bbb = aaa; }"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///local.cpp"},"position":{"line":0,"character":33}}}
+#      CHECK:    "containerName": "",
+# CHECK-NEXT:    "id": "C05589F2664B06F392C2C438568E55E0",
+# CHECK-NEXT:    "name": "aaa",
+# CHECK-NEXT:    "usr": "c:local.cpp@13@F@foo#@aaa"
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///macro.cpp","languageId":"cpp","version":1,"text":"#define MACRO 5\nint i = MACRO;"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///macro.cpp"},"position":{"line":1,"character":11}}}
+#      CHECK:    "containerName": "",
+# CHECK-NEXT:    "id": "29EB506CBDF1BA6D1B6EC203FF03B384",
+# CHECK-NEXT:    "name": "MACRO",
+# CHECK-NEXT:    "usr": "c:macro.cpp@24@macro@MACRO"
+---
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
Index: XRefs.h
===================================================================
--- XRefs.h
+++ XRefs.h
@@ -38,6 +38,9 @@
 std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
                                      const SymbolIndex *Index = nullptr);
 
+/// Get info about symbol at \p Cursor.
+llvm::Optional<IdentifierData> getCursorInfo(ParsedAST &AST, Position Pos);
+
 } // namespace clangd
 } // namespace clang
 
Index: XRefs.cpp
===================================================================
--- XRefs.cpp
+++ XRefs.cpp
@@ -92,11 +92,14 @@
   const SourceLocation &SearchedLocation;
   const ASTContext &AST;
   Preprocessor &PP;
+  const bool StopOnFirstDeclFound;
 
 public:
   DeclarationAndMacrosFinder(const SourceLocation &SearchedLocation,
-                             ASTContext &AST, Preprocessor &PP)
-      : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {}
+                             ASTContext &AST, Preprocessor &PP,
+                             bool StopOnFirstDeclFound)
+      : SearchedLocation(SearchedLocation), AST(AST), PP(PP),
+        StopOnFirstDeclFound(StopOnFirstDeclFound) {}
 
   // Get all DeclInfo of the found declarations.
   // The results are sorted by "IsReferencedExplicitly" and declaration
@@ -153,6 +156,12 @@
       };
 
       bool IsExplicit = !hasImplicitExpr(ASTNode.OrigE);
+
+      if (StopOnFirstDeclFound) {
+        Decls[D] |= IsExplicit;
+        return false;
+      }
+
       // Find and add definition declarations (for GoToDefinition).
       // We don't use parameter `D`, as Parameter `D` is the canonical
       // declaration, which is the first declaration of a redeclarable
@@ -203,9 +212,10 @@
   std::vector<MacroDecl> Macros;
 };
 
-IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) {
-  auto DeclMacrosFinder = DeclarationAndMacrosFinder(Pos, AST.getASTContext(),
-                                                     AST.getPreprocessor());
+IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos,
+                                     bool ReturnFirstDeclFound = false) {
+  auto DeclMacrosFinder = DeclarationAndMacrosFinder(
+      Pos, AST.getASTContext(), AST.getPreprocessor(), ReturnFirstDeclFound);
   index::IndexingOptions IndexOpts;
   IndexOpts.SystemSymbolFilter =
       index::IndexingOptions::SystemSymbolFilterKind::All;
@@ -749,5 +759,42 @@
   return Results;
 }
 
+llvm::Optional<IdentifierData> getCursorInfo(ParsedAST &AST, Position Pos) {
+  const SourceManager &SM = AST.getASTContext().getSourceManager();
+
+  auto Loc = getBeginningOfIdentifier(AST, Pos, SM.getMainFileID());
+  auto Symbols = getSymbolAtPosition(AST, Loc, true);
+
+  IdentifierData Result;
+
+  if (!Symbols.Decls.empty()) {
+    if (const NamedDecl *ND = dyn_cast<NamedDecl>(Symbols.Decls.front().D)) {
+      std::string QName = printQualifiedName(*ND);
+      std::tie(Result.containerName, Result.name) = splitQualifiedName(QName);
+
+      StringRef ContainerNameRef = Result.containerName;
+      ContainerNameRef.consume_back("::");
+    }
+    if (!index::generateUSRForDecl(Symbols.Decls.front().D, Result.USR)) {
+      Result.ID = SymbolID(Result.USR).str();
+    }
+    // TODO discuss in the review
+    if (!Result.USR.empty())
+      return Result;
+  }
+
+  if (!Symbols.Macros.empty()) {
+    Result.name = Symbols.Macros.front().Name;
+    if (!index::generateUSRForMacro(Result.name, Loc, SM, Result.USR)) {
+      Result.ID = SymbolID(Result.USR).str();
+    }
+    // TODO discuss in the review
+    if (!Result.USR.empty())
+      return Result;
+  }
+
+  return llvm::None;
+}
+
 } // namespace clangd
 } // namespace clang
Index: Protocol.h
===================================================================
--- Protocol.h
+++ Protocol.h
@@ -673,6 +673,24 @@
 llvm::json::Value toJSON(const SymbolInformation &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolInformation &);
 
+/// Represents information about identifier.
+struct IdentifierData {
+  /// The name of this symbol.
+  std::string name;
+
+  /// The name of the symbol containing this symbol.
+  std::string containerName;
+
+  /// The USR of this symbol.
+  llvm::SmallString<128> USR;
+
+  /// The hex-encoded ID of this symbol.
+  /// Size is dependent on clang::clangd::SymbolID::RawValue.
+  llvm::SmallString<32> ID;
+};
+llvm::json::Value toJSON(const IdentifierData &);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const IdentifierData &);
+
 /// The parameters of a Workspace Symbol Request.
 struct WorkspaceSymbolParams {
   /// A non-empty query string
Index: Protocol.cpp
===================================================================
--- Protocol.cpp
+++ Protocol.cpp
@@ -422,6 +422,21 @@
   return O;
 }
 
+llvm::json::Value toJSON(const IdentifierData &P) {
+  return json::Object{
+      {"name", P.name},
+      {"containerName", P.containerName},
+      {"usr", P.USR},
+      {"id", P.ID},
+  };
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
+                              const IdentifierData &SUCI) {
+  O << SUCI.containerName << "::" << SUCI.name << " - " << toJSON(SUCI);
+  return O;
+}
+
 bool fromJSON(const json::Value &Params, WorkspaceSymbolParams &R) {
   json::ObjectMapper O(Params);
   return O && O.map("query", R.query);
Index: ClangdServer.h
===================================================================
--- ClangdServer.h
+++ ClangdServer.h
@@ -201,6 +201,10 @@
   /// Called when an event occurs for a watched file in the workspace.
   void onFileEvent(const DidChangeWatchedFilesParams &Params);
 
+  /// Get cursor info for given position.
+  void getCursorInfo(PathRef File, Position Pos,
+                     Callback<llvm::Optional<IdentifierData>> CB);
+
   /// Returns estimated memory usage for each of the currently open files.
   /// The order of results is unspecified.
   /// Overall memory usage of clangd may be significantly more than reported
Index: ClangdServer.cpp
===================================================================
--- ClangdServer.cpp
+++ ClangdServer.cpp
@@ -517,6 +517,18 @@
   WorkScheduler.runWithAST("References", File, Bind(Action, std::move(CB)));
 }
 
+void ClangdServer::getCursorInfo(PathRef File, Position Pos,
+                                 Callback<llvm::Optional<IdentifierData>> CB) {
+  auto Action = [Pos, this](Callback<llvm::Optional<IdentifierData>> CB,
+                            Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::getCursorInfo(InpAST->AST, Pos));
+  };
+
+  WorkScheduler.runWithAST("CursorInfo", File, Bind(Action, std::move(CB)));
+}
+
 std::vector<std::pair<Path, std::size_t>>
 ClangdServer::getUsedBytesPerFile() const {
   return WorkScheduler.getUsedBytesPerFile();
Index: ClangdLSPServer.h
===================================================================
--- ClangdLSPServer.h
+++ ClangdLSPServer.h
@@ -88,6 +88,8 @@
   void onHover(const TextDocumentPositionParams &,
                Callback<llvm::Optional<Hover>>);
   void onChangeConfiguration(const DidChangeConfigurationParams &);
+  void onCursorInfo(const TextDocumentPositionParams &,
+                    Callback<llvm::Optional<IdentifierData>>);
 
   std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D);
 
Index: ClangdLSPServer.cpp
===================================================================
--- ClangdLSPServer.cpp
+++ ClangdLSPServer.cpp
@@ -685,6 +685,12 @@
                          std::move(Reply));
 }
 
+void ClangdLSPServer::onCursorInfo(const TextDocumentPositionParams &Params,
+                                   Callback<Optional<IdentifierData>> Reply) {
+  Server->getCursorInfo(Params.textDocument.uri.file(), Params.position,
+                        std::move(Reply));
+}
+
 ClangdLSPServer::ClangdLSPServer(class Transport &Transp,
                                  const clangd::CodeCompleteOptions &CCOpts,
                                  Optional<Path> CompileCommandsDir,
@@ -719,6 +725,7 @@
   MsgHandler->bind("textDocument/didChange", &ClangdLSPServer::onDocumentDidChange);
   MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent);
   MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration);
+  MsgHandler->bind("textDocument/cursorInfo", &ClangdLSPServer::onCursorInfo);
   // clang-format on
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to