[clang-tools-extra] r325395 - [clangd] Implement textDocument/hover

Marc-Andre Laperle via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 16 13:38:15 PST 2018


Author: malaperle
Date: Fri Feb 16 13:38:15 2018
New Revision: 325395

URL: http://llvm.org/viewvc/llvm-project?rev=325395&view=rev
Log:
[clangd] Implement textDocument/hover

Summary: Implemention of textDocument/hover as described in LSP definition.

This patch adds a basic Hover implementation.  When hovering a variable,
function, method or namespace, clangd will return a text containing the
declaration's scope, as well as the declaration of the hovered entity.
For example, for a variable:

  Declared in class Foo::Bar

  int hello = 2

For macros, the macro definition is returned.

This patch doesn't include:

- markdown support (the client I use doesn't support it yet)
- range support (optional in the Hover response)
- comments associated to variables/functions/classes

They are kept as future work to keep this patch simpler.

I added tests in XRefsTests.cpp.  hover.test contains one simple
smoketest to make sure the feature works from a black box perspective.

Reviewers: malaperle, krasimir, bkramer, ilya-biryukov

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

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

Signed-off-by: Simon Marchi <simon.marchi at ericsson.com>
Signed-off-by: William Enright <william.enright at polymtl.ca>

Added:
    clang-tools-extra/trunk/test/clangd/hover.test
Modified:
    clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
    clang-tools-extra/trunk/clangd/ClangdLSPServer.h
    clang-tools-extra/trunk/clangd/ClangdServer.cpp
    clang-tools-extra/trunk/clangd/ClangdServer.h
    clang-tools-extra/trunk/clangd/Protocol.cpp
    clang-tools-extra/trunk/clangd/Protocol.h
    clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
    clang-tools-extra/trunk/clangd/ProtocolHandlers.h
    clang-tools-extra/trunk/clangd/XRefs.cpp
    clang-tools-extra/trunk/clangd/XRefs.h
    clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
    clang-tools-extra/trunk/test/clangd/initialize-params.test
    clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Fri Feb 16 13:38:15 2018
@@ -118,6 +118,7 @@ void ClangdLSPServer::onInitialize(Initi
              }},
             {"definitionProvider", true},
             {"documentHighlightProvider", true},
+            {"hoverProvider", true},
             {"renameProvider", true},
             {"executeCommandProvider",
              json::obj{
@@ -355,6 +356,19 @@ void ClangdLSPServer::onDocumentHighligh
       });
 }
 
+void ClangdLSPServer::onHover(TextDocumentPositionParams &Params) {
+  Server.findHover(Params.textDocument.uri.file(), Params.position,
+                   [](llvm::Expected<Tagged<Hover>> H) {
+                     if (!H) {
+                       replyError(ErrorCode::InternalError,
+                                  llvm::toString(H.takeError()));
+                       return;
+                     }
+
+                     reply(H->Value);
+                   });
+}
+
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
                                  bool StorePreamblesInMemory,
                                  const clangd::CodeCompleteOptions &CCOpts,

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Fri Feb 16 13:38:15 2018
@@ -75,6 +75,7 @@ private:
   void onFileEvent(DidChangeWatchedFilesParams &Params) override;
   void onCommand(ExecuteCommandParams &Params) override;
   void onRename(RenameParams &Parames) override;
+  void onHover(TextDocumentPositionParams &Params) override;
 
   std::vector<TextEdit> getFixIts(StringRef File, const clangd::Diagnostic &D);
 

Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Fri Feb 16 13:38:15 2018
@@ -491,6 +491,29 @@ void ClangdServer::findDocumentHighlight
   WorkScheduler.runWithAST(File, BindWithForward(Action, std::move(Callback)));
 }
 
+void ClangdServer::findHover(
+    PathRef File, Position Pos,
+    UniqueFunction<void(llvm::Expected<Tagged<Hover>>)> Callback) {
+  Hover FinalHover;
+  auto FileContents = DraftMgr.getDraft(File);
+  if (!FileContents.Draft)
+    return Callback(llvm::make_error<llvm::StringError>(
+        "findHover called on non-added file", llvm::errc::invalid_argument));
+
+  auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+
+  auto Action = [Pos, TaggedFS](decltype(Callback) Callback,
+                                llvm::Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return Callback(InpAST.takeError());
+
+    Hover Result = clangd::getHover(InpAST->AST, Pos);
+    Callback(make_tagged(std::move(Result), TaggedFS.Tag));
+  };
+
+  WorkScheduler.runWithAST(File, BindWithForward(Action, std::move(Callback)));
+}
+
 void ClangdServer::scheduleReparseAndDiags(
     PathRef File, VersionedDraft Contents,
     Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS) {

Modified: clang-tools-extra/trunk/clangd/ClangdServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.h Fri Feb 16 13:38:15 2018
@@ -212,6 +212,10 @@ public:
           void(llvm::Expected<Tagged<std::vector<DocumentHighlight>>>)>
           Callback);
 
+  /// Get code hover for a given position.
+  void findHover(PathRef File, Position Pos,
+                 UniqueFunction<void(llvm::Expected<Tagged<Hover>>)> Callback);
+
   /// Run formatting for \p Rng inside \p File with content \p Code.
   llvm::Expected<tooling::Replacements> formatRange(StringRef Code,
                                                     PathRef File, Range Rng);

Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Fri Feb 16 13:38:15 2018
@@ -386,6 +386,35 @@ bool fromJSON(const json::Expr &Params,
          O.map("position", R.position);
 }
 
+static StringRef toTextKind(MarkupKind Kind) {
+  switch (Kind) {
+  case MarkupKind::PlainText:
+    return "plaintext";
+  case MarkupKind::Markdown:
+    return "markdown";
+  }
+  llvm_unreachable("Invalid MarkupKind");
+}
+
+json::Expr toJSON(const MarkupContent &MC) {
+  if (MC.Value.empty())
+    return nullptr;
+
+  return json::obj{
+      {"kind", toTextKind(MC.Kind)},
+      {"value", MC.Value},
+  };
+}
+
+json::Expr toJSON(const Hover &H) {
+  json::obj Result{{"contents", toJSON(H.Contents)}};
+
+  if (H.Range.hasValue())
+    Result["range"] = toJSON(*H.Range);
+
+  return std::move(Result);
+}
+
 json::Expr toJSON(const CompletionItem &CI) {
   assert(!CI.label.empty() && "completion item label is required");
   json::obj Result{{"label", CI.label}};

Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Fri Feb 16 13:38:15 2018
@@ -480,6 +480,27 @@ struct TextDocumentPositionParams {
 };
 bool fromJSON(const json::Expr &, TextDocumentPositionParams &);
 
+enum class MarkupKind {
+  PlainText,
+  Markdown,
+};
+
+struct MarkupContent {
+  MarkupKind Kind = MarkupKind::PlainText;
+  std::string Value;
+};
+json::Expr toJSON(const MarkupContent &MC);
+
+struct Hover {
+  /// The hover's content
+  MarkupContent Contents;
+
+  /// An optional range is a range inside a text document
+  /// that is used to visualize a hover, e.g. by changing the background color.
+  llvm::Optional<Range> Range;
+};
+json::Expr toJSON(const Hover &H);
+
 /// The kind of a completion entry.
 enum class CompletionItemKind {
   Missing = 0,

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp Fri Feb 16 13:38:15 2018
@@ -67,6 +67,7 @@ void clangd::registerCallbackHandlers(JS
   Register("textDocument/switchSourceHeader",
            &ProtocolCallbacks::onSwitchSourceHeader);
   Register("textDocument/rename", &ProtocolCallbacks::onRename);
+  Register("textDocument/hover", &ProtocolCallbacks::onHover);
   Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
   Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
   Register("textDocument/documentHighlight",

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.h?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.h (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.h Fri Feb 16 13:38:15 2018
@@ -51,6 +51,7 @@ public:
   virtual void onCommand(ExecuteCommandParams &Params) = 0;
   virtual void onRename(RenameParams &Parames) = 0;
   virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0;
+  virtual void onHover(TextDocumentPositionParams &Params) = 0;
 };
 
 void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,

Modified: clang-tools-extra/trunk/clangd/XRefs.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.cpp?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/XRefs.cpp (original)
+++ clang-tools-extra/trunk/clangd/XRefs.cpp Fri Feb 16 13:38:15 2018
@@ -9,6 +9,7 @@
 #include "XRefs.h"
 #include "Logger.h"
 #include "URI.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/IndexingAction.h"
 #include "llvm/Support/Path.h"
@@ -31,10 +32,15 @@ const Decl* GetDefinition(const Decl* D)
   return nullptr;
 }
 
+struct MacroDecl {
+  StringRef Name;
+  const MacroInfo *Info;
+};
+
 /// Finds declarations locations that a given source location refers to.
 class DeclarationAndMacrosFinder : public index::IndexDataConsumer {
   std::vector<const Decl *> Decls;
-  std::vector<const MacroInfo *> MacroInfos;
+  std::vector<MacroDecl> MacroInfos;
   const SourceLocation &SearchedLocation;
   const ASTContext &AST;
   Preprocessor &PP;
@@ -54,10 +60,17 @@ public:
     return std::move(Decls);
   }
 
-  std::vector<const MacroInfo *> takeMacroInfos() {
+  std::vector<MacroDecl> takeMacroInfos() {
     // Don't keep the same Macro info multiple times.
-    std::sort(MacroInfos.begin(), MacroInfos.end());
-    auto Last = std::unique(MacroInfos.begin(), MacroInfos.end());
+    std::sort(MacroInfos.begin(), MacroInfos.end(),
+              [](const MacroDecl &Left, const MacroDecl &Right) {
+                return Left.Info < Right.Info;
+              });
+
+    auto Last = std::unique(MacroInfos.begin(), MacroInfos.end(),
+                            [](const MacroDecl &Left, const MacroDecl &Right) {
+                              return Left.Info == Right.Info;
+                            });
     MacroInfos.erase(Last, MacroInfos.end());
     return std::move(MacroInfos);
   }
@@ -111,7 +124,7 @@ private:
             PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation);
         MacroInfo *MacroInf = MacroDef.getMacroInfo();
         if (MacroInf) {
-          MacroInfos.push_back(MacroInf);
+          MacroInfos.push_back(MacroDecl{IdentifierInfo->getName(), MacroInf});
         }
       }
     }
@@ -176,8 +189,7 @@ std::vector<Location> findDefinitions(Pa
                      DeclMacrosFinder, IndexOpts);
 
   std::vector<const Decl *> Decls = DeclMacrosFinder->takeDecls();
-  std::vector<const MacroInfo *> MacroInfos =
-      DeclMacrosFinder->takeMacroInfos();
+  std::vector<MacroDecl> MacroInfos = DeclMacrosFinder->takeMacroInfos();
   std::vector<Location> Result;
 
   for (auto Item : Decls) {
@@ -187,7 +199,8 @@ std::vector<Location> findDefinitions(Pa
   }
 
   for (auto Item : MacroInfos) {
-    SourceRange SR(Item->getDefinitionLoc(), Item->getDefinitionEndLoc());
+    SourceRange SR(Item.Info->getDefinitionLoc(),
+                   Item.Info->getDefinitionEndLoc());
     auto L = getDeclarationLocation(AST, SR);
     if (L)
       Result.push_back(*L);
@@ -299,5 +312,138 @@ std::vector<DocumentHighlight> findDocum
   return DocHighlightsFinder->takeHighlights();
 }
 
+static PrintingPolicy PrintingPolicyForDecls(PrintingPolicy Base) {
+  PrintingPolicy Policy(Base);
+
+  Policy.AnonymousTagLocations = false;
+  Policy.TerseOutput = true;
+  Policy.PolishForDeclaration = true;
+  Policy.ConstantsAsWritten = true;
+  Policy.SuppressTagKeyword = false;
+
+  return Policy;
+}
+
+/// Return a string representation (e.g. "class MyNamespace::MyClass") of
+/// the type declaration \p TD.
+static std::string TypeDeclToString(const TypeDecl *TD) {
+  QualType Type = TD->getASTContext().getTypeDeclType(TD);
+
+  PrintingPolicy Policy =
+      PrintingPolicyForDecls(TD->getASTContext().getPrintingPolicy());
+
+  std::string Name;
+  llvm::raw_string_ostream Stream(Name);
+  Type.print(Stream, Policy);
+
+  return Stream.str();
+}
+
+/// Return a string representation (e.g. "namespace ns1::ns2") of
+/// the named declaration \p ND.
+static std::string NamedDeclQualifiedName(const NamedDecl *ND,
+                                          StringRef Prefix) {
+  PrintingPolicy Policy =
+      PrintingPolicyForDecls(ND->getASTContext().getPrintingPolicy());
+
+  std::string Name;
+  llvm::raw_string_ostream Stream(Name);
+  Stream << Prefix << ' ';
+  ND->printQualifiedName(Stream, Policy);
+
+  return Stream.str();
+}
+
+/// Given a declaration \p D, return a human-readable string representing the
+/// scope in which it is declared.  If the declaration is in the global scope,
+/// return the string "global namespace".
+static llvm::Optional<std::string> getScopeName(const Decl *D) {
+  const DeclContext *DC = D->getDeclContext();
+
+  if (const TranslationUnitDecl *TUD = dyn_cast<TranslationUnitDecl>(DC))
+    return std::string("global namespace");
+
+  if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC))
+    return TypeDeclToString(TD);
+  else if (const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC))
+    return NamedDeclQualifiedName(ND, "namespace");
+  else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
+    return NamedDeclQualifiedName(FD, "function");
+
+  return llvm::None;
+}
+
+/// Generate a \p Hover object given the declaration \p D.
+static Hover getHoverContents(const Decl *D) {
+  Hover H;
+  llvm::Optional<std::string> NamedScope = getScopeName(D);
+
+  // Generate the "Declared in" section.
+  if (NamedScope) {
+    assert(!NamedScope->empty());
+
+    H.Contents.Value += "Declared in ";
+    H.Contents.Value += *NamedScope;
+    H.Contents.Value += "\n\n";
+  }
+
+  // We want to include the template in the Hover.
+  if (TemplateDecl *TD = D->getDescribedTemplate())
+    D = TD;
+
+  std::string DeclText;
+  llvm::raw_string_ostream OS(DeclText);
+
+  PrintingPolicy Policy =
+      PrintingPolicyForDecls(D->getASTContext().getPrintingPolicy());
+
+  D->print(OS, Policy);
+
+  OS.flush();
+
+  H.Contents.Value += DeclText;
+  return H;
+}
+
+/// Generate a \p Hover object given the macro \p MacroInf.
+static Hover getHoverContents(StringRef MacroName) {
+  Hover H;
+
+  H.Contents.Value = "#define ";
+  H.Contents.Value += MacroName;
+
+  return H;
+}
+
+Hover getHover(ParsedAST &AST, Position Pos) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+  if (FE == nullptr)
+    return Hover();
+
+  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;
+
+  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+                     DeclMacrosFinder, IndexOpts);
+
+  std::vector<MacroDecl> Macros = DeclMacrosFinder->takeMacroInfos();
+  if (!Macros.empty())
+    return getHoverContents(Macros[0].Name);
+
+  std::vector<const Decl *> Decls = DeclMacrosFinder->takeDecls();
+  if (!Decls.empty())
+    return getHoverContents(Decls[0]);
+
+  return Hover();
+}
+
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/clangd/XRefs.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.h?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/XRefs.h (original)
+++ clang-tools-extra/trunk/clangd/XRefs.h Fri Feb 16 13:38:15 2018
@@ -27,6 +27,9 @@ std::vector<Location> findDefinitions(Pa
 std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
                                                       Position Pos);
 
