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.lape...@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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits