[clang-tools-extra] r336386 - [clangd] Implementation of textDocument/documentSymbol

Marc-Andre Laperle via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 5 12:35:01 PDT 2018


Author: malaperle
Date: Thu Jul  5 12:35:01 2018
New Revision: 336386

URL: http://llvm.org/viewvc/llvm-project?rev=336386&view=rev
Log:
[clangd] Implementation of textDocument/documentSymbol

Summary:
An AST-based approach is used to retrieve the document symbols rather than an
in-memory index query. The index is not an ideal fit to achieve this because of
the file-centric query being done here whereas the index is suited for
project-wide queries. Document symbols also includes more symbols and need to
keep the order as seen in the file.

Signed-off-by: Marc-Andre Laperle <marc-andre.laperle at ericsson.com>

Subscribers: tomgr, ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits

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

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/FindSymbols.cpp
    clang-tools-extra/trunk/clangd/FindSymbols.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/SourceCode.cpp
    clang-tools-extra/trunk/clangd/SourceCode.h
    clang-tools-extra/trunk/clangd/XRefs.cpp
    clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
    clang-tools-extra/trunk/test/clangd/initialize-params.test
    clang-tools-extra/trunk/test/clangd/symbols.test
    clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp
    clang-tools-extra/trunk/unittests/clangd/SyncAPI.cpp
    clang-tools-extra/trunk/unittests/clangd/SyncAPI.h

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Thu Jul  5 12:35:01 2018
@@ -112,6 +112,7 @@ void ClangdLSPServer::onInitialize(Initi
             {"documentHighlightProvider", true},
             {"hoverProvider", true},
             {"renameProvider", true},
+            {"documentSymbolProvider", true},
             {"workspaceSymbolProvider", true},
             {"executeCommandProvider",
              json::obj{
@@ -294,6 +295,19 @@ void ClangdLSPServer::onDocumentFormatti
                llvm::toString(ReplacementsOrError.takeError()));
 }
 
+void ClangdLSPServer::onDocumentSymbol(DocumentSymbolParams &Params) {
+  Server.documentSymbols(
+      Params.textDocument.uri.file(),
+      [this](llvm::Expected<std::vector<SymbolInformation>> Items) {
+        if (!Items)
+          return replyError(ErrorCode::InvalidParams,
+                            llvm::toString(Items.takeError()));
+        for (auto &Sym : *Items)
+          Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
+        reply(json::ary(*Items));
+      });
+}
+
 void ClangdLSPServer::onCodeAction(CodeActionParams &Params) {
   // We provide a code action for each diagnostic at the requested location
   // which has FixIts available.

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Thu Jul  5 12:35:01 2018
@@ -62,6 +62,7 @@ private:
   void
   onDocumentRangeFormatting(DocumentRangeFormattingParams &Params) override;
   void onDocumentFormatting(DocumentFormattingParams &Params) override;
+  void onDocumentSymbol(DocumentSymbolParams &Params) override;
   void onCodeAction(CodeActionParams &Params) override;
   void onCompletion(TextDocumentPositionParams &Params) override;
   void onSignatureHelp(TextDocumentPositionParams &Params) override;

Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Thu Jul  5 12:35:01 2018
@@ -456,6 +456,18 @@ void ClangdServer::workspaceSymbols(
                                  RootPath ? *RootPath : ""));
 }
 