+/// Get the hover information when hovering at \p Pos.
+Hover getHover(ParsedAST &AST, Position Pos);
+
 } // namespace clangd
 } // namespace clang
 #endif

Added: clang-tools-extra/trunk/test/clangd/hover.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/hover.test?rev=325395&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clangd/hover.test (added)
+++ clang-tools-extra/trunk/test/clangd/hover.test Fri Feb 16 13:38:15 2018
@@ -0,0 +1,19 @@
+# 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":"file:///main.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":0,"character":27}}}
+#      CHECK:  "id": 1,
+# CHECK-NEXT:  "jsonrpc": "2.0",
+# CHECK-NEXT:  "result": {
+# CHECK-NEXT:    "contents": {
+# CHECK-NEXT:      "kind": "plaintext",
+# CHECK-NEXT:      "value": "Declared in global namespace\n\nvoid foo()"
+# CHECK-NEXT:    }
+# CHECK-NEXT:  }
+# CHECK-NEXT:}
+---
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test (original)
+++ clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test Fri Feb 16 13:38:15 2018
@@ -28,6 +28,7 @@
 # CHECK-NEXT:          "clangd.insertInclude"
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
+# CHECK-NEXT:      "hoverProvider": true,
 # CHECK-NEXT:      "renameProvider": true,
 # CHECK-NEXT:      "signatureHelpProvider": {
 # CHECK-NEXT:        "triggerCharacters": [

Modified: clang-tools-extra/trunk/test/clangd/initialize-params.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params.test?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/initialize-params.test (original)
+++ clang-tools-extra/trunk/test/clangd/initialize-params.test Fri Feb 16 13:38:15 2018
@@ -28,6 +28,7 @@
 # CHECK-NEXT:          "clangd.insertInclude"
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
+# CHECK-NEXT:      "hoverProvider": true,
 # CHECK-NEXT:      "renameProvider": true,
 # CHECK-NEXT:      "signatureHelpProvider": {
 # CHECK-NEXT:        "triggerCharacters": [

Modified: clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp?rev=325395&r1=325394&r2=325395&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp Fri Feb 16 13:38:15 2018
@@ -258,6 +258,311 @@ int baz = f^oo;
       ElementsAre(Location{URIForFile{FooCpp}, SourceAnnotations.range()}));
 }
 
+TEST(Hover, All) {
+  struct OneTest {
+    StringRef Input;
+    StringRef ExpectedHover;
+  };
+
+  OneTest Tests[] = {
+      {
+          R"cpp(// Local variable
+            int main() {
+              int bonjour;
+              ^bonjour = 2;
+              int test1 = bonjour;
+            }
+          )cpp",
+          "Declared in function main\n\nint bonjour",
+      },
+      {
+          R"cpp(// Local variable in method
+            struct s {
+              void method() {
+                int bonjour;
+                ^bonjour = 2;
+              }
+            };
+          )cpp",
+          "Declared in function s::method\n\nint bonjour",
+      },
+      {
+          R"cpp(// Struct
+            namespace ns1 {
+              struct MyClass {};
+            } // namespace ns1
+            int main() {
+              ns1::My^Class* Params;
+            }
+          )cpp",
+          "Declared in namespace ns1\n\nstruct MyClass {}",
+      },
+      {
+          R"cpp(// Class
+            namespace ns1 {
+              class MyClass {};
+            } // namespace ns1
+            int main() {
+              ns1::My^Class* Params;
+            }
+          )cpp",
+          "Declared in namespace ns1\n\nclass MyClass {}",
+      },
+      {
+          R"cpp(// Union
+            namespace ns1 {
+              union MyUnion { int x; int y; };
+            } // namespace ns1
+            int main() {
+              ns1::My^Union Params;
+            }
+          )cpp",
+          "Declared in namespace ns1\n\nunion MyUnion {}",
+      },
+      {
+          R"cpp(// Function definition via pointer
+            int foo(int) {}
+            int main() {
+              auto *X = &^foo;
+            }
+          )cpp",
+          "Declared in global namespace\n\nint foo(int)",
+      },
+      {
+          R"cpp(// Function declaration via call
+            int foo(int);
+            int main() {
+              return ^foo(42);
+            }
+          )cpp",
+          "Declared in global namespace\n\nint foo(int)",
+      },
+      {
+          R"cpp(// Field
+            struct Foo { int x; };
+            int main() {
+              Foo bar;
+              bar.^x;
+            }
+          )cpp",
+          "Declared in struct Foo\n\nint x",
+      },
+      {
+          R"cpp(// Field with initialization
+            struct Foo { int x = 5; };
+            int main() {
+              Foo bar;
+              bar.^x;
+            }
+          )cpp",
+          "Declared in struct Foo\n\nint x = 5",
+      },
+      {
+          R"cpp(// Static field
+            struct Foo { static int x; };
+            int main() {
+              Foo::^x;
+            }
+          )cpp",
+          "Declared in struct Foo\n\nstatic int x",
+      },
+      {
+          R"cpp(// Field, member initializer
+            struct Foo {
+              int x;
+              Foo() : ^x(0) {}
+            };
+          )cpp",
+          "Declared in struct Foo\n\nint x",
+      },
+      {
+          R"cpp(// Field, GNU old-style field designator
+            struct Foo { int x; };
+            int main() {
+              Foo bar = { ^x : 1 };
+            }
+          )cpp",
+          "Declared in struct Foo\n\nint x",
+      },
+      {
+          R"cpp(// Field, field designator
+            struct Foo { int x; };
+            int main() {
+              Foo bar = { .^x = 2 };
+            }
+          )cpp",
+          "Declared in struct Foo\n\nint x",
+      },
+      {
+          R"cpp(// Method call
+            struct Foo { int x(); };
+            int main() {
+              Foo bar;
+              bar.^x();
+            }
+          )cpp",
+          "Declared in struct Foo\n\nint x()",
+      },
+      {
+          R"cpp(// Static method call
+            struct Foo { static int x(); };
+            int main() {
+              Foo::^x();
+            }
+          )cpp",
+          "Declared in struct Foo\n\nstatic int x()",
+      },
+      {
+          R"cpp(// Typedef
+            typedef int Foo;
+            int main() {
+              ^Foo bar;
+            }
+          )cpp",
+          "Declared in global namespace\n\ntypedef int Foo",
+      },
+      {
+          R"cpp(// Namespace
+            namespace ns {
+            struct Foo { static void bar(); }
+            } // namespace ns
+            int main() { ^ns::Foo::bar(); }
+          )cpp",
+          "Declared in global namespace\n\nnamespace ns {\n}",
+      },
+      {
+          R"cpp(// Anonymous namespace
+            namespace ns {
+              namespace {
+                int foo;
+              } // anonymous namespace
+            } // namespace ns
+            int main() { ns::f^oo++; }
+          )cpp",
+          "Declared in namespace ns::(anonymous)\n\nint foo",
+      },
+      {
+          R"cpp(// Macro
+            #define MACRO 0
+            #define MACRO 1
+            int main() { return ^MACRO; }
+            #define MACRO 2
+            #undef macro
+          )cpp",
+          "#define MACRO",
+      },
+      {
+          R"cpp(// Forward class declaration
+            class Foo;
+            class Foo {};
+            F^oo* foo();
+          )cpp",
+          "Declared in global namespace\n\nclass Foo {}",
+      },
+      {
+          R"cpp(// Function declaration
+            void foo();
+            void g() { f^oo(); }
+            void foo() {}
+          )cpp",
+          "Declared in global namespace\n\nvoid foo()",
+      },
+      {
+          R"cpp(// Enum declaration
+            enum Hello {
+              ONE, TWO, THREE,
+            };
+            void foo() {
+              Hel^lo hello = ONE;
+            }
+          )cpp",
+          "Declared in global namespace\n\nenum Hello {\n}",
+      },
+      {
+          R"cpp(// Enumerator
+            enum Hello {
+              ONE, TWO, THREE,
+            };
+            void foo() {
+              Hello hello = O^NE;
+            }
+          )cpp",
+          "Declared in enum Hello\n\nONE",
+      },
+      {
+          R"cpp(// Enumerator in anonymous enum
+            enum {
+              ONE, TWO, THREE,
+            };
+            void foo() {
+              int hello = O^NE;
+            }
+          )cpp",
+          "Declared in enum (anonymous)\n\nONE",
+      },
+      {
+          R"cpp(// Global variable
+            static int hey = 10;
+            void foo() {
+              he^y++;
+            }
+          )cpp",
+          "Declared in global namespace\n\nstatic int hey = 10",
+      },
+      {
+          R"cpp(// Global variable in namespace
+            namespace ns1 {
+              static int hey = 10;
+            }
+            void foo() {
+              ns1::he^y++;
+            }
+          )cpp",
+          "Declared in namespace ns1\n\nstatic int hey = 10",
+      },
+      {
+          R"cpp(// Field in anonymous struct
+            static struct {
+              int hello;
+            } s;
+            void foo() {
+              s.he^llo++;
+            }
+          )cpp",
+          "Declared in struct (anonymous)\n\nint hello",
+      },
+      {
+          R"cpp(// Templated function
+            template <typename T>
+            T foo() {
+              return 17;
+            }
+            void g() { auto x = f^oo<int>(); }
+          )cpp",
+          "Declared in global namespace\n\ntemplate <typename T> T foo()",
+      },
+      {
+          R"cpp(// Anonymous union
+            struct outer {
+              union {
+                int abc, def;
+              } v;
+            };
+            void g() { struct outer o; o.v.d^ef++; }
+          )cpp",
+          "Declared in union outer::(anonymous)\n\nint def",
+      },
+  };
+
+  for (const OneTest &Test : Tests) {
+    Annotations T(Test.Input);
+    auto AST = build(T.code());
+    Hover H = getHover(AST, T.point());
+
+    EXPECT_EQ(H.Contents.Value, Test.ExpectedHover) << Test.Input;
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang




More information about the cfe-commits mailing list