Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -46,7 +46,7 @@ } // namespace void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) { - C.reply(json::obj{ + reply(C, json::obj{ {{"capabilities", json::obj{ {"textDocumentSync", 1}, @@ -84,7 +84,7 @@ void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) { // Do essentially nothing, just say we're ready to exit. ShutdownRequestReceived = true; - C.reply(nullptr); + reply(C, nullptr); } void ClangdLSPServer::onExit(Ctx C, ExitParams &Params) { IsDone = true; } @@ -94,16 +94,17 @@ if (Params.metadata && !Params.metadata->extraFlags.empty()) CDB.setExtraFlagsForFile(Params.textDocument.uri.file, std::move(Params.metadata->extraFlags)); - Server.addDocument(Params.textDocument.uri.file, Params.textDocument.text); + Server.addDocument(std::move(C), Params.textDocument.uri.file, + Params.textDocument.text); } void ClangdLSPServer::onDocumentDidChange(Ctx C, DidChangeTextDocumentParams &Params) { if (Params.contentChanges.size() != 1) - return C.replyError(ErrorCode::InvalidParams, - "can only apply one change at a time"); + return replyError(C, ErrorCode::InvalidParams, + "can only apply one change at a time"); // We only support full syncing right now. - Server.addDocument(Params.textDocument.uri.file, + Server.addDocument(std::move(C), Params.textDocument.uri.file, Params.contentChanges[0].text); } @@ -125,39 +126,39 @@ ApplyWorkspaceEditParams ApplyEdit; ApplyEdit.edit = *Params.workspaceEdit; - C.reply("Fix applied."); + reply(C, "Fix applied."); // We don't need the response so id == 1 is OK. // Ideally, we would wait for the response and if there is no error, we // would reply success/failure to the original RPC. - C.call("workspace/applyEdit", ApplyEdit); + call(C, "workspace/applyEdit", ApplyEdit); } else { // We should not get here because ExecuteCommandParams would not have // parsed in the first place and this handler should not be called. But if // more commands are added, this will be here has a safe guard. - C.replyError( - ErrorCode::InvalidParams, + replyError( + C, ErrorCode::InvalidParams, llvm::formatv("Unsupported command \"{0}\".", Params.command).str()); } } void ClangdLSPServer::onRename(Ctx C, RenameParams &Params) { auto File = Params.textDocument.uri.file; - auto Replacements = Server.rename(File, Params.position, Params.newName); + auto Replacements = Server.rename(C, File, Params.position, Params.newName); if (!Replacements) { - C.replyError(ErrorCode::InternalError, - llvm::toString(Replacements.takeError())); + replyError(C, ErrorCode::InternalError, + llvm::toString(Replacements.takeError())); return; } std::string Code = Server.getDocument(File); std::vector Edits = replacementsToEdits(Code, *Replacements); WorkspaceEdit WE; WE.changes = {{Params.textDocument.uri.uri, Edits}}; - C.reply(WE); + reply(C, WE); } void ClangdLSPServer::onDocumentDidClose(Ctx C, DidCloseTextDocumentParams &Params) { - Server.removeDocument(Params.textDocument.uri.file); + Server.removeDocument(std::move(C), Params.textDocument.uri.file); } void ClangdLSPServer::onDocumentOnTypeFormatting( @@ -166,10 +167,10 @@ std::string Code = Server.getDocument(File); auto ReplacementsOrError = Server.formatOnType(Code, File, Params.position); if (ReplacementsOrError) - C.reply(json::ary(replacementsToEdits(Code, ReplacementsOrError.get()))); + reply(C, json::ary(replacementsToEdits(Code, ReplacementsOrError.get()))); else - C.replyError(ErrorCode::UnknownErrorCode, - llvm::toString(ReplacementsOrError.takeError())); + replyError(C, ErrorCode::UnknownErrorCode, + llvm::toString(ReplacementsOrError.takeError())); } void ClangdLSPServer::onDocumentRangeFormatting( @@ -178,10 +179,10 @@ std::string Code = Server.getDocument(File); auto ReplacementsOrError = Server.formatRange(Code, File, Params.range); if (ReplacementsOrError) - C.reply(json::ary(replacementsToEdits(Code, ReplacementsOrError.get()))); + reply(C, json::ary(replacementsToEdits(Code, ReplacementsOrError.get()))); else - C.replyError(ErrorCode::UnknownErrorCode, - llvm::toString(ReplacementsOrError.takeError())); + replyError(C, ErrorCode::UnknownErrorCode, + llvm::toString(ReplacementsOrError.takeError())); } void ClangdLSPServer::onDocumentFormatting(Ctx C, @@ -190,10 +191,10 @@ std::string Code = Server.getDocument(File); auto ReplacementsOrError = Server.formatFile(Code, File); if (ReplacementsOrError) - C.reply(json::ary(replacementsToEdits(Code, ReplacementsOrError.get()))); + reply(C, json::ary(replacementsToEdits(Code, ReplacementsOrError.get()))); else - C.replyError(ErrorCode::UnknownErrorCode, - llvm::toString(ReplacementsOrError.takeError())); + replyError(C, ErrorCode::UnknownErrorCode, + llvm::toString(ReplacementsOrError.takeError())); } void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) { @@ -213,65 +214,68 @@ }); } } - C.reply(std::move(Commands)); + reply(C, std::move(Commands)); } void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) { - auto List = - Server - .codeComplete( - Params.textDocument.uri.file, - Position{Params.position.line, Params.position.character}, CCOpts) - .get() // FIXME(ibiryukov): This could be made async if we - // had an API that would allow to attach callbacks to - // futures returned by ClangdServer. - .Value; - C.reply(List); + auto Reply = Server + .codeComplete(std::move(C), Params.textDocument.uri.file, + Position{Params.position.line, + Params.position.character}, + CCOpts) + .get(); // FIXME(ibiryukov): This could be made async if we + // had an API that would allow to attach callbacks to + // futures returned by ClangdServer. + + // We have std::move'd from C, now restore it from response of codeComplete. + C = std::move(Reply.first); + auto List = std::move(Reply.second.Value); + reply(C, List); } void ClangdLSPServer::onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) { auto SignatureHelp = Server.signatureHelp( - Params.textDocument.uri.file, + C, Params.textDocument.uri.file, Position{Params.position.line, Params.position.character}); if (!SignatureHelp) - return C.replyError(ErrorCode::InvalidParams, - llvm::toString(SignatureHelp.takeError())); - C.reply(SignatureHelp->Value); + return replyError(C, ErrorCode::InvalidParams, + llvm::toString(SignatureHelp.takeError())); + reply(C, SignatureHelp->Value); } void ClangdLSPServer::onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) { auto Items = Server.findDefinitions( - Params.textDocument.uri.file, + C, Params.textDocument.uri.file, Position{Params.position.line, Params.position.character}); if (!Items) - return C.replyError(ErrorCode::InvalidParams, - llvm::toString(Items.takeError())); - C.reply(json::ary(Items->Value)); + return replyError(C, ErrorCode::InvalidParams, + llvm::toString(Items.takeError())); + reply(C, json::ary(Items->Value)); } void ClangdLSPServer::onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) { llvm::Optional Result = Server.switchSourceHeader(Params.uri.file); std::string ResultUri; - C.reply(Result ? URI::fromFile(*Result).uri : ""); + reply(C, Result ? URI::fromFile(*Result).uri : ""); } void ClangdLSPServer::onDocumentHighlight(Ctx C, TextDocumentPositionParams &Params) { auto Highlights = Server.findDocumentHighlights( - Params.textDocument.uri.file, + C, Params.textDocument.uri.file, Position{Params.position.line, Params.position.character}); if (!Highlights) { - C.replyError(ErrorCode::InternalError, - llvm::toString(Highlights.takeError())); + replyError(C, ErrorCode::InternalError, + llvm::toString(Highlights.takeError())); return; } - C.reply(json::ary(Highlights->Value)); + reply(C, json::ary(Highlights->Value)); } ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount, @@ -279,19 +283,17 @@ const clangd::CodeCompleteOptions &CCOpts, llvm::Optional ResourceDir, llvm::Optional CompileCommandsDir) - : Out(Out), CDB(/*Logger=*/Out, std::move(CompileCommandsDir)), - CCOpts(CCOpts), Server(CDB, /*DiagConsumer=*/*this, FSProvider, - AsyncThreadsCount, StorePreamblesInMemory, - /*Logger=*/Out, ResourceDir) {} + : Out(Out), CDB(std::move(CompileCommandsDir)), CCOpts(CCOpts), + Server(CDB, /*DiagConsumer=*/*this, FSProvider, AsyncThreadsCount, + StorePreamblesInMemory, ResourceDir) {} bool ClangdLSPServer::run(std::istream &In) { assert(!IsDone && "Run was called before"); // Set up JSONRPCDispatcher. - JSONRPCDispatcher Dispatcher( - [](RequestContext Ctx, const json::Expr &Params) { - Ctx.replyError(ErrorCode::MethodNotFound, "method not found"); - }); + JSONRPCDispatcher Dispatcher([](Context Ctx, const json::Expr &Params) { + replyError(Ctx, ErrorCode::MethodNotFound, "method not found"); + }); registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this); // Run the Language Server loop. Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -37,8 +37,6 @@ namespace clangd { -class Logger; - /// Turn a [line, column] pair into an offset in Code. size_t positionToOffset(StringRef Code, Position P); @@ -205,12 +203,10 @@ /// /// \p StorePreamblesInMemory defines whether the Preambles generated by /// clangd are stored in-memory or on disk. - /// - /// Various messages are logged using \p Logger. ClangdServer(GlobalCompilationDatabase &CDB, DiagnosticsConsumer &DiagConsumer, FileSystemProvider &FSProvider, unsigned AsyncThreadsCount, - bool StorePreamblesInMemory, clangd::Logger &Logger, + bool StorePreamblesInMemory, llvm::Optional ResourceDir = llvm::None); /// Set the root path of the workspace. @@ -222,17 +218,18 @@ /// constructor will receive onDiagnosticsReady callback. /// \return A future that will become ready when the rebuild (including /// diagnostics) is finished. - std::future addDocument(PathRef File, StringRef Contents); + std::future addDocument(Context Ctx, PathRef File, + StringRef Contents); /// Remove \p File from list of tracked files, schedule a request to free /// resources associated with it. /// \return A future that will become ready when the file is removed and all /// associated resources are freed. - std::future removeDocument(PathRef File); + std::future removeDocument(Context Ctx, PathRef File); /// Force \p File to be reparsed using the latest contents. /// Will also check if CompileCommand, provided by GlobalCompilationDatabase /// for \p File has changed. If it has, will remove currently stored Preamble /// and AST and rebuild them from scratch. - std::future forceReparse(PathRef File); + std::future forceReparse(Context Ctx, PathRef File); /// DEPRECATED. Please use a callback-based version, this API is deprecated /// and will soon be removed. @@ -251,19 +248,20 @@ /// This method should only be called for currently tracked files. However, it /// is safe to call removeDocument for \p File after this method returns, even /// while returned future is not yet ready. - std::future> - codeComplete(PathRef File, Position Pos, + std::future>> + codeComplete(Context Ctx, PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, llvm::Optional OverridenContents = llvm::None, IntrusiveRefCntPtr *UsedFS = nullptr); /// A version of `codeComplete` that runs \p Callback on the processing thread /// when codeComplete results become available. - void codeComplete(UniqueFunction)> Callback, - PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, - llvm::Optional OverridenContents = llvm::None, - IntrusiveRefCntPtr *UsedFS = nullptr); + void + codeComplete(Context Ctx, PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + UniqueFunction)> Callback, + llvm::Optional OverridenContents = llvm::None, + IntrusiveRefCntPtr *UsedFS = nullptr); /// Provide signature help for \p File at \p Pos. If \p OverridenContents is /// not None, they will used only for signature help, i.e. no diagnostics @@ -273,13 +271,13 @@ /// vfs::FileSystem used for signature help. This method should only be called /// for currently tracked files. llvm::Expected> - signatureHelp(PathRef File, Position Pos, + signatureHelp(const Context &Ctx, PathRef File, Position Pos, llvm::Optional OverridenContents = llvm::None, IntrusiveRefCntPtr *UsedFS = nullptr); /// Get definition of symbol at a specified \p Line and \p Column in \p File. - llvm::Expected>> findDefinitions(PathRef File, - Position Pos); + llvm::Expected>> + findDefinitions(const Context &Ctx, PathRef File, Position Pos); /// Helper function that returns a path to the corresponding source file when /// given a header file and vice versa. @@ -287,7 +285,7 @@ /// Get document highlights for a given position. llvm::Expected>> - findDocumentHighlights(PathRef File, Position Pos); + findDocumentHighlights(const Context &Ctx, PathRef File, Position Pos); /// Run formatting for \p Rng inside \p File with content \p Code. llvm::Expected formatRange(StringRef Code, @@ -304,7 +302,8 @@ /// Rename all occurrences of the symbol at the \p Pos in \p File to /// \p NewName. - Expected> rename(PathRef File, Position Pos, + Expected> rename(const Context &Ctx, + PathRef File, Position Pos, llvm::StringRef NewName); /// Gets current document contents for \p File. \p File must point to a @@ -321,21 +320,20 @@ void onFileEvent(const DidChangeWatchedFilesParams &Params); private: - /// FIXME: This stats several files to find a .clang-format file. I/O can be /// slow. Think of a way to cache this. llvm::Expected formatCode(llvm::StringRef Code, PathRef File, ArrayRef Ranges); - std::future - scheduleReparseAndDiags(PathRef File, VersionedDraft Contents, + std::future + scheduleReparseAndDiags(Context Ctx, PathRef File, VersionedDraft Contents, std::shared_ptr Resources, Tagged> TaggedFS); - std::future scheduleCancelRebuild(std::shared_ptr Resources); + std::future + scheduleCancelRebuild(Context Ctx, std::shared_ptr Resources); - clangd::Logger &Logger; GlobalCompilationDatabase &CDB; DiagnosticsConsumer &DiagConsumer; FileSystemProvider &FSProvider; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -28,16 +28,6 @@ namespace { -class FulfillPromiseGuard { -public: - FulfillPromiseGuard(std::promise &Promise) : Promise(Promise) {} - - ~FulfillPromiseGuard() { Promise.set_value(); } - -private: - std::promise &Promise; -}; - std::string getStandardResourceDir() { static int Dummy; // Just an address in this process. return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy); @@ -163,10 +153,9 @@ DiagnosticsConsumer &DiagConsumer, FileSystemProvider &FSProvider, unsigned AsyncThreadsCount, - bool StorePreamblesInMemory, clangd::Logger &Logger, + bool StorePreamblesInMemory, llvm::Optional ResourceDir) - : Logger(Logger), CDB(CDB), DiagConsumer(DiagConsumer), - FSProvider(FSProvider), + : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), ResourceDir(ResourceDir ? ResourceDir->str() : getStandardResourceDir()), PCHs(std::make_shared()), StorePreamblesInMemory(StorePreamblesInMemory), @@ -179,65 +168,69 @@ this->RootPath = NewRootPath; } -std::future ClangdServer::addDocument(PathRef File, StringRef Contents) { +std::future ClangdServer::addDocument(Context Ctx, PathRef File, + StringRef Contents) { DocVersion Version = DraftMgr.updateDraft(File, Contents); auto TaggedFS = FSProvider.getTaggedFileSystem(File); std::shared_ptr Resources = Units.getOrCreateFile( - File, ResourceDir, CDB, StorePreamblesInMemory, PCHs, Logger); - return scheduleReparseAndDiags(File, VersionedDraft{Version, Contents.str()}, + File, ResourceDir, CDB, StorePreamblesInMemory, PCHs); + return scheduleReparseAndDiags(std::move(Ctx), File, + VersionedDraft{Version, Contents.str()}, std::move(Resources), std::move(TaggedFS)); } -std::future ClangdServer::removeDocument(PathRef File) { +std::future ClangdServer::removeDocument(Context Ctx, PathRef File) { DraftMgr.removeDraft(File); std::shared_ptr Resources = Units.removeIfPresent(File); - return scheduleCancelRebuild(std::move(Resources)); + return scheduleCancelRebuild(std::move(Ctx), std::move(Resources)); } -std::future ClangdServer::forceReparse(PathRef File) { +std::future ClangdServer::forceReparse(Context Ctx, PathRef File) { auto FileContents = DraftMgr.getDraft(File); assert(FileContents.Draft && "forceReparse() was called for non-added document"); auto TaggedFS = FSProvider.getTaggedFileSystem(File); auto Recreated = Units.recreateFileIfCompileCommandChanged( - File, ResourceDir, CDB, StorePreamblesInMemory, PCHs, Logger); + File, ResourceDir, CDB, StorePreamblesInMemory, PCHs); // Note that std::future from this cleanup action is ignored. - scheduleCancelRebuild(std::move(Recreated.RemovedFile)); + scheduleCancelRebuild(Ctx.clone(), std::move(Recreated.RemovedFile)); // Schedule a reparse. - return scheduleReparseAndDiags(File, std::move(FileContents), + return scheduleReparseAndDiags(std::move(Ctx), File, std::move(FileContents), std::move(Recreated.FileInCollection), std::move(TaggedFS)); } -std::future> -ClangdServer::codeComplete(PathRef File, Position Pos, +std::future>> +ClangdServer::codeComplete(Context Ctx, PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { - using ResultType = Tagged; + using ResultType = std::pair>; std::promise ResultPromise; - auto Callback = [](std::promise ResultPromise, - ResultType Result) -> void { - ResultPromise.set_value(std::move(Result)); + auto Callback = [](std::promise ResultPromise, Context Ctx, + Tagged Result) -> void { + ResultPromise.set_value({std::move(Ctx), std::move(Result)}); }; std::future ResultFuture = ResultPromise.get_future(); - codeComplete(BindWithForward(Callback, std::move(ResultPromise)), File, Pos, - Opts, OverridenContents, UsedFS); + codeComplete(std::move(Ctx), File, Pos, Opts, + BindWithForward(Callback, std::move(ResultPromise)), + OverridenContents, UsedFS); return ResultFuture; } void ClangdServer::codeComplete( - UniqueFunction)> Callback, PathRef File, - Position Pos, const clangd::CodeCompleteOptions &Opts, + Context Ctx, PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + UniqueFunction)> Callback, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { - using CallbackType = UniqueFunction)>; + using CallbackType = UniqueFunction)>; std::string Contents; if (OverridenContents) { @@ -268,7 +261,7 @@ // A task that will be run asynchronously. auto Task = // 'mutable' to reassign Preamble variable. - [=](CallbackType Callback) mutable { + [=](Context Ctx, CallbackType Callback) mutable { if (!Preamble) { // Maybe we built some preamble before processing this request. Preamble = Resources->getPossiblyStalePreamble(); @@ -277,18 +270,20 @@ // both the old and the new version in case only one of them matches. CompletionList Result = clangd::codeComplete( - File, Resources->getCompileCommand(), + Ctx, File, Resources->getCompileCommand(), Preamble ? &Preamble->Preamble : nullptr, Contents, Pos, - TaggedFS.Value, PCHs, CodeCompleteOpts, Logger); + TaggedFS.Value, PCHs, CodeCompleteOpts); - Callback(make_tagged(std::move(Result), std::move(TaggedFS.Tag))); + Callback(std::move(Ctx), + make_tagged(std::move(Result), std::move(TaggedFS.Tag))); }; - WorkScheduler.addToFront(std::move(Task), std::move(Callback)); + WorkScheduler.addToFront(std::move(Task), std::move(Ctx), + std::move(Callback)); } llvm::Expected> -ClangdServer::signatureHelp(PathRef File, Position Pos, +ClangdServer::signatureHelp(const Context &Ctx, PathRef File, Position Pos, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { std::string DraftStorage; @@ -314,10 +309,10 @@ llvm::errc::invalid_argument); auto Preamble = Resources->getPossiblyStalePreamble(); - auto Result = clangd::signatureHelp(File, Resources->getCompileCommand(), - Preamble ? &Preamble->Preamble : nullptr, - *OverridenContents, Pos, TaggedFS.Value, - PCHs, Logger); + auto Result = + clangd::signatureHelp(Ctx, File, Resources->getCompileCommand(), + Preamble ? &Preamble->Preamble : nullptr, + *OverridenContents, Pos, TaggedFS.Value, PCHs); return make_tagged(std::move(Result), TaggedFS.Tag); } @@ -348,7 +343,8 @@ } Expected> -ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName) { +ClangdServer::rename(const Context &Ctx, PathRef File, Position Pos, + llvm::StringRef NewName) { std::string Code = getDocument(File); std::shared_ptr Resources = Units.getFile(File); RefactoringResultCollector ResultCollector; @@ -419,7 +415,7 @@ } llvm::Expected>> -ClangdServer::findDefinitions(PathRef File, Position Pos) { +ClangdServer::findDefinitions(const Context &Ctx, PathRef File, Position Pos) { auto TaggedFS = FSProvider.getTaggedFileSystem(File); std::shared_ptr Resources = Units.getFile(File); @@ -429,10 +425,10 @@ llvm::errc::invalid_argument); std::vector Result; - Resources->getAST().get()->runUnderLock([Pos, &Result, this](ParsedAST *AST) { + Resources->getAST().get()->runUnderLock([Pos, &Result, &Ctx](ParsedAST *AST) { if (!AST) return; - Result = clangd::findDefinitions(*AST, Pos, Logger); + Result = clangd::findDefinitions(Ctx, *AST, Pos); }); return make_tagged(std::move(Result), TaggedFS.Tag); } @@ -511,7 +507,8 @@ } llvm::Expected>> -ClangdServer::findDocumentHighlights(PathRef File, Position Pos) { +ClangdServer::findDocumentHighlights(const Context &Ctx, PathRef File, + Position Pos) { auto FileContents = DraftMgr.getDraft(File); if (!FileContents.Draft) return llvm::make_error( @@ -528,14 +525,14 @@ std::vector Result; llvm::Optional Err; - Resources->getAST().get()->runUnderLock([Pos, &Result, &Err, - this](ParsedAST *AST) { + Resources->getAST().get()->runUnderLock([Pos, &Ctx, &Err, + &Result](ParsedAST *AST) { if (!AST) { Err = llvm::make_error("Invalid AST", llvm::errc::invalid_argument); return; } - Result = clangd::findDocumentHighlights(*AST, Pos, Logger); + Result = clangd::findDocumentHighlights(Ctx, *AST, Pos); }); if (Err) @@ -543,32 +540,34 @@ return make_tagged(Result, TaggedFS.Tag); } -std::future ClangdServer::scheduleReparseAndDiags( - PathRef File, VersionedDraft Contents, std::shared_ptr Resources, +std::future ClangdServer::scheduleReparseAndDiags( + Context Ctx, PathRef File, VersionedDraft Contents, + std::shared_ptr Resources, Tagged> TaggedFS) { assert(Contents.Draft && "Draft must have contents"); - UniqueFunction>()> + UniqueFunction>(const Context &)> DeferredRebuild = Resources->deferRebuild(*Contents.Draft, TaggedFS.Value); - std::promise DonePromise; - std::future DoneFuture = DonePromise.get_future(); + std::promise DonePromise; + std::future DoneFuture = DonePromise.get_future(); DocVersion Version = Contents.Version; Path FileStr = File; VFSTag Tag = TaggedFS.Tag; auto ReparseAndPublishDiags = [this, FileStr, Version, - Tag](UniqueFunction>()> + Tag](UniqueFunction>( + const Context &)> DeferredRebuild, - std::promise DonePromise) -> void { - FulfillPromiseGuard Guard(DonePromise); + std::promise DonePromise, Context Ctx) -> void { + auto Guard = onScopeExit([&]() { DonePromise.set_value(std::move(Ctx)); }); auto CurrentVersion = DraftMgr.getVersion(FileStr); if (CurrentVersion != Version) return; // This request is outdated - auto Diags = DeferredRebuild(); + auto Diags = DeferredRebuild(Ctx); if (!Diags) return; // A new reparse was requested before this one completed. @@ -589,28 +588,31 @@ }; WorkScheduler.addToFront(std::move(ReparseAndPublishDiags), - std::move(DeferredRebuild), std::move(DonePromise)); + std::move(DeferredRebuild), std::move(DonePromise), + std::move(Ctx)); return DoneFuture; } -std::future -ClangdServer::scheduleCancelRebuild(std::shared_ptr Resources) { - std::promise DonePromise; - std::future DoneFuture = DonePromise.get_future(); +std::future +ClangdServer::scheduleCancelRebuild(Context Ctx, + std::shared_ptr Resources) { + std::promise DonePromise; + std::future DoneFuture = DonePromise.get_future(); if (!Resources) { // No need to schedule any cleanup. - DonePromise.set_value(); + DonePromise.set_value(std::move(Ctx)); return DoneFuture; } UniqueFunction DeferredCancel = Resources->deferCancelRebuild(); - auto CancelReparses = [Resources](std::promise DonePromise, - UniqueFunction DeferredCancel) { - FulfillPromiseGuard Guard(DonePromise); + auto CancelReparses = [Resources](std::promise DonePromise, + UniqueFunction DeferredCancel, + Context Ctx) { DeferredCancel(); + DonePromise.set_value(std::move(Ctx)); }; WorkScheduler.addToFront(std::move(CancelReparses), std::move(DonePromise), - std::move(DeferredCancel)); + std::move(DeferredCancel), std::move(Ctx)); return DoneFuture; } Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H +#include "Context.h" #include "Function.h" #include "Path.h" #include "Protocol.h" @@ -40,8 +41,6 @@ namespace clangd { -class Logger; - /// A diagnostic with its FixIts. struct DiagWithFixIts { clangd::Diagnostic Diag; @@ -65,11 +64,11 @@ /// Attempts to run Clang and store parsed AST. If \p Preamble is non-null /// it is reused during parsing. static llvm::Optional - Build(std::unique_ptr CI, + Build(const Context &Ctx, std::unique_ptr CI, std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS, clangd::Logger &Logger); + IntrusiveRefCntPtr VFS); ParsedAST(ParsedAST &&Other); ParsedAST &operator=(ParsedAST &&Other); @@ -146,12 +145,12 @@ static std::shared_ptr Create(PathRef FileName, tooling::CompileCommand Command, bool StorePreamblesInMemory, - std::shared_ptr PCHs, clangd::Logger &Logger); + std::shared_ptr PCHs); private: CppFile(PathRef FileName, tooling::CompileCommand Command, bool StorePreamblesInMemory, - std::shared_ptr PCHs, clangd::Logger &Logger); + std::shared_ptr PCHs); public: CppFile(CppFile const &) = delete; @@ -173,7 +172,8 @@ /// requested in parallel (effectively cancelling this rebuild) before /// diagnostics were produced. llvm::Optional> - rebuild(StringRef NewContents, IntrusiveRefCntPtr VFS); + rebuild(const Context &Ctx, StringRef NewContents, + IntrusiveRefCntPtr VFS); /// Schedule a rebuild and return a deferred computation that will finish the /// rebuild, that can be called on a different thread. @@ -189,7 +189,7 @@ /// The future to finish rebuild returns a list of diagnostics built during /// reparse, or None, if another deferRebuild was called before this /// rebuild was finished. - UniqueFunction>()> + UniqueFunction>(const Context &)> deferRebuild(StringRef NewContents, IntrusiveRefCntPtr VFS); /// Returns a future to get the most fresh PreambleData for a file. The @@ -252,8 +252,6 @@ std::shared_ptr LatestAvailablePreamble; /// Utility class, required by clang. std::shared_ptr PCHs; - /// Used for logging various messages. - clangd::Logger &Logger; }; /// Get the beginning SourceLocation at a specified \p Pos. @@ -261,11 +259,11 @@ const FileEntry *FE); /// Get definition of symbol at a specified \p Pos. -std::vector findDefinitions(ParsedAST &AST, Position Pos, - clangd::Logger &Logger); +std::vector findDefinitions(const Context &Ctx, ParsedAST &AST, + Position Pos); std::vector -findDocumentHighlights(ParsedAST &AST, Position Pos, clangd::Logger &Logger); +findDocumentHighlights(const Context &Ctx, ParsedAST &AST, Position Pos); /// For testing/debugging purposes. Note that this method deserializes all /// unserialized Decls, so use with care. Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -227,12 +227,12 @@ } llvm::Optional -ParsedAST::Build(std::unique_ptr CI, +ParsedAST::Build(const Context &Ctx, + std::unique_ptr CI, std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS, - clangd::Logger &Logger) { + IntrusiveRefCntPtr VFS) { std::vector ASTDiags; StoreDiagsConsumer UnitDiagsConsumer(/*ref*/ ASTDiags); @@ -250,12 +250,12 @@ auto Action = llvm::make_unique(); const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0]; if (!Action->BeginSourceFile(*Clang, MainInput)) { - Logger.log("BeginSourceFile() failed when building AST for " + - MainInput.getFile()); + log(Ctx, "BeginSourceFile() failed when building AST for " + + MainInput.getFile()); return llvm::None; } if (!Action->Execute()) - Logger.log("Execute() failed when building AST for " + MainInput.getFile()); + log(Ctx, "Execute() failed when building AST for " + MainInput.getFile()); // UnitDiagsConsumer is local, we can not store it in CompilerInstance that // has a longer lifetime. @@ -457,8 +457,8 @@ return L; } -std::vector clangd::findDefinitions(ParsedAST &AST, Position Pos, - clangd::Logger &Logger) { +std::vector clangd::findDefinitions(const Context &Ctx, + ParsedAST &AST, Position Pos) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); if (!FE) @@ -499,8 +499,8 @@ } std::vector -clangd::findDocumentHighlights(ParsedAST &AST, Position Pos, - clangd::Logger &Logger) { +clangd::findDocumentHighlights(const Context &Ctx, ParsedAST &AST, + Position Pos) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); if (!FE) @@ -612,23 +612,21 @@ std::shared_ptr CppFile::Create(PathRef FileName, tooling::CompileCommand Command, bool StorePreamblesInMemory, - std::shared_ptr PCHs, - clangd::Logger &Logger) { - return std::shared_ptr(new CppFile(FileName, std::move(Command), - StorePreamblesInMemory, - std::move(PCHs), Logger)); + std::shared_ptr PCHs) { + return std::shared_ptr(new CppFile( + FileName, std::move(Command), StorePreamblesInMemory, std::move(PCHs))); } CppFile::CppFile(PathRef FileName, tooling::CompileCommand Command, bool StorePreamblesInMemory, - std::shared_ptr PCHs, - clangd::Logger &Logger) + std::shared_ptr PCHs) : FileName(FileName), Command(std::move(Command)), StorePreamblesInMemory(StorePreamblesInMemory), RebuildCounter(0), - RebuildInProgress(false), PCHs(std::move(PCHs)), Logger(Logger) { - Logger.log("Opened file " + FileName + " with command [" + - this->Command.Directory + "] " + - llvm::join(this->Command.CommandLine, " ")); + RebuildInProgress(false), PCHs(std::move(PCHs)) { + // FIXME(ibiryukov): we should pass a proper Context here. + log(Context::empty(), "Opened file " + FileName + " with command [" + + this->Command.Directory + "] " + + llvm::join(this->Command.CommandLine, " ")); std::lock_guard Lock(Mutex); LatestAvailablePreamble = nullptr; @@ -680,12 +678,12 @@ } llvm::Optional> -CppFile::rebuild(StringRef NewContents, +CppFile::rebuild(const Context &Ctx, StringRef NewContents, IntrusiveRefCntPtr VFS) { - return deferRebuild(NewContents, std::move(VFS))(); + return deferRebuild(NewContents, std::move(VFS))(Ctx); } -UniqueFunction>()> +UniqueFunction>(const Context &)> CppFile::deferRebuild(StringRef NewContents, IntrusiveRefCntPtr VFS) { std::shared_ptr OldPreamble; @@ -721,10 +719,10 @@ // Don't let this CppFile die before rebuild is finished. std::shared_ptr That = shared_from_this(); - auto FinishRebuild = [OldPreamble, VFS, RequestRebuildCounter, PCHs, - That](std::string NewContents) mutable // 'mutable' to - // allow changing - // OldPreamble. + auto FinishRebuild = + [OldPreamble, VFS, RequestRebuildCounter, PCHs, + That](std::string NewContents, + const Context &Ctx) mutable /* to allow changing OldPreamble. */ -> llvm::Optional> { // Only one execution of this method is possible at a time. // RebuildGuard will wait for any ongoing rebuilds to finish and will put us @@ -820,9 +818,8 @@ { trace::Span Tracer("Build"); SPAN_ATTACH(Tracer, "File", That->FileName); - NewAST = - ParsedAST::Build(std::move(CI), std::move(NewPreamble), - std::move(ContentsBuffer), PCHs, VFS, That->Logger); + NewAST = ParsedAST::Build(Ctx, std::move(CI), std::move(NewPreamble), + std::move(ContentsBuffer), PCHs, VFS); } if (NewAST) { Index: clangd/ClangdUnitStore.h =================================================================== --- clangd/ClangdUnitStore.h +++ clangd/ClangdUnitStore.h @@ -14,6 +14,7 @@ #include "ClangdUnit.h" #include "GlobalCompilationDatabase.h" +#include "Logger.h" #include "Path.h" #include "clang/Tooling/CompilationDatabase.h" @@ -28,8 +29,7 @@ std::shared_ptr getOrCreateFile(PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB, bool StorePreamblesInMemory, - std::shared_ptr PCHs, - clangd::Logger &Logger) { + std::shared_ptr PCHs) { std::lock_guard Lock(Mutex); auto It = OpenedFiles.find(File); @@ -39,7 +39,7 @@ It = OpenedFiles .try_emplace(File, CppFile::Create(File, std::move(Command), StorePreamblesInMemory, - std::move(PCHs), Logger)) + std::move(PCHs))) .first; } return It->second; @@ -61,8 +61,8 @@ /// will be returned in RecreateResult.RemovedFile. RecreateResult recreateFileIfCompileCommandChanged( PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB, - bool StorePreamblesInMemory, std::shared_ptr PCHs, - clangd::Logger &Logger); + bool StorePreamblesInMemory, + std::shared_ptr PCHs); std::shared_ptr getFile(PathRef File) { std::lock_guard Lock(Mutex); Index: clangd/ClangdUnitStore.cpp =================================================================== --- clangd/ClangdUnitStore.cpp +++ clangd/ClangdUnitStore.cpp @@ -29,8 +29,7 @@ CppFileCollection::RecreateResult CppFileCollection::recreateFileIfCompileCommandChanged( PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB, - bool StorePreamblesInMemory, std::shared_ptr PCHs, - clangd::Logger &Logger) { + bool StorePreamblesInMemory, std::shared_ptr PCHs) { auto NewCommand = getCompileCommand(CDB, File, ResourceDir); std::lock_guard Lock(Mutex); @@ -42,14 +41,13 @@ It = OpenedFiles .try_emplace(File, CppFile::Create(File, std::move(NewCommand), StorePreamblesInMemory, - std::move(PCHs), Logger)) + std::move(PCHs))) .first; } else if (!compileCommandsAreEqual(It->second->getCompileCommand(), NewCommand)) { Result.RemovedFile = std::move(It->second); - It->second = - CppFile::Create(File, std::move(NewCommand), StorePreamblesInMemory, - std::move(PCHs), Logger); + It->second = CppFile::Create(File, std::move(NewCommand), + StorePreamblesInMemory, std::move(PCHs)); } Result.FileInCollection = It->second; return Result; Index: clangd/CodeComplete.h =================================================================== --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H +#include "Context.h" #include "Logger.h" #include "Path.h" #include "Protocol.h" @@ -61,20 +62,21 @@ }; /// Get code completions at a specified \p Pos in \p FileName. -CompletionList codeComplete(PathRef FileName, +CompletionList codeComplete(const Context &Ctx, PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - CodeCompleteOptions Opts, Logger &Logger); + CodeCompleteOptions Opts); /// Get signature help at a specified \p Pos in \p FileName. -SignatureHelp -signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, StringRef Contents, - Position Pos, IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs, Logger &Logger); +SignatureHelp signatureHelp(const Context &Ctx, PathRef FileName, + const tooling::CompileCommand &Command, + PrecompiledPreamble const *Preamble, + StringRef Contents, Position Pos, + IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs); } // namespace clangd } // namespace clang Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -584,14 +584,14 @@ }; // SignatureHelpCollector -bool invokeCodeComplete(std::unique_ptr Consumer, +bool invokeCodeComplete(const Context &Ctx, + std::unique_ptr Consumer, const clang::CodeCompleteOptions &Options, PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs, - Logger &Logger) { + std::shared_ptr PCHs) { std::vector ArgStrs; for (const auto &S : Command.CommandLine) ArgStrs.push_back(S.c_str()); @@ -634,12 +634,12 @@ SyntaxOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { - Logger.log("BeginSourceFile() failed when running codeComplete for " + - FileName); + log(Ctx, + "BeginSourceFile() failed when running codeComplete for " + FileName); return false; } if (!Action.Execute()) { - Logger.log("Execute() failed when running codeComplete for " + FileName); + log(Ctx, "Execute() failed when running codeComplete for " + FileName); return false; } @@ -660,13 +660,13 @@ return Result; } -CompletionList codeComplete(PathRef FileName, +CompletionList codeComplete(const Context &Ctx, PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - CodeCompleteOptions Opts, Logger &Logger) { + CodeCompleteOptions Opts) { CompletionList Results; std::unique_ptr Consumer; if (Opts.EnableSnippets) { @@ -676,26 +676,28 @@ Consumer = llvm::make_unique(Opts, Results); } - invokeCodeComplete(std::move(Consumer), Opts.getClangCompleteOpts(), FileName, - Command, Preamble, Contents, Pos, std::move(VFS), - std::move(PCHs), Logger); + invokeCodeComplete(Ctx, std::move(Consumer), Opts.getClangCompleteOpts(), + FileName, Command, Preamble, Contents, Pos, std::move(VFS), + std::move(PCHs)); return Results; } -SignatureHelp -signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, StringRef Contents, - Position Pos, IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs, Logger &Logger) { +SignatureHelp signatureHelp(const Context &Ctx, PathRef FileName, + const tooling::CompileCommand &Command, + PrecompiledPreamble const *Preamble, + StringRef Contents, Position Pos, + IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs) { SignatureHelp Result; clang::CodeCompleteOptions Options; Options.IncludeGlobals = false; Options.IncludeMacros = false; Options.IncludeCodePatterns = false; Options.IncludeBriefComments = true; - invokeCodeComplete(llvm::make_unique(Options, Result), + invokeCodeComplete(Ctx, + llvm::make_unique(Options, Result), Options, FileName, Command, Preamble, Contents, Pos, - std::move(VFS), std::move(PCHs), Logger); + std::move(VFS), std::move(PCHs)); return Result; } Index: clangd/Function.h =================================================================== --- clangd/Function.h +++ clangd/Function.h @@ -39,7 +39,11 @@ UniqueFunction(UniqueFunction &&) noexcept = default; UniqueFunction &operator=(UniqueFunction &&) noexcept = default; - template + template ()(std::declval()...)), + Ret>::value>::type> UniqueFunction(Callable &&Func) : CallablePtr(llvm::make_unique< FunctionCallImpl::type>>( @@ -133,6 +137,40 @@ std::make_tuple(std::forward(F), std::forward(As)...)); } +namespace detail { +/// Runs provided callback in destructor. Use onScopeExit helper function to +/// create this object. +template struct ScopeExitGuard { + static_assert(std::is_same::type, Func>::value, + "Func must be decayed"); + + ScopeExitGuard(Func F) : F(std::move(F)) {} + ~ScopeExitGuard() { + if (!F) + return; + (*F)(); + } + + // Move-only. + ScopeExitGuard(const ScopeExitGuard &) = delete; + ScopeExitGuard &operator=(const ScopeExitGuard &) = delete; + + ScopeExitGuard(ScopeExitGuard &&Other) = default; + ScopeExitGuard &operator=(ScopeExitGuard &&Other) = default; + +private: + llvm::Optional F; +}; +} // namespace detail + +/// Creates a RAII object that will run \p F in its destructor. +template +auto onScopeExit(Func &&F) + -> detail::ScopeExitGuard::type> { + return detail::ScopeExitGuard::type>( + std::forward(F)); +} + } // namespace clangd } // namespace clang Index: clangd/GlobalCompilationDatabase.h =================================================================== --- clangd/GlobalCompilationDatabase.h +++ clangd/GlobalCompilationDatabase.h @@ -51,7 +51,7 @@ : public GlobalCompilationDatabase { public: DirectoryBasedGlobalCompilationDatabase( - clangd::Logger &Logger, llvm::Optional CompileCommandsDir); + llvm::Optional CompileCommandsDir); /// Scans File's parents looking for compilation databases. /// Any extra flags will be added. @@ -77,8 +77,6 @@ /// Stores extra flags per file. llvm::StringMap> ExtraFlagsForFile; - /// Used for logging. - clangd::Logger &Logger; /// Used for command argument pointing to folder where compile_commands.json /// is located. llvm::Optional CompileCommandsDir; Index: clangd/GlobalCompilationDatabase.cpp =================================================================== --- clangd/GlobalCompilationDatabase.cpp +++ clangd/GlobalCompilationDatabase.cpp @@ -26,8 +26,8 @@ DirectoryBasedGlobalCompilationDatabase:: DirectoryBasedGlobalCompilationDatabase( - clangd::Logger &Logger, llvm::Optional CompileCommandsDir) - : Logger(Logger), CompileCommandsDir(std::move(CompileCommandsDir)) {} + llvm::Optional CompileCommandsDir) + : CompileCommandsDir(std::move(CompileCommandsDir)) {} llvm::Optional DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const { @@ -103,9 +103,12 @@ if (CompileCommandsDir.hasValue()) { tooling::CompilationDatabase *ReturnValue = tryLoadDatabaseFromPath(CompileCommandsDir.getValue()); - if (ReturnValue == nullptr) - Logger.log("Failed to find compilation database for " + Twine(File) + - "in overriden directory " + CompileCommandsDir.getValue()); + if (ReturnValue == nullptr) { + // FIXME(ibiryukov): pass a proper Context here. + log(Context::empty(), "Failed to find compilation database for " + + Twine(File) + "in overriden directory " + + CompileCommandsDir.getValue()); + } return ReturnValue; } @@ -118,7 +121,9 @@ return CDB; } - Logger.log("Failed to find compilation database for " + Twine(File)); + // FIXME(ibiryukov): pass a proper Context here. + log(Context::empty(), + "Failed to find compilation database for " + Twine(File)); return nullptr; } Index: clangd/JSONRPCDispatcher.h =================================================================== --- clangd/JSONRPCDispatcher.h +++ clangd/JSONRPCDispatcher.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H +#include "Context.h" #include "JSONExpr.h" #include "Logger.h" #include "Protocol.h" @@ -26,6 +27,8 @@ /// Encapsulates output and logs streams and provides thread-safe access to /// them. class JSONOutput : public Logger { + // FIXME(ibiryukov): figure out if we can shrink the public interface of + // JSONOutput now that we pass Context everywhere. public: JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs, llvm::raw_ostream *InputMirror = nullptr, bool Pretty = false) @@ -35,7 +38,7 @@ void writeMessage(const json::Expr &Result); /// Write a line to the logging stream. - void log(const Twine &Message) override; + void log(const Context &Ctx, const Twine &Message) override; /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is /// null. @@ -53,38 +56,23 @@ std::mutex StreamMutex; }; -/// Context object passed to handlers to allow replies. -class RequestContext { -public: - RequestContext(JSONOutput &Out, StringRef Method, - llvm::Optional ID) - : Out(Out), ID(std::move(ID)), - Tracer(llvm::make_unique(Method)) { - if (this->ID) - SPAN_ATTACH(tracer(), "ID", *this->ID); - } - - /// Sends a successful reply. - void reply(json::Expr &&Result); - /// Sends an error response to the client, and logs it. - void replyError(ErrorCode code, const llvm::StringRef &Message); - /// Sends a request to the client. - void call(llvm::StringRef Method, json::Expr &&Params); - - trace::Span &tracer() { return *Tracer; } - -private: - JSONOutput &Out; - llvm::Optional ID; - std::unique_ptr Tracer; -}; +/// Sends a successful reply. \p Ctx must either be the Context accepted by +/// JSONRPCDispatcher::Handler or be derived from it. +void reply(const Context &Ctx, json::Expr &&Result); +/// Sends an error response to the client, and logs it. \p Ctx must either be +/// the Context accepted by JSONRPCDispatcher::Handler or be derived from it. +void replyError(const Context &Ctx, ErrorCode code, + const llvm::StringRef &Message); +/// Sends a request to the client. \p Ctx must either be the Context accepted by +/// JSONRPCDispatcher::Handler or be derived from it. +void call(const Context &Ctx, llvm::StringRef Method, json::Expr &&Params); /// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the /// registered Handler for the method received. class JSONRPCDispatcher { public: // A handler responds to requests for a particular method name. - using Handler = std::function; + using Handler = std::function; /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown /// method is received. Index: clangd/JSONRPCDispatcher.cpp =================================================================== --- clangd/JSONRPCDispatcher.cpp +++ clangd/JSONRPCDispatcher.cpp @@ -19,6 +19,12 @@ using namespace clang; using namespace clangd; +namespace { +static Key> RequestSpan; +static Key RequestID; +static Key RequestOut; +} // namespace + void JSONOutput::writeMessage(const json::Expr &Message) { std::string S; llvm::raw_string_ostream OS(S); @@ -38,7 +44,7 @@ Outs.flush(); } -void JSONOutput::log(const Twine &Message) { +void JSONOutput::log(const Context &Ctx, const Twine &Message) { trace::log(Message); std::lock_guard Guard(StreamMutex); Logs << Message << '\n'; @@ -53,46 +59,56 @@ InputMirror->flush(); } -void RequestContext::reply(json::Expr &&Result) { +void clangd::reply(const Context &Ctx, json::Expr &&Result) { + auto ID = Ctx.get(RequestID); if (!ID) { - Out.log("Attempted to reply to a notification!"); + log(Ctx, "Attempted to reply to a notification!"); return; } - SPAN_ATTACH(tracer(), "Reply", Result); - Out.writeMessage(json::obj{ - {"jsonrpc", "2.0"}, - {"id", *ID}, - {"result", std::move(Result)}, - }); -} - -void RequestContext::replyError(ErrorCode code, - const llvm::StringRef &Message) { - Out.log("Error " + Twine(static_cast(code)) + ": " + Message); - SPAN_ATTACH(tracer(), "Error", - (json::obj{{"code", static_cast(code)}, - {"message", Message.str()}})); - if (ID) { - Out.writeMessage(json::obj{ - {"jsonrpc", "2.0"}, - {"id", *ID}, - {"error", - json::obj{{"code", static_cast(code)}, {"message", Message}}}, - }); + + if (auto *Span = Ctx.get(RequestSpan)) + SPAN_ATTACH(**Span, "Reply", Result); + + Ctx.getExisting(RequestOut) + ->writeMessage(json::obj{ + {"jsonrpc", "2.0"}, + {"id", *ID}, + {"result", std::move(Result)}, + }); +} + +void clangd::replyError(const Context &Ctx, ErrorCode code, + const llvm::StringRef &Message) { + log(Ctx, "Error " + Twine(static_cast(code)) + ": " + Message); + if (auto *Span = Ctx.get(RequestSpan)) + SPAN_ATTACH(**Span, "Error", + (json::obj{{"code", static_cast(code)}, + {"message", Message.str()}})); + + if (auto ID = Ctx.get(RequestID)) { + Ctx.getExisting(RequestOut) + ->writeMessage(json::obj{ + {"jsonrpc", "2.0"}, + {"id", *ID}, + {"error", + json::obj{{"code", static_cast(code)}, {"message", Message}}}, + }); } } -void RequestContext::call(StringRef Method, json::Expr &&Params) { +void clangd::call(const Context &Ctx, StringRef Method, json::Expr &&Params) { // FIXME: Generate/Increment IDs for every request so that we can get proper // replies once we need to. - SPAN_ATTACH(tracer(), "Call", - (json::obj{{"method", Method.str()}, {"params", Params}})); - Out.writeMessage(json::obj{ - {"jsonrpc", "2.0"}, - {"id", 1}, - {"method", Method}, - {"params", std::move(Params)}, - }); + if (auto *Span = Ctx.get(RequestSpan)) + SPAN_ATTACH(**Span, "Call", + (json::obj{{"method", Method.str()}, {"params", Params}})); + Ctx.getExisting(RequestOut) + ->writeMessage(json::obj{ + {"jsonrpc", "2.0"}, + {"id", 1}, + {"method", Method}, + {"params", std::move(Params)}, + }); } void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) { @@ -120,8 +136,18 @@ auto I = Handlers.find(*Method); auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; - RequestContext Ctx(Out, *Method, std::move(ID)); - SPAN_ATTACH(Ctx.tracer(), "Params", Params); + + auto Tracer = llvm::make_unique(*Method); + if (ID) + SPAN_ATTACH(*Tracer, "ID", *ID); + SPAN_ATTACH(*Tracer, "Params", Params); + + auto Ctx = Context::empty() + .derive(RequestOut, &Out) + .derive(RequestSpan, std::move(Tracer)); + if (ID) + Ctx = std::move(Ctx).derive(RequestID, *ID); + Handler(std::move(Ctx), std::move(Params)); return true; } @@ -164,9 +190,10 @@ // The end of headers is signified by an empty line. if (LineRef.consume_front("Content-Length: ")) { if (ContentLength != 0) { - Out.log("Warning: Duplicate Content-Length header received. " - "The previous value for this message (" + - std::to_string(ContentLength) + ") was ignored."); + log(Context::empty(), + "Warning: Duplicate Content-Length header received. " + "The previous value for this message (" + + std::to_string(ContentLength) + ") was ignored.\n"); } llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength); @@ -185,8 +212,8 @@ // and we don't want to crash downstream because of it. if (ContentLength > 1 << 30) { // 1024M In.ignore(ContentLength); - Out.log("Skipped overly large message of " + Twine(ContentLength) + - " bytes."); + log(Context::empty(), "Skipped overly large message of " + + Twine(ContentLength) + " bytes.\n"); continue; } @@ -200,9 +227,10 @@ // If the stream is aborted before we read ContentLength bytes, In // will have eofbit and failbit set. if (!In) { - Out.log("Input was aborted. Read only " + - std::to_string(In.gcount()) + " bytes of expected " + - std::to_string(ContentLength) + "."); + log(Context::empty(), "Input was aborted. Read only " + + std::to_string(In.gcount()) + + " bytes of expected " + + std::to_string(ContentLength) + ".\n"); break; } @@ -211,23 +239,25 @@ if (auto Doc = json::parse(JSONRef)) { // Log the formatted message. - Out.log(llvm::formatv(Out.Pretty ? "<-- {0:2}" : "<-- {0}", *Doc)); + log(Context::empty(), + llvm::formatv(Out.Pretty ? "<-- {0:2}\n" : "<-- {0}\n", *Doc)); // Finally, execute the action for this JSON message. if (!Dispatcher.call(*Doc, Out)) - Out.log("JSON dispatch failed!"); + log(Context::empty(), "JSON dispatch failed!\n"); } else { // Parse error. Log the raw message. - Out.log("<-- " + JSONRef); - Out.log(llvm::Twine("JSON parse error: ") + - llvm::toString(Doc.takeError())); + log(Context::empty(), "<-- " + JSONRef + "\n"); + log(Context::empty(), llvm::Twine("JSON parse error: ") + + llvm::toString(Doc.takeError()) + "\n"); } // If we're done, exit the loop. if (IsDone) break; } else { - Out.log("Warning: Missing Content-Length header, or message has zero " - "length."); + log(Context::empty(), + "Warning: Missing Content-Length header, or message has zero " + "length.\n"); } } } Index: clangd/Logger.h =================================================================== --- clangd/Logger.h +++ clangd/Logger.h @@ -10,29 +10,36 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_LOGGER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LOGGER_H +#include "Context.h" #include "llvm/ADT/Twine.h" namespace clang { namespace clangd { +/// Main logging function. Logs messages to a global logger, which can be set up +/// by LoggingSesssion. +void log(const Context &Ctx, const llvm::Twine &Message); + /// Interface to allow custom logging in clangd. class Logger { public: virtual ~Logger() = default; /// Implementations of this method must be thread-safe. - virtual void log(const llvm::Twine &Message) = 0; + virtual void log(const Context &Ctx, const llvm::Twine &Message) = 0; }; -/// Logger implementation that ignores all messages. -class EmptyLogger : public Logger { +/// Only one LoggingSession can be active at a time. +class LoggingSession { public: - static EmptyLogger &getInstance(); + LoggingSession(clangd::Logger &Instance); + ~LoggingSession(); - void log(const llvm::Twine &Message) override; + LoggingSession(LoggingSession &&) = delete; + LoggingSession &operator=(LoggingSession &&) = delete; -private: - EmptyLogger() = default; + LoggingSession(LoggingSession const &) = delete; + LoggingSession &operator=(LoggingSession const &) = delete; }; } // namespace clangd Index: clangd/Logger.cpp =================================================================== --- clangd/Logger.cpp +++ clangd/Logger.cpp @@ -9,11 +9,25 @@ #include "Logger.h" -using namespace clang::clangd; +namespace clang { +namespace clangd { -EmptyLogger &EmptyLogger::getInstance() { - static EmptyLogger Logger; - return Logger; +namespace { +Logger *L = nullptr; +} // namespace + +LoggingSession::LoggingSession(clangd::Logger &Instance) { + assert(!L); + L = &Instance; +} + +LoggingSession::~LoggingSession() { L = nullptr; } + +void log(const Context &Ctx, const llvm::Twine &Message) { + if (!L) + return; + L->log(Ctx, Message); } -void EmptyLogger::log(const llvm::Twine &Message) {} +} // namespace clangd +} // namespace clang Index: clangd/ProtocolHandlers.h =================================================================== --- clangd/ProtocolHandlers.h +++ clangd/ProtocolHandlers.h @@ -29,7 +29,8 @@ // The interface implemented by ClangLSPServer to handle incoming requests. class ProtocolCallbacks { public: - using Ctx = RequestContext; + // FIXME(ibiryukov): remove this typedef, inline its usages. + using Ctx = Context; virtual ~ProtocolCallbacks() = default; virtual void onInitialize(Ctx C, InitializeParams &Params) = 0; Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -25,17 +25,16 @@ struct HandlerRegisterer { template void operator()(StringRef Method, - void (ProtocolCallbacks::*Handler)(RequestContext, Param)) { + void (ProtocolCallbacks::*Handler)(Context, Param)) { // Capture pointers by value, as the lambda will outlive this object. - auto *Out = this->Out; auto *Callbacks = this->Callbacks; Dispatcher.registerHandler( - Method, [=](RequestContext C, const json::Expr &RawParams) { + Method, [=](Context C, const json::Expr &RawParams) { typename std::remove_reference::type P; if (fromJSON(RawParams, P)) { (Callbacks->*Handler)(std::move(C), P); } else { - Out->log("Failed to decode " + Method + " request."); + log(C, "Failed to decode " + Method + " request."); } }); } Index: clangd/tool/ClangdMain.cpp =================================================================== --- clangd/tool/ClangdMain.cpp +++ clangd/tool/ClangdMain.cpp @@ -134,6 +134,8 @@ InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint); + clangd::LoggingSession LoggingSession(Out); + // If --compile-commands-dir arg was invoked, check value and override default // path. llvm::Optional CompileCommandsDirPath; Index: unittests/clangd/ClangdTests.cpp =================================================================== --- unittests/clangd/ClangdTests.cpp +++ unittests/clangd/ClangdTests.cpp @@ -9,7 +9,7 @@ #include "ClangdLSPServer.h" #include "ClangdServer.h" -#include "Logger.h" +#include "Context.h" #include "TestFS.h" #include "clang/Config/config.h" #include "llvm/ADT/SmallVector.h" @@ -123,8 +123,7 @@ ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); for (const auto &FileWithContents : ExtraFiles) FS.Files[getVirtualTestFilePath(FileWithContents.first)] = FileWithContents.second; @@ -135,7 +134,8 @@ // Have to sync reparses because requests are processed on the calling // thread. - auto AddDocFuture = Server.addDocument(SourceFilename, SourceContents); + auto AddDocFuture = + Server.addDocument(Context::empty(), SourceFilename, SourceContents); auto Result = dumpASTWithoutMemoryLocs(Server, SourceFilename); @@ -187,8 +187,7 @@ ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); const auto SourceContents = R"cpp( #include "foo.h" @@ -203,21 +202,21 @@ FS.ExpectedFile = FooCpp; // To sync reparses before checking for errors. - std::future ParseFuture; + std::future ParseFuture; - ParseFuture = Server.addDocument(FooCpp, SourceContents); + ParseFuture = Server.addDocument(Context::empty(), FooCpp, SourceContents); auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); - ParseFuture = Server.addDocument(FooCpp, ""); + ParseFuture = Server.addDocument(Context::empty(), FooCpp, ""); auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); - ParseFuture = Server.addDocument(FooCpp, SourceContents); + ParseFuture = Server.addDocument(Context::empty(), FooCpp, SourceContents); auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); @@ -233,8 +232,7 @@ MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); const auto SourceContents = R"cpp( #include "foo.h" @@ -249,23 +247,23 @@ FS.ExpectedFile = FooCpp; // To sync reparses before checking for errors. - std::future ParseFuture; + std::future ParseFuture; - ParseFuture = Server.addDocument(FooCpp, SourceContents); + ParseFuture = Server.addDocument(Context::empty(), FooCpp, SourceContents); auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); FS.Files[FooH] = ""; - ParseFuture = Server.forceReparse(FooCpp); + ParseFuture = Server.forceReparse(Context::empty(), FooCpp); auto DumpParseDifferent = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); FS.Files[FooH] = "int a;"; - ParseFuture = Server.forceReparse(FooCpp); + ParseFuture = Server.forceReparse(Context::empty(), FooCpp); auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); @@ -282,8 +280,7 @@ // Run ClangdServer synchronously. ClangdServer Server(CDB, DiagConsumer, FS, /*AsyncThreadsCount=*/0, - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); auto FooCpp = getVirtualTestFilePath("foo.cpp"); const auto SourceContents = "int a;"; @@ -296,16 +293,22 @@ // No need to sync reparses, because requests are processed on the calling // thread. FS.Tag = "123"; - Server.addDocument(FooCpp, SourceContents); - EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}, CCOpts).get().Tag, - FS.Tag); + Server.addDocument(Context::empty(), FooCpp, SourceContents); + EXPECT_EQ( + Server.codeComplete(Context::empty(), FooCpp, Position{0, 0}, CCOpts) + .get() + .second.Tag, + FS.Tag); EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag); FS.Tag = "321"; - Server.addDocument(FooCpp, SourceContents); + Server.addDocument(Context::empty(), FooCpp, SourceContents); EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag); - EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}, CCOpts).get().Tag, - FS.Tag); + EXPECT_EQ( + Server.codeComplete(Context::empty(), FooCpp, Position{0, 0}, CCOpts) + .get() + .second.Tag, + FS.Tag); } // Only enable this test on Unix @@ -322,8 +325,7 @@ // Run ClangdServer synchronously. ClangdServer Server(CDB, DiagConsumer, FS, /*AsyncThreadsCount=*/0, - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); // Just a random gcc version string SmallString<8> Version("4.9.3"); @@ -354,14 +356,14 @@ // No need to sync reparses, because requests are processed on the calling // thread. - Server.addDocument(FooCpp, SourceContents); + Server.addDocument(Context::empty(), FooCpp, SourceContents); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); const auto SourceContentsWithError = R"cpp( #include std::string x; )cpp"; - Server.addDocument(FooCpp, SourceContentsWithError); + Server.addDocument(Context::empty(), FooCpp, SourceContentsWithError); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); } #endif // LLVM_ON_UNIX @@ -372,11 +374,10 @@ MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, /*AsyncThreadsCount=*/0, - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); - // No need to sync reparses, because reparses are performed on the calling - // thread to true. + /*StorePreamblesInMemory=*/true); + // No need to sync reparses, because reparses are performed on the calling + // thread. auto FooCpp = getVirtualTestFilePath("foo.cpp"); const auto SourceContents1 = R"cpp( template @@ -392,26 +393,26 @@ // First parse files in C mode and check they produce errors. CDB.ExtraClangFlags = {"-xc"}; - Server.addDocument(FooCpp, SourceContents1); + Server.addDocument(Context::empty(), FooCpp, SourceContents1); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(FooCpp, SourceContents2); + Server.addDocument(Context::empty(), FooCpp, SourceContents2); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); // Now switch to C++ mode. CDB.ExtraClangFlags = {"-xc++"}; // Currently, addDocument never checks if CompileCommand has changed, so we // expect to see the errors. - Server.addDocument(FooCpp, SourceContents1); + Server.addDocument(Context::empty(), FooCpp, SourceContents1); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(FooCpp, SourceContents2); + Server.addDocument(Context::empty(), FooCpp, SourceContents2); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); // But forceReparse should reparse the file with proper flags. - Server.forceReparse(FooCpp); + Server.forceReparse(Context::empty(), FooCpp); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); // Subsequent addDocument calls should finish without errors too. - Server.addDocument(FooCpp, SourceContents1); + Server.addDocument(Context::empty(), FooCpp, SourceContents1); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(FooCpp, SourceContents2); + Server.addDocument(Context::empty(), FooCpp, SourceContents2); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); } @@ -501,7 +502,7 @@ unsigned RequestsWithErrors = 0; bool LastContentsHadErrors = false; bool FileIsRemoved = true; - std::future LastRequestFuture; + std::future LastRequestFuture; }; std::vector ReqStats; @@ -513,8 +514,7 @@ { MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); // Prepare some random distributions for the test. std::random_device RandGen; @@ -529,7 +529,7 @@ // Some helpers. auto UpdateStatsOnAddDocument = [&](unsigned FileIndex, bool HadErrors, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; if (HadErrors) @@ -542,7 +542,7 @@ }; auto UpdateStatsOnRemoveDocument = [&](unsigned FileIndex, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; Stats.FileIsRemoved = true; @@ -550,7 +550,7 @@ }; auto UpdateStatsOnForceReparse = [&](unsigned FileIndex, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; Stats.LastRequestFuture = std::move(Future); @@ -562,9 +562,10 @@ auto AddDocument = [&](unsigned FileIndex) { bool ShouldHaveErrors = ShouldHaveErrorsDist(RandGen); - auto Future = Server.addDocument( - FilePaths[FileIndex], ShouldHaveErrors ? SourceContentsWithErrors - : SourceContentsWithoutErrors); + auto Future = + Server.addDocument(Context::empty(), FilePaths[FileIndex], + ShouldHaveErrors ? SourceContentsWithErrors + : SourceContentsWithoutErrors); UpdateStatsOnAddDocument(FileIndex, ShouldHaveErrors, std::move(Future)); }; @@ -580,7 +581,7 @@ if (ReqStats[FileIndex].FileIsRemoved) AddDocument(FileIndex); - auto Future = Server.forceReparse(FilePaths[FileIndex]); + auto Future = Server.forceReparse(Context::empty(), FilePaths[FileIndex]); UpdateStatsOnForceReparse(FileIndex, std::move(Future)); }; @@ -590,7 +591,8 @@ if (ReqStats[FileIndex].FileIsRemoved) AddDocument(FileIndex); - auto Future = Server.removeDocument(FilePaths[FileIndex]); + auto Future = + Server.removeDocument(Context::empty(), FilePaths[FileIndex]); UpdateStatsOnRemoveDocument(FileIndex, std::move(Future)); }; @@ -608,7 +610,7 @@ // cancelled by any subsequent AddDocument/RemoveDocument request to the // same file. Server - .codeComplete(FilePaths[FileIndex], Pos, + .codeComplete(Context::empty(), FilePaths[FileIndex], Pos, clangd::CodeCompleteOptions()) .wait(); }; @@ -620,7 +622,8 @@ AddDocument(FileIndex); Position Pos{LineDist(RandGen), ColumnDist(RandGen)}; - ASSERT_TRUE(!!Server.findDefinitions(FilePaths[FileIndex], Pos)); + ASSERT_TRUE(!!Server.findDefinitions(Context::empty(), + FilePaths[FileIndex], Pos)); }; std::vector> AsyncRequests = { @@ -676,8 +679,7 @@ MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); auto SourceContents = R"cpp( #include "foo.h" @@ -802,12 +804,13 @@ std::move(StartSecondReparsePromise)); MockCompilationDatabase CDB; - ClangdServer Server(CDB, DiagConsumer, FS, 4, /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); - Server.addDocument(FooCpp, SourceContentsWithErrors); + ClangdServer Server(CDB, DiagConsumer, FS, 4, + /*StorePreamblesInMemory=*/true); + Server.addDocument(Context::empty(), FooCpp, SourceContentsWithErrors); StartSecondReparse.wait(); - auto Future = Server.addDocument(FooCpp, SourceContentsWithoutErrors); + auto Future = + Server.addDocument(Context::empty(), FooCpp, SourceContentsWithoutErrors); Future.wait(); } Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -9,6 +9,7 @@ #include "ClangdServer.h" #include "Compiler.h" #include "Matchers.h" +#include "Context.h" #include "Protocol.h" #include "TestFS.h" #include "gmock/gmock.h" @@ -92,12 +93,13 @@ MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); auto File = getVirtualTestFilePath("foo.cpp"); auto Test = parseTextMarker(Text); - Server.addDocument(File, Test.Text); - return Server.codeComplete(File, Test.MarkerPos, Opts).get().Value; + Server.addDocument(Context::empty(), File, Test.Text); + return Server.codeComplete(Context::empty(), File, Test.MarkerPos, Opts) + .get() + .second.Value; } TEST(CompletionTest, Limit) { @@ -127,7 +129,6 @@ int Qux; }; )cpp"; - EXPECT_THAT(completions(Body + "int main() { S().Foba^ }").items, AllOf(Has("FooBar"), Has("FooBaz"), Not(Has("Qux")))); @@ -269,18 +270,17 @@ IgnoreDiagnostics DiagConsumer; MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); auto File = getVirtualTestFilePath("foo.cpp"); - Server.addDocument(File, "ignored text!"); + Server.addDocument(Context::empty(), File, "ignored text!"); auto Example = parseTextMarker("int cbc; int b = ^;"); auto Results = Server - .codeComplete(File, Example.MarkerPos, clangd::CodeCompleteOptions(), - StringRef(Example.Text)) + .codeComplete(Context::empty(), File, Example.MarkerPos, + clangd::CodeCompleteOptions(), StringRef(Example.Text)) .get() - .Value; + .second.Value; EXPECT_THAT(Results.items, Contains(Named("cbc"))); }