+void ClangdServer::documentSymbols(
+    StringRef File, Callback<std::vector<SymbolInformation>> CB) {
+  auto Action = [](Callback<std::vector<SymbolInformation>> CB,
+                   llvm::Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::getDocumentSymbols(InpAST->AST));
+  };
+  WorkScheduler.runWithAST("documentSymbols", File,
+                           Bind(Action, std::move(CB)));
+}
+
 std::vector<std::pair<Path, std::size_t>>
 ClangdServer::getUsedBytesPerFile() const {
   return WorkScheduler.getUsedBytesPerFile();

Modified: clang-tools-extra/trunk/clangd/ClangdServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.h Thu Jul  5 12:35:01 2018
@@ -167,6 +167,10 @@ public:
   void workspaceSymbols(StringRef Query, int Limit,
                         Callback<std::vector<SymbolInformation>> CB);
 
+  /// Retrieve the symbols within the specified file.
+  void documentSymbols(StringRef File,
+                       Callback<std::vector<SymbolInformation>> CB);
+
   /// 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/FindSymbols.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/FindSymbols.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/FindSymbols.cpp (original)
+++ clang-tools-extra/trunk/clangd/FindSymbols.cpp Thu Jul  5 12:35:01 2018
@@ -8,12 +8,16 @@
 //===----------------------------------------------------------------------===//
 #include "FindSymbols.h"
 
-#include "Logger.h"
+#include "AST.h"
+#include "ClangdUnit.h"
 #include "FuzzyMatch.h"
-#include "SourceCode.h"
+#include "Logger.h"
 #include "Quality.h"
+#include "SourceCode.h"
 #include "index/Index.h"
+#include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/IndexSymbol.h"
+#include "clang/Index/IndexingAction.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"
 
@@ -172,5 +176,106 @@ getWorkspaceSymbols(StringRef Query, int
   return Result;
 }
 
+namespace {
+/// Finds document symbols in the main file of the AST.
+class DocumentSymbolsConsumer : public index::IndexDataConsumer {
+  ASTContext &AST;
+  std::vector<SymbolInformation> Symbols;
+  // We are always list document for the same file, so cache the value.
+  llvm::Optional<URIForFile> MainFileUri;
+
+public:
+  DocumentSymbolsConsumer(ASTContext &AST) : AST(AST) {}
+  std::vector<SymbolInformation> takeSymbols() { return std::move(Symbols); }
+
+  void initialize(ASTContext &Ctx) override {
+    // Compute the absolute path of the main file which we will use for all
+    // results.
+    const SourceManager &SM = AST.getSourceManager();
+    const FileEntry *F = SM.getFileEntryForID(SM.getMainFileID());
+    if (!F)
+      return;
+    auto FilePath = getAbsoluteFilePath(F, SM);
+    if (FilePath)
+      MainFileUri = URIForFile(*FilePath);
+  }
+
+  bool shouldIncludeSymbol(const NamedDecl *ND) {
+    if (!ND || ND->isImplicit())
+      return false;
+    // Skip anonymous declarations, e.g (anonymous enum/class/struct).
+    if (ND->getDeclName().isEmpty())
+      return false;
+    return true;
+  }
+
+  bool
+  handleDeclOccurence(const Decl *, index::SymbolRoleSet Roles,
+                      ArrayRef<index::SymbolRelation> Relations,
+                      SourceLocation Loc,
+                      index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
+    assert(ASTNode.OrigD);
+    // No point in continuing the index consumer if we could not get the
+    // absolute path of the main file.
+    if (!MainFileUri)
+      return false;
+    // We only want declarations and definitions, i.e. no references.
+    if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
+          Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
+      return true;
+    SourceLocation NameLoc = findNameLoc(ASTNode.OrigD);
+    const SourceManager &SourceMgr = AST.getSourceManager();
+    // We should be only be looking at "local" decls in the main file.
+    if (!SourceMgr.isWrittenInMainFile(NameLoc)) {
+      // Even thought we are visiting only local (non-preamble) decls,
+      // we can get here when in the presense of "extern" decls.
+      return true;
+    }
+    const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(ASTNode.OrigD);
+    if (!shouldIncludeSymbol(ND))
+      return true;
+
+    SourceLocation EndLoc =
+        Lexer::getLocForEndOfToken(NameLoc, 0, SourceMgr, AST.getLangOpts());
+    Position Begin = sourceLocToPosition(SourceMgr, NameLoc);
+    Position End = sourceLocToPosition(SourceMgr, EndLoc);
+    Range R = {Begin, End};
+    Location L;
+    L.uri = *MainFileUri;
+    L.range = R;
+
+    std::string QName = printQualifiedName(*ND);
+    StringRef Scope, Name;
+    std::tie(Scope, Name) = splitQualifiedName(QName);
+    Scope.consume_back("::");
+
+    index::SymbolInfo SymInfo = index::getSymbolInfo(ND);
+    SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
+
+    SymbolInformation SI;
+    SI.name = Name;
+    SI.kind = SK;
+    SI.location = L;
+    SI.containerName = Scope;
+    Symbols.push_back(std::move(SI));
+    return true;
+  }
+};
+} // namespace
+
+llvm::Expected<std::vector<SymbolInformation>>
+getDocumentSymbols(ParsedAST &AST) {
+  DocumentSymbolsConsumer DocumentSymbolsCons(AST.getASTContext());
+
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly;
+  IndexOpts.IndexFunctionLocals = false;
+  indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(),
+                     DocumentSymbolsCons, IndexOpts);
+
+  return DocumentSymbolsCons.takeSymbols();
+}
+
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/clangd/FindSymbols.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/FindSymbols.h?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/FindSymbols.h (original)
+++ clang-tools-extra/trunk/clangd/FindSymbols.h Thu Jul  5 12:35:01 2018
@@ -17,6 +17,7 @@
 #include "llvm/ADT/StringRef.h"
 
 namespace clang {
+class ParsedAST;
 namespace clangd {
 class SymbolIndex;
 
@@ -33,6 +34,11 @@ llvm::Expected<std::vector<SymbolInforma
 getWorkspaceSymbols(llvm::StringRef Query, int Limit,
                     const SymbolIndex *const Index, llvm::StringRef HintPath);
 
+/// Retrieves the symbols contained in the "main file" section of an AST in the
+/// same order that they appear.
+llvm::Expected<std::vector<SymbolInformation>>
+getDocumentSymbols(ParsedAST &AST);
+
 } // namespace clangd
 } // namespace clang
 

Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Thu Jul  5 12:35:01 2018
@@ -342,6 +342,11 @@ bool fromJSON(const json::Expr &Params,
          O.map("options", R.options);
 }
 
+bool fromJSON(const json::Expr &Params, DocumentSymbolParams &R) {
+  json::ObjectMapper O(Params);
+  return O && O.map("textDocument", R.textDocument);
+}
+
 bool fromJSON(const json::Expr &Params, Diagnostic &R) {
   json::ObjectMapper O(Params);
   if (!O || !O.map("range", R.range) || !O.map("message", R.message))

Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Thu Jul  5 12:35:01 2018
@@ -479,6 +479,12 @@ struct DocumentFormattingParams {
 };
 bool fromJSON(const json::Expr &, DocumentFormattingParams &);
 
+struct DocumentSymbolParams {
+  // The text document to find symbols in.
+  TextDocumentIdentifier textDocument;
+};
+bool fromJSON(const json::Expr &, DocumentSymbolParams &);
+
 struct Diagnostic {
   /// The range at which the message applies.
   Range range;

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp Thu Jul  5 12:35:01 2018
@@ -66,6 +66,7 @@ void clangd::registerCallbackHandlers(JS
            &ProtocolCallbacks::onSwitchSourceHeader);
   Register("textDocument/rename", &ProtocolCallbacks::onRename);
   Register("textDocument/hover", &ProtocolCallbacks::onHover);
+  Register("textDocument/documentSymbol", &ProtocolCallbacks::onDocumentSymbol);
   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=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.h (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.h Thu Jul  5 12:35:01 2018
@@ -38,6 +38,7 @@ public:
   virtual void onDocumentDidChange(DidChangeTextDocumentParams &Params) = 0;
   virtual void onDocumentDidClose(DidCloseTextDocumentParams &Params) = 0;
   virtual void onDocumentFormatting(DocumentFormattingParams &Params) = 0;
+  virtual void onDocumentSymbol(DocumentSymbolParams &Params) = 0;
   virtual void
   onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams &Params) = 0;
   virtual void

Modified: clang-tools-extra/trunk/clangd/SourceCode.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/SourceCode.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/SourceCode.cpp (original)
+++ clang-tools-extra/trunk/clangd/SourceCode.cpp Thu Jul  5 12:35:01 2018
@@ -8,9 +8,13 @@
 //===----------------------------------------------------------------------===//
 #include "SourceCode.h"
 
+#include "Logger.h"
+#include "clang/AST/ASTContext.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
 
 namespace clang {
 namespace clangd {
@@ -181,5 +185,19 @@ std::vector<TextEdit> replacementsToEdit
   return Edits;
 }
 
+llvm::Optional<std::string>
+getAbsoluteFilePath(const FileEntry *F, const SourceManager &SourceMgr) {
+  SmallString<64> FilePath = F->tryGetRealPathName();
+  if (FilePath.empty())
+    FilePath = F->getName();
+  if (!llvm::sys::path::is_absolute(FilePath)) {
+    if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) {
+      log("Could not turn relative path to absolute: " + FilePath);
+      return llvm::None;
+    }
+  }
+  return FilePath.str().str();
+}
+
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/clangd/SourceCode.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/SourceCode.h?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/SourceCode.h (original)
+++ clang-tools-extra/trunk/clangd/SourceCode.h Thu Jul  5 12:35:01 2018
@@ -61,6 +61,9 @@ TextEdit replacementToEdit(StringRef Cod
 std::vector<TextEdit> replacementsToEdits(StringRef Code,
                                           const tooling::Replacements &Repls);
 
+/// Get the absolute file path of a given file entry.
+llvm::Optional<std::string> getAbsoluteFilePath(const FileEntry *F,
+                                                const SourceManager &SourceMgr);
 } // namespace clangd
 } // namespace clang
 #endif

Modified: clang-tools-extra/trunk/clangd/XRefs.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/XRefs.cpp (original)
+++ clang-tools-extra/trunk/clangd/XRefs.cpp Thu Jul  5 12:35:01 2018
@@ -175,20 +175,6 @@ IdentifiedSymbol getSymbolAtPosition(Par
   return {DeclMacrosFinder.takeDecls(), DeclMacrosFinder.takeMacroInfos()};
 }
 
-llvm::Optional<std::string>
-getAbsoluteFilePath(const FileEntry *F, const SourceManager &SourceMgr) {
-  SmallString<64> FilePath = F->tryGetRealPathName();
-  if (FilePath.empty())
-    FilePath = F->getName();
-  if (!llvm::sys::path::is_absolute(FilePath)) {
-    if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) {
-      log("Could not turn relative path to absolute: " + FilePath);
-      return llvm::None;
-    }
-  }
-  return FilePath.str().str();
-}
-
 llvm::Optional<Location>
 makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) {
   const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();

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=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test (original)
+++ clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test Thu Jul  5 12:35:01 2018
@@ -22,6 +22,7 @@
 # CHECK-NEXT:        "moreTriggerCharacter": []
 # CHECK-NEXT:      },
 # CHECK-NEXT:      "documentRangeFormattingProvider": true,
+# CHECK-NEXT:      "documentSymbolProvider": true,
 # CHECK-NEXT:      "executeCommandProvider": {
 # CHECK-NEXT:        "commands": [
 # CHECK-NEXT:          "clangd.applyFix"

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=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/initialize-params.test (original)
+++ clang-tools-extra/trunk/test/clangd/initialize-params.test Thu Jul  5 12:35:01 2018
@@ -22,6 +22,7 @@
 # CHECK-NEXT:        "moreTriggerCharacter": []
 # CHECK-NEXT:      },
 # CHECK-NEXT:      "documentRangeFormattingProvider": true,
+# CHECK-NEXT:      "documentSymbolProvider": true,
 # CHECK-NEXT:      "executeCommandProvider": {
 # CHECK-NEXT:        "commands": [
 # CHECK-NEXT:          "clangd.applyFix"

Modified: clang-tools-extra/trunk/test/clangd/symbols.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/symbols.test?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/symbols.test (original)
+++ clang-tools-extra/trunk/test/clangd/symbols.test Thu Jul  5 12:35:01 2018
@@ -28,6 +28,57 @@
 # CHECK-NEXT:    ]
 # CHECK-NEXT:}
 ---
+{"jsonrpc":"2.0","id":2,"method":"textDocument/documentSymbol","params":{"textDocument":{"uri":"test:///main.cpp"}}}
+#      CHECK:  "id": 2,
+# CHECK-NEXT:  "jsonrpc": "2.0",
+# CHECK-NEXT:    "result": [
+# CHECK-NEXT:      {
+# CHECK-NEXT:        "containerName": "",
+# CHECK-NEXT:        "kind": 12,
+# CHECK-NEXT:        "location": {
+# CHECK-NEXT:          "range": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": {{.*}},
+# CHECK-NEXT:              "line": {{.*}}
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": {{.*}},
+# CHECK-NEXT:              "line": {{.*}}
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "uri": "file://{{.*}}/main.cpp"
+# CHECK-NEXT:        },
+# CHECK-NEXT:        "name": "foo"
+# CHECK-NEXT:      }
+# CHECK-NEXT:      {
+# CHECK-NEXT:        "containerName": "",
+# CHECK-NEXT:        "kind": 12,
+# CHECK-NEXT:        "location": {
+# CHECK-NEXT:          "range": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": {{.*}},
+# CHECK-NEXT:              "line": {{.*}}
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": {{.*}},
+# CHECK-NEXT:              "line": {{.*}}
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "uri": "file://{{.*}}/main.cpp"
+# CHECK-NEXT:        },
+# CHECK-NEXT:        "name": "main"
+# CHECK-NEXT:      }
+# CHECK-NEXT:    ]
+# CHECK-NEXT:}
+---
+{"jsonrpc":"2.0","id":3,"method":"textDocument/documentSymbol","params":{"textDocument":{"uri":"test:///foo.cpp"}}}
+#      CHECK:  "error": {
+# CHECK-NEXT:    "code": -32602,
+# CHECK-NEXT:    "message": "trying to get AST for non-added document"
+# CHECK-NEXT:  },
+# CHECK-NEXT:  "id": 3,
+# CHECK-NEXT:  "jsonrpc": "2.0"
+---
 {"jsonrpc":"2.0","id":3,"method":"shutdown"}
 ---
 {"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp Thu Jul  5 12:35:01 2018
@@ -22,6 +22,7 @@ namespace {
 using ::testing::AllOf;
 using ::testing::AnyOf;
 using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
 using ::testing::IsEmpty;
 using ::testing::UnorderedElementsAre;
 
@@ -37,6 +38,7 @@ MATCHER_P(QName, Name, "") {
   return (arg.containerName + "::" + arg.name) == Name;
 }
 MATCHER_P(WithKind, Kind, "") { return arg.kind == Kind; }
+MATCHER_P(SymRange, Range, "") { return arg.location.range == Range; }
 
 ClangdServer::Options optsForTests() {
   auto ServerOpts = ClangdServer::optsForTest();
@@ -287,5 +289,274 @@ TEST_F(WorkspaceSymbolsTest, WithLimit)
   EXPECT_THAT(getSymbols("foo"), ElementsAre(QName("foo")));
 }
 
+namespace {
+class DocumentSymbolsTest : public ::testing::Test {
+public:
+  DocumentSymbolsTest()
+      : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {}
+
+protected:
+  MockFSProvider FSProvider;
+  MockCompilationDatabase CDB;
+  IgnoreDiagnostics DiagConsumer;
+  ClangdServer Server;
+
+  std::vector<SymbolInformation> getSymbols(PathRef File) {
+    EXPECT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for preamble";
+    auto SymbolInfos = runDocumentSymbols(Server, File);
+    EXPECT_TRUE(bool(SymbolInfos)) << "documentSymbols returned an error";
+    return *SymbolInfos;
+  }
+
+  void addFile(StringRef FilePath, StringRef Contents) {
+    FSProvider.Files[FilePath] = Contents;
+    Server.addDocument(FilePath, Contents);
+  }
+};
+} // namespace
+
+TEST_F(DocumentSymbolsTest, BasicSymbols) {
+  std::string FilePath = testPath("foo.cpp");
+  Annotations Main(R"(
+      class Foo;
+      class Foo {
+        Foo() {}
+        Foo(int a) {}
+        void $decl[[f]]();
+        friend void f1();
+        friend class Friend;
+        Foo& operator=(const Foo&);
+        ~Foo();
+        class Nested {
+        void f();
+        };
+      };
+      class Friend {
+      };
+
+      void f1();
+      inline void f2() {}
+      static const int KInt = 2;
+      const char* kStr = "123";
+
+      void f1() {}
+
+      namespace foo {
+      // Type alias
+      typedef int int32;
+      using int32_t = int32;
+
+      // Variable
+      int v1;
+
+      // Namespace
+      namespace bar {
+      int v2;
+      }
+      // Namespace alias
+      namespace baz = bar;
+
+      // FIXME: using declaration is not supported as the IndexAction will ignore
+      // implicit declarations (the implicit using shadow declaration) by default,
+      // and there is no way to customize this behavior at the moment.
+      using bar::v2;
+      } // namespace foo
+    )");
+
+  addFile(FilePath, Main.code());
+  EXPECT_THAT(getSymbols(FilePath),
+              ElementsAreArray(
+                  {AllOf(QName("Foo"), WithKind(SymbolKind::Class)),
+                   AllOf(QName("Foo"), WithKind(SymbolKind::Class)),
+                   AllOf(QName("Foo::Foo"), WithKind(SymbolKind::Method)),
+                   AllOf(QName("Foo::Foo"), WithKind(SymbolKind::Method)),
+                   AllOf(QName("Foo::f"), WithKind(SymbolKind::Method)),
+                   AllOf(QName("f1"), WithKind(SymbolKind::Function)),
+                   AllOf(QName("Foo::operator="), WithKind(SymbolKind::Method)),
+                   AllOf(QName("Foo::~Foo"), WithKind(SymbolKind::Method)),
+                   AllOf(QName("Foo::Nested"), WithKind(SymbolKind::Class)),
+                   AllOf(QName("Foo::Nested::f"), WithKind(SymbolKind::Method)),
+                   AllOf(QName("Friend"), WithKind(SymbolKind::Class)),
+                   AllOf(QName("f1"), WithKind(SymbolKind::Function)),
+                   AllOf(QName("f2"), WithKind(SymbolKind::Function)),
+                   AllOf(QName("KInt"), WithKind(SymbolKind::Variable)),
+                   AllOf(QName("kStr"), WithKind(SymbolKind::Variable)),
+                   AllOf(QName("f1"), WithKind(SymbolKind::Function)),
+                   AllOf(QName("foo"), WithKind(SymbolKind::Namespace)),
+                   AllOf(QName("foo::int32"), WithKind(SymbolKind::Class)),
+                   AllOf(QName("foo::int32_t"), WithKind(SymbolKind::Class)),
+                   AllOf(QName("foo::v1"), WithKind(SymbolKind::Variable)),
+                   AllOf(QName("foo::bar"), WithKind(SymbolKind::Namespace)),
+                   AllOf(QName("foo::bar::v2"), WithKind(SymbolKind::Variable)),
+                   AllOf(QName("foo::baz"), WithKind(SymbolKind::Namespace))}));
+}
+
+TEST_F(DocumentSymbolsTest, DeclarationDefinition) {
+  std::string FilePath = testPath("foo.cpp");
+  Annotations Main(R"(
+      class Foo {
+        void $decl[[f]]();
+      };
+      void Foo::$def[[f]]() {
+      }
+    )");
+
+  addFile(FilePath, Main.code());
+  EXPECT_THAT(getSymbols(FilePath),
+              ElementsAre(AllOf(QName("Foo"), WithKind(SymbolKind::Class)),
+                          AllOf(QName("Foo::f"), WithKind(SymbolKind::Method),
+                                SymRange(Main.range("decl"))),
+                          AllOf(QName("Foo::f"), WithKind(SymbolKind::Method),
+                                SymRange(Main.range("def")))));
+}
+
+TEST_F(DocumentSymbolsTest, ExternSymbol) {
+  std::string FilePath = testPath("foo.cpp");
+  addFile(testPath("foo.h"), R"cpp(
+      extern int var = 2;
+      )cpp");
+  addFile(FilePath, R"cpp(
+      #include "foo.h"
+      )cpp");
+
+  EXPECT_THAT(getSymbols(FilePath), IsEmpty());
+}
+
+TEST_F(DocumentSymbolsTest, NoLocals) {
+  std::string FilePath = testPath("foo.cpp");
+  addFile(FilePath,
+          R"cpp(
+      void test(int FirstParam, int SecondParam) {
+        struct LocalClass {};
+        int local_var;
+      })cpp");
+  EXPECT_THAT(getSymbols(FilePath), ElementsAre(QName("test")));
+}
+
+TEST_F(DocumentSymbolsTest, Unnamed) {
+  std::string FilePath = testPath("foo.h");
+  addFile(FilePath,
+          R"cpp(
+      struct {
+        int InUnnamed;
+      } UnnamedStruct;
+      )cpp");
+  EXPECT_THAT(
+      getSymbols(FilePath),
+      ElementsAre(AllOf(QName("UnnamedStruct"), WithKind(SymbolKind::Variable)),
+                  AllOf(QName("(anonymous struct)::InUnnamed"),
+                        WithKind(SymbolKind::Field))));
+}
+
+TEST_F(DocumentSymbolsTest, InHeaderFile) {
+  addFile("bar.h", R"cpp(
+      int foo() {
+      }
+      )cpp");
+  std::string FilePath = testPath("foo.h");
+  addFile(FilePath, R"cpp(
+      #include "bar.h"
+      int test() {
+      }
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols(FilePath), ElementsAre(QName("test")));
+}
+
+TEST_F(DocumentSymbolsTest, Template) {
+  std::string FilePath = testPath("foo.cpp");
+  addFile(FilePath, R"(
+    // Primary templates and specializations are included but instantiations
+    // are not.
+    template <class T> struct Tmpl {T x = 0;};
+    template <> struct Tmpl<int> {};
+    extern template struct Tmpl<float>;
+    template struct Tmpl<double>;
+  )");
+  EXPECT_THAT(getSymbols(FilePath),
+              ElementsAre(AllOf(QName("Tmpl"), WithKind(SymbolKind::Struct)),
+                          AllOf(QName("Tmpl::x"), WithKind(SymbolKind::Field)),
+                          AllOf(QName("Tmpl"), WithKind(SymbolKind::Struct))));
+}
+
+TEST_F(DocumentSymbolsTest, Namespaces) {
+  std::string FilePath = testPath("foo.cpp");
+  addFile(FilePath, R"cpp(
+      namespace ans1 {
+        int ai1;
+      namespace ans2 {
+        int ai2;
+      }
+      }
+      namespace {
+      void test() {}
+      }
+
+      namespace na {
+      inline namespace nb {
+      class Foo {};
+      }
+      }
+      namespace na {
+      // This is still inlined.
+      namespace nb {
+      class Bar {};
+      }
+      }
+      )cpp");
+  EXPECT_THAT(
+      getSymbols(FilePath),
+      ElementsAreArray({QName("ans1"), QName("ans1::ai1"), QName("ans1::ans2"),
+                        QName("ans1::ans2::ai2"), QName("test"), QName("na"),
+                        QName("na::nb"), QName("na::Foo"), QName("na"),
+                        QName("na::nb"), QName("na::Bar")}));
+}
+
+TEST_F(DocumentSymbolsTest, Enums) {
+  std::string FilePath = testPath("foo.cpp");
+  addFile(FilePath, R"(
+      enum {
+        Red
+      };
+      enum Color {
+        Green
+      };
+      enum class Color2 {
+        Yellow
+      };
+      namespace ns {
+      enum {
+        Black
+      };
+      }
+    )");
+  EXPECT_THAT(getSymbols(FilePath),
+              ElementsAre(QName("Red"), QName("Color"), QName("Green"),
+                          QName("Color2"), QName("Color2::Yellow"), QName("ns"),
+                          QName("ns::Black")));
+}
+
+TEST_F(DocumentSymbolsTest, FromMacro) {
+  std::string FilePath = testPath("foo.cpp");
+  Annotations Main(R"(
+    #define FF(name) \
+      class name##_Test {};
+
+    $expansion[[FF]](abc);
+
+    #define FF2() \
+      class $spelling[[Test]] {};
+
+    FF2();
+  )");
+  addFile(FilePath, Main.code());
+  EXPECT_THAT(
+      getSymbols(FilePath),
+      ElementsAre(AllOf(QName("abc_Test"), SymRange(Main.range("expansion"))),
+                  AllOf(QName("Test"), SymRange(Main.range("spelling")))));
+}
+
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/unittests/clangd/SyncAPI.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/SyncAPI.cpp?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/SyncAPI.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/SyncAPI.cpp Thu Jul  5 12:35:01 2018
@@ -117,5 +117,12 @@ runWorkspaceSymbols(ClangdServer &Server
   return std::move(*Result);
 }
 
+llvm::Expected<std::vector<SymbolInformation>>
+runDocumentSymbols(ClangdServer &Server, PathRef File) {
+  llvm::Optional<llvm::Expected<std::vector<SymbolInformation>>> Result;
+  Server.documentSymbols(File, capture(Result));
+  return std::move(*Result);
+}
+
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/unittests/clangd/SyncAPI.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/SyncAPI.h?rev=336386&r1=336385&r2=336386&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/SyncAPI.h (original)
+++ clang-tools-extra/trunk/unittests/clangd/SyncAPI.h Thu Jul  5 12:35:01 2018
@@ -43,6 +43,9 @@ std::string runDumpAST(ClangdServer &Ser
 llvm::Expected<std::vector<SymbolInformation>>
 runWorkspaceSymbols(ClangdServer &Server, StringRef Query, int Limit);
 
+llvm::Expected<std::vector<SymbolInformation>>
+runDocumentSymbols(ClangdServer &Server, PathRef File);
+
 } // namespace clangd
 } // namespace clang
 




More information about the cfe-commits mailing list