Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -35,7 +35,7 @@ } // namespace void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) { - C.reply(json::obj{ + reply(C, json::obj{ {{"capabilities", json::obj{ {"textDocumentSync", 1}, @@ -72,7 +72,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; } @@ -82,17 +82,19 @@ 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(Params.textDocument.uri.file, Params.textDocument.text, + std::move(C)); } 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, - Params.contentChanges[0].text); + Params.contentChanges[0].text, + std::move(C)); } void ClangdLSPServer::onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) { @@ -113,16 +115,16 @@ 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( + replyError(C, ErrorCode::InvalidParams, llvm::formatv("Unsupported command \"{0}\".", Params.command).str()); } @@ -130,29 +132,30 @@ 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(File, Params.position, Params.newName, C); 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(Params.textDocument.uri.file, std::move(C)); } void ClangdLSPServer::onDocumentOnTypeFormatting( Ctx C, DocumentOnTypeFormattingParams &Params) { auto File = Params.textDocument.uri.file; std::string Code = Server.getDocument(File); - C.reply(json::ary( + reply(C, json::ary( replacementsToEdits(Code, Server.formatOnType(File, Params.position)))); } @@ -160,7 +163,7 @@ Ctx C, DocumentRangeFormattingParams &Params) { auto File = Params.textDocument.uri.file; std::string Code = Server.getDocument(File); - C.reply(json::ary( + reply(C, json::ary( replacementsToEdits(Code, Server.formatRange(File, Params.range)))); } @@ -168,7 +171,7 @@ DocumentFormattingParams &Params) { auto File = Params.textDocument.uri.file; std::string Code = Server.getDocument(File); - C.reply(json::ary(replacementsToEdits(Code, Server.formatFile(File)))); + reply(C, json::ary(replacementsToEdits(Code, Server.formatFile(File)))); } void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) { @@ -190,49 +193,49 @@ }); } } - 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 List = Server + .codeComplete( + Params.textDocument.uri.file, + Position{Params.position.line, Params.position.character}, + CCOpts, C.derive()) + .get() // FIXME(ibiryukov): This could be made async if we + // had an API that would allow to attach callbacks to + // futures returned by ClangdServer. + .first.Value; + reply(C, List); } void ClangdLSPServer::onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) { auto SignatureHelp = Server.signatureHelp( Params.textDocument.uri.file, - Position{Params.position.line, Params.position.character}); + Position{Params.position.line, Params.position.character}, C); 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, - Position{Params.position.line, Params.position.character}); + Position{Params.position.line, Params.position.character}, C); 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 : ""); } ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount, @@ -240,18 +243,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"); + [](Context Ctx, const json::Expr &Params) { + replyError(Ctx, ErrorCode::MethodNotFound, "method not found"); }); registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this); 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,13 +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, llvm::Optional ResourceDir = llvm::None); /// Set the root path of the workspace. @@ -223,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(PathRef File, StringRef Contents, + Context Ctx); /// 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(PathRef File, Context Ctx); /// 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(PathRef File, Context Ctx); /// DEPRECATED. Please use a callback-based version, this API is deprecated /// and will soon be removed. @@ -252,19 +248,25 @@ /// 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> + /// + /// The callers are responsible for making sure \p Ctx stays alive until + /// std::future<> is ready (i.e. wait() or get() returns) + std::future, Context>> codeComplete(PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, + const clangd::CodeCompleteOptions &Opts, Context Ctx, 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); + /// The callers are responsible for making sure \p Ctx stays alive until \p + /// Callback is executed. + void + codeComplete(UniqueFunction, Context)> Callback, + PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, Context Ctx, + 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 @@ -274,13 +276,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(PathRef File, Position Pos, const Context &Ctx, 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(PathRef File, Position Pos, const Context &Ctx); /// Helper function that returns a path to the corresponding source file when /// given a header file and vice versa. @@ -295,7 +297,8 @@ /// Rename all occurrences of the symbol at the \p Pos in \p File to /// \p NewName. Expected> rename(PathRef File, Position Pos, - llvm::StringRef NewName); + llvm::StringRef NewName, + const Context &Ctx); /// Gets current document contents for \p File. \p File must point to a /// currently tracked file. @@ -311,14 +314,13 @@ void onFileEvent(const DidChangeWatchedFilesParams &Params); private: - std::future - scheduleReparseAndDiags(PathRef File, VersionedDraft Contents, - std::shared_ptr Resources, - Tagged> TaggedFS); + std::future scheduleReparseAndDiags( + PathRef File, VersionedDraft Contents, std::shared_ptr Resources, + Tagged> TaggedFS, Context Ctx); - std::future scheduleCancelRebuild(std::shared_ptr Resources); + std::future scheduleCancelRebuild(std::shared_ptr Resources, + Context Ctx); - clangd::Logger &Logger; GlobalCompilationDatabase &CDB; DiagnosticsConsumer &DiagConsumer; FileSystemProvider &FSProvider; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -28,14 +28,16 @@ namespace { -class FulfillPromiseGuard { +class FulfillContextPromiseGuard { public: - FulfillPromiseGuard(std::promise &Promise) : Promise(Promise) {} + FulfillContextPromiseGuard(std::promise &Promise, Context &Ctx) + : Promise(Promise), Ctx(Ctx) {} - ~FulfillPromiseGuard() { Promise.set_value(); } + ~FulfillContextPromiseGuard() { Promise.set_value(std::move(Ctx)); } private: - std::promise &Promise; + std::promise &Promise; + Context &Ctx; }; std::vector formatCode(StringRef Code, StringRef Filename, @@ -173,10 +175,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), @@ -189,65 +190,69 @@ this->RootPath = NewRootPath; } -std::future ClangdServer::addDocument(PathRef File, StringRef Contents) { +std::future ClangdServer::addDocument(PathRef File, StringRef Contents, + Context Ctx) { DocVersion Version = DraftMgr.updateDraft(File, Contents); auto TaggedFS = FSProvider.getTaggedFileSystem(File); std::shared_ptr Resources = Units.getOrCreateFile( - File, ResourceDir, CDB, StorePreamblesInMemory, PCHs, Logger); + File, ResourceDir, CDB, StorePreamblesInMemory, PCHs); return scheduleReparseAndDiags(File, VersionedDraft{Version, Contents.str()}, - std::move(Resources), std::move(TaggedFS)); + std::move(Resources), std::move(TaggedFS), + std::move(Ctx)); } -std::future ClangdServer::removeDocument(PathRef File) { +std::future ClangdServer::removeDocument(PathRef File, Context Ctx) { DraftMgr.removeDraft(File); std::shared_ptr Resources = Units.removeIfPresent(File); - return scheduleCancelRebuild(std::move(Resources)); + return scheduleCancelRebuild(std::move(Resources), std::move(Ctx)); } -std::future ClangdServer::forceReparse(PathRef File) { +std::future ClangdServer::forceReparse(PathRef File, Context Ctx) { 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)); + // Note that std::future from this cleanup action. + // FIXME(ibiryukov): We use a global context here, should not fork the action + // instead. + scheduleCancelRebuild(std::move(Recreated.RemovedFile), buildCtx()); // Schedule a reparse. return scheduleReparseAndDiags(File, std::move(FileContents), std::move(Recreated.FileInCollection), - std::move(TaggedFS)); + std::move(TaggedFS), std::move(Ctx)); } -std::future> +std::future, Context>> ClangdServer::codeComplete(PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, + const clangd::CodeCompleteOptions &Opts, Context Ctx, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { - using ResultType = Tagged; + using ResultType = std::pair, Context>; std::promise ResultPromise; auto Callback = [](std::promise ResultPromise, - ResultType Result) -> void { - ResultPromise.set_value(std::move(Result)); + Tagged Result, Context Ctx) -> void { + ResultPromise.set_value({std::move(Result), std::move(Ctx)}); }; std::future ResultFuture = ResultPromise.get_future(); codeComplete(BindWithForward(Callback, std::move(ResultPromise)), File, Pos, - Opts, OverridenContents, UsedFS); + Opts, std::move(Ctx), OverridenContents, UsedFS); return ResultFuture; } void ClangdServer::codeComplete( - UniqueFunction)> Callback, PathRef File, - Position Pos, const clangd::CodeCompleteOptions &Opts, - llvm::Optional OverridenContents, + UniqueFunction, Context)> Callback, + PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, + Context Ctx, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { - using CallbackType = UniqueFunction)>; + using CallbackType = UniqueFunction, Context)>; std::string Contents; if (OverridenContents) { @@ -278,7 +283,7 @@ // A task that will be run asynchronously. auto Task = // 'mutable' to reassign Preamble variable. - [=](CallbackType Callback) mutable { + [=](CallbackType Callback, Context Ctx) mutable { if (!Preamble) { // Maybe we built some preamble before processing this request. Preamble = Resources->getPossiblyStalePreamble(); @@ -289,16 +294,18 @@ CompletionList Result = clangd::codeComplete( File, Resources->getCompileCommand(), Preamble ? &Preamble->Preamble : nullptr, Contents, Pos, - TaggedFS.Value, PCHs, CodeCompleteOpts, Logger); + TaggedFS.Value, PCHs, CodeCompleteOpts, Ctx); - Callback(make_tagged(std::move(Result), std::move(TaggedFS.Tag))); + Callback(make_tagged(std::move(Result), std::move(TaggedFS.Tag)), + std::move(Ctx)); }; - WorkScheduler.addToFront(std::move(Task), std::move(Callback)); + WorkScheduler.addToFront(std::move(Task), std::move(Callback), + std::move(Ctx)); } llvm::Expected> -ClangdServer::signatureHelp(PathRef File, Position Pos, +ClangdServer::signatureHelp(PathRef File, Position Pos, const Context &Ctx, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { std::string DraftStorage; @@ -324,10 +331,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(File, Resources->getCompileCommand(), + Preamble ? &Preamble->Preamble : nullptr, + *OverridenContents, Pos, TaggedFS.Value, PCHs, Ctx); return make_tagged(std::move(Result), TaggedFS.Tag); } @@ -361,7 +368,8 @@ } Expected> -ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName) { +ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName, + const Context &Ctx) { std::string Code = getDocument(File); std::shared_ptr Resources = Units.getFile(File); RefactoringResultCollector ResultCollector; @@ -432,7 +440,7 @@ } llvm::Expected>> -ClangdServer::findDefinitions(PathRef File, Position Pos) { +ClangdServer::findDefinitions(PathRef File, Position Pos, const Context &Ctx) { auto TaggedFS = FSProvider.getTaggedFileSystem(File); std::shared_ptr Resources = Units.getFile(File); @@ -442,10 +450,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(*AST, Pos, Ctx); }); return make_tagged(std::move(Result), TaggedFS.Tag); } @@ -509,32 +517,33 @@ return llvm::None; } -std::future ClangdServer::scheduleReparseAndDiags( +std::future ClangdServer::scheduleReparseAndDiags( PathRef File, VersionedDraft Contents, std::shared_ptr Resources, - Tagged> TaggedFS) { + Tagged> TaggedFS, Context Ctx) { 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 { + FulfillContextPromiseGuard Guard(DonePromise, 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. @@ -555,28 +564,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(std::shared_ptr Resources, + Context Ctx) { + 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) { + FulfillContextPromiseGuard Guard(DonePromise, Ctx); DeferredCancel(); }; 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; @@ -69,7 +68,7 @@ std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS, clangd::Logger &Logger); + IntrusiveRefCntPtr VFS, const Context &Ctx); 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(StringRef NewContents, IntrusiveRefCntPtr VFS, + const Context &Ctx); /// 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,10 +252,14 @@ std::shared_ptr LatestAvailablePreamble; /// Utility class, required by clang. std::shared_ptr PCHs; - /// Used for logging various messages. - clangd::Logger &Logger; }; +/// 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, const Context &Ctx); /// Get the beginning SourceLocation at a specified \p Pos. SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos, @@ -263,7 +267,7 @@ /// Get definition of symbol at a specified \p Pos. std::vector findDefinitions(ParsedAST &AST, Position Pos, - clangd::Logger &Logger); + const Context &Ctx); /// 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 @@ -170,8 +170,7 @@ std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS, - clangd::Logger &Logger) { + IntrusiveRefCntPtr VFS, const Context &Ctx) { std::vector ASTDiags; StoreDiagsConsumer UnitDiagsConsumer(/*ref*/ ASTDiags); @@ -189,12 +188,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. @@ -320,7 +319,7 @@ } // namespace std::vector clangd::findDefinitions(ParsedAST &AST, Position Pos, - clangd::Logger &Logger) { + const Context &Ctx) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); if (!FE) @@ -423,23 +422,20 @@ 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)) { + log(Context::empty(), "Opened file " + FileName + " with command [" + + this->Command.Directory + "] " + + llvm::join(this->Command.CommandLine, " ")); std::lock_guard Lock(Mutex); LatestAvailablePreamble = nullptr; @@ -491,12 +487,12 @@ } llvm::Optional> -CppFile::rebuild(StringRef NewContents, - IntrusiveRefCntPtr VFS) { - return deferRebuild(NewContents, std::move(VFS))(); +CppFile::rebuild(StringRef NewContents, IntrusiveRefCntPtr VFS, + const Context &Ctx) { + return deferRebuild(NewContents, std::move(VFS))(Ctx); } -UniqueFunction>()> +UniqueFunction>(const Context &)> CppFile::deferRebuild(StringRef NewContents, IntrusiveRefCntPtr VFS) { std::shared_ptr OldPreamble; @@ -533,9 +529,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. + That](std::string NewContents, + const Context &Ctx) mutable // '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 @@ -631,9 +628,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(std::move(CI), std::move(NewPreamble), + std::move(ContentsBuffer), PCHs, VFS, Ctx); } 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" @@ -67,14 +68,14 @@ StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - CodeCompleteOptions Opts, Logger &Logger); + CodeCompleteOptions Opts, const Context &Ctx); /// 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); + std::shared_ptr PCHs, const Context &Ctx); } // namespace clangd } // namespace clang Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -591,7 +591,7 @@ PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - Logger &Logger) { + const Context &Ctx) { 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; } @@ -666,7 +666,7 @@ StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - CodeCompleteOptions Opts, Logger &Logger) { + CodeCompleteOptions Opts, const Context &Ctx) { CompletionList Results; std::unique_ptr Consumer; if (Opts.EnableSnippets) { @@ -678,15 +678,17 @@ } invokeCodeComplete(std::move(Consumer), Opts.getClangCompleteOpts(), FileName, Command, Preamble, Contents, Pos, std::move(VFS), - std::move(PCHs), Logger); + std::move(PCHs), Ctx); 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(PathRef FileName, + const tooling::CompileCommand &Command, + PrecompiledPreamble const *Preamble, + StringRef Contents, Position Pos, + IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs, + const Context &Ctx) { SignatureHelp Result; clang::CodeCompleteOptions Options; Options.IncludeGlobals = false; @@ -695,7 +697,7 @@ Options.IncludeBriefComments = true; invokeCodeComplete(llvm::make_unique(Options, Result), Options, FileName, Command, Preamble, Contents, Pos, - std::move(VFS), std::move(PCHs), Logger); + std::move(VFS), std::move(PCHs), Ctx); return Result; } 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 { @@ -104,8 +104,9 @@ tooling::CompilationDatabase *ReturnValue = tryLoadDatabaseFromPath(CompileCommandsDir.getValue()); if (ReturnValue == nullptr) - Logger.log("Failed to find compilation database for " + Twine(File) + - "in overriden directory " + CompileCommandsDir.getValue()); + log(Context::empty(), "Failed to find compilation database for " + + Twine(File) + "in overriden directory " + + CompileCommandsDir.getValue()); return ReturnValue; } @@ -118,7 +119,8 @@ return CDB; } - Logger.log("Failed to find compilation database for " + Twine(File)); + 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" @@ -35,7 +36,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 +54,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> TracerKey; +static Key IDKey; +static Key OutKey; +} // namespace + void JSONOutput::writeMessage(const json::Expr &Message) { std::string S; llvm::raw_string_ostream OS(S); @@ -38,7 +44,8 @@ Outs.flush(); } -void JSONOutput::log(const Twine &Message) { +void JSONOutput::log(const Context &Ctx, const Twine &Message) { + // FIXME(ibiryukov): get rid of trace::log here. trace::log(Message); std::lock_guard Guard(StreamMutex); Logs << Message << '\n'; @@ -53,27 +60,30 @@ InputMirror->flush(); } -void RequestContext::reply(json::Expr &&Result) { +void clangd::reply(const Context &Ctx, json::Expr &&Result) { + auto ID = Ctx.get(IDKey); 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{ + + SPAN_ATTACH(*Ctx.getExisting(TracerKey), "Reply", Result); + Ctx.getExisting(OutKey)->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", +void clangd::replyError(const Context &Ctx, ErrorCode code, + const llvm::StringRef &Message) { + log(Ctx, "Error " + Twine(static_cast(code)) + ": " + Message); + SPAN_ATTACH(*Ctx.getExisting(TracerKey), "Error", (json::obj{{"code", static_cast(code)}, {"message", Message.str()}})); - if (ID) { - Out.writeMessage(json::obj{ + + if (auto ID = Ctx.get(IDKey)) { + Ctx.getExisting(OutKey)->writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", *ID}, {"error", @@ -82,12 +92,12 @@ } } -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", + SPAN_ATTACH(*Ctx.getExisting(TracerKey), "Call", (json::obj{{"method", Method.str()}, {"params", Params}})); - Out.writeMessage(json::obj{ + Ctx.getExisting(OutKey)->writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", 1}, {"method", Method}, @@ -120,8 +130,17 @@ 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 = buildCtx() + .addOpt(IDKey, ID) + .add(OutKey, &Out) + .add(TracerKey, std::move(Tracer)); + Handler(std::move(Ctx), std::move(Params)); return true; } @@ -164,9 +183,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 +205,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 +220,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 +232,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,35 @@ #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 globalLogger(). +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,29 @@ #include "Logger.h" -using namespace clang::clangd; +namespace clang { +namespace clangd { -EmptyLogger &EmptyLogger::getInstance() { - static EmptyLogger Logger; - return Logger; +namespace { +class EmptyLogger : public Logger { +public: + void log(const Context &Ctx, const llvm::Twine &Message) override {} +}; + +EmptyLogger Empty; +Logger *GlobalLogger = &Empty; +} // namespace + +LoggingSession::LoggingSession(clangd::Logger &Instance) { + assert(GlobalLogger == &Empty); + GlobalLogger = &Instance; +} + +LoggingSession::~LoggingSession() { GlobalLogger = &Empty; } + +void log(const Context &Ctx, const llvm::Twine &Message) { + GlobalLogger->log(Ctx, Message); } -void EmptyLogger::log(const llvm::Twine &Message) {} +} // namespace clangd +} // namespace clang Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -21,6 +21,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H +#include "Context.h" #include "JSONExpr.h" #include "llvm/ADT/Optional.h" #include Index: clangd/ProtocolHandlers.h =================================================================== --- clangd/ProtocolHandlers.h +++ clangd/ProtocolHandlers.h @@ -29,7 +29,7 @@ // The interface implemented by ClangLSPServer to handle incoming requests. class ProtocolCallbacks { public: - using Ctx = RequestContext; + 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; @@ -172,8 +174,7 @@ CCOpts.IncludeIneligibleResults = IncludeIneligibleResults; // Initialize and run ClangdLSPServer. ClangdLSPServer LSPServer(Out, WorkerThreadsCount, StorePreamblesInMemory, - CCOpts, ResourceDirRef, - CompileCommandsDirPath); + CCOpts, ResourceDirRef, CompileCommandsDirPath); constexpr int NoShutdownRequestErrorCode = 1; llvm::set_thread_name("clangd.main"); return LSPServer.run(std::cin) ? 0 : NoShutdownRequestErrorCode; 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" @@ -28,6 +28,7 @@ namespace clang { namespace clangd { + namespace { // Don't wait for async ops in clangd test more than that to avoid blocking @@ -123,8 +124,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 +135,8 @@ // Have to sync reparses because requests are processed on the calling // thread. - auto AddDocFuture = Server.addDocument(SourceFilename, SourceContents); + auto AddDocFuture = + Server.addDocument(SourceFilename, SourceContents, buildCtx()); auto Result = dumpASTWithoutMemoryLocs(Server, SourceFilename); @@ -187,8 +188,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 +203,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(FooCpp, SourceContents, buildCtx()); 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(FooCpp, "", buildCtx()); 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(FooCpp, SourceContents, buildCtx()); auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); @@ -233,8 +233,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 +248,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(FooCpp, SourceContents, buildCtx()); 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(FooCpp, buildCtx()); 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(FooCpp, buildCtx()); auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); @@ -282,8 +281,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,15 +294,19 @@ // 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, + Server.addDocument(FooCpp, SourceContents, buildCtx()); + EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}, CCOpts, buildCtx()) + .get() + .first.Tag, FS.Tag); EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag); FS.Tag = "321"; - Server.addDocument(FooCpp, SourceContents); + Server.addDocument(FooCpp, SourceContents, buildCtx()); EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag); - EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}, CCOpts).get().Tag, + EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}, CCOpts, buildCtx()) + .get() + .first.Tag, FS.Tag); } @@ -322,8 +324,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 +355,14 @@ // No need to sync reparses, because requests are processed on the calling // thread. - Server.addDocument(FooCpp, SourceContents); + Server.addDocument(FooCpp, SourceContents, buildCtx()); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); const auto SourceContentsWithError = R"cpp( #include std::string x; )cpp"; - Server.addDocument(FooCpp, SourceContentsWithError); + Server.addDocument(FooCpp, SourceContentsWithError, buildCtx()); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); } #endif // LLVM_ON_UNIX @@ -372,11 +373,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 +392,26 @@ // First parse files in C mode and check they produce errors. CDB.ExtraClangFlags = {"-xc"}; - Server.addDocument(FooCpp, SourceContents1); + Server.addDocument(FooCpp, SourceContents1, buildCtx()); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(FooCpp, SourceContents2); + Server.addDocument(FooCpp, SourceContents2, buildCtx()); 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(FooCpp, SourceContents1, buildCtx()); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(FooCpp, SourceContents2); + Server.addDocument(FooCpp, SourceContents2, buildCtx()); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); // But forceReparse should reparse the file with proper flags. - Server.forceReparse(FooCpp); + Server.forceReparse(FooCpp, buildCtx()); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); // Subsequent addDocument calls should finish without errors too. - Server.addDocument(FooCpp, SourceContents1); + Server.addDocument(FooCpp, SourceContents1, buildCtx()); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(FooCpp, SourceContents2); + Server.addDocument(FooCpp, SourceContents2, buildCtx()); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); } @@ -501,7 +501,7 @@ unsigned RequestsWithErrors = 0; bool LastContentsHadErrors = false; bool FileIsRemoved = true; - std::future LastRequestFuture; + std::future LastRequestFuture; }; std::vector ReqStats; @@ -513,8 +513,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 +528,7 @@ // Some helpers. auto UpdateStatsOnAddDocument = [&](unsigned FileIndex, bool HadErrors, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; if (HadErrors) @@ -542,7 +541,7 @@ }; auto UpdateStatsOnRemoveDocument = [&](unsigned FileIndex, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; Stats.FileIsRemoved = true; @@ -550,7 +549,7 @@ }; auto UpdateStatsOnForceReparse = [&](unsigned FileIndex, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; Stats.LastRequestFuture = std::move(Future); @@ -562,9 +561,11 @@ auto AddDocument = [&](unsigned FileIndex) { bool ShouldHaveErrors = ShouldHaveErrorsDist(RandGen); - auto Future = Server.addDocument( - FilePaths[FileIndex], ShouldHaveErrors ? SourceContentsWithErrors - : SourceContentsWithoutErrors); + auto Future = + Server.addDocument(FilePaths[FileIndex], + ShouldHaveErrors ? SourceContentsWithErrors + : SourceContentsWithoutErrors, + buildCtx()); 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(FilePaths[FileIndex], buildCtx()); UpdateStatsOnForceReparse(FileIndex, std::move(Future)); }; @@ -590,7 +591,7 @@ if (ReqStats[FileIndex].FileIsRemoved) AddDocument(FileIndex); - auto Future = Server.removeDocument(FilePaths[FileIndex]); + auto Future = Server.removeDocument(FilePaths[FileIndex], buildCtx()); UpdateStatsOnRemoveDocument(FileIndex, std::move(Future)); }; @@ -609,7 +610,7 @@ // same file. Server .codeComplete(FilePaths[FileIndex], Pos, - clangd::CodeCompleteOptions()) + clangd::CodeCompleteOptions(), buildCtx()) .wait(); }; @@ -620,7 +621,8 @@ AddDocument(FileIndex); Position Pos{LineDist(RandGen), ColumnDist(RandGen)}; - ASSERT_TRUE(!!Server.findDefinitions(FilePaths[FileIndex], Pos)); + ASSERT_TRUE(!!Server.findDefinitions(FilePaths[FileIndex], Pos, + Context::empty())); }; std::vector> AsyncRequests = { @@ -676,8 +678,7 @@ MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); auto SourceContents = R"cpp( #include "foo.h" @@ -802,12 +803,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(FooCpp, SourceContentsWithErrors, buildCtx()); StartSecondReparse.wait(); - auto Future = Server.addDocument(FooCpp, SourceContentsWithoutErrors); + auto Future = + Server.addDocument(FooCpp, SourceContentsWithoutErrors, buildCtx()); Future.wait(); } Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "ClangdServer.h" #include "Compiler.h" +#include "Context.h" #include "Protocol.h" #include "TestFS.h" #include "gtest/gtest.h" @@ -74,8 +75,7 @@ MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); auto FooCpp = getVirtualTestFilePath("foo.cpp"); const auto SourceContents = R"cpp( @@ -101,11 +101,13 @@ // No need to sync reparses here as there are no asserts on diagnostics (or // other async operations). - Server.addDocument(FooCpp, SourceContents); + Server.addDocument(FooCpp, SourceContents, buildCtx()); { auto CodeCompletionResults1 = - Server.codeComplete(FooCpp, CompletePos, CCOpts, None).get().Value; + Server.codeComplete(FooCpp, CompletePos, CCOpts, buildCtx(), None) + .get() + .first.Value; EXPECT_TRUE(ContainsItem(CodeCompletionResults1, "aba")); EXPECT_FALSE(ContainsItem(CodeCompletionResults1, "cbc")); } @@ -113,17 +115,19 @@ { auto CodeCompletionResultsOverriden = Server - .codeComplete(FooCpp, CompletePos, CCOpts, + .codeComplete(FooCpp, CompletePos, CCOpts, buildCtx(), StringRef(OverridenSourceContents)) .get() - .Value; + .first.Value; EXPECT_TRUE(ContainsItem(CodeCompletionResultsOverriden, "cbc")); EXPECT_FALSE(ContainsItem(CodeCompletionResultsOverriden, "aba")); } { auto CodeCompletionResults2 = - Server.codeComplete(FooCpp, CompletePos, CCOpts, None).get().Value; + Server.codeComplete(FooCpp, CompletePos, CCOpts, buildCtx(), None) + .get() + .first.Value; EXPECT_TRUE(ContainsItem(CodeCompletionResults2, "aba")); EXPECT_FALSE(ContainsItem(CodeCompletionResults2, "cbc")); } @@ -135,8 +139,7 @@ CDB.ExtraClangFlags.push_back("-xc++"); IgnoreDiagnostics DiagConsumer; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); auto FooCpp = getVirtualTestFilePath("foo.cpp"); FS.Files[FooCpp] = ""; @@ -150,7 +153,7 @@ int main() { ClassWithMembers().{complete} } )cpp", "complete"); - Server.addDocument(FooCpp, Completion.Text); + Server.addDocument(FooCpp, Completion.Text, buildCtx()); clangd::CodeCompleteOptions Opts; Opts.Limit = 2; @@ -158,9 +161,9 @@ /// For after-dot completion we must always get consistent results. auto Results = Server .codeComplete(FooCpp, Completion.MarkerPos, Opts, - StringRef(Completion.Text)) + buildCtx(), StringRef(Completion.Text)) .get() - .Value; + .first.Value; EXPECT_TRUE(Results.isIncomplete); EXPECT_EQ(Opts.Limit, Results.items.size()); @@ -175,8 +178,7 @@ CDB.ExtraClangFlags.push_back("-xc++"); IgnoreDiagnostics DiagConsumer; ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); auto FooCpp = getVirtualTestFilePath("foo.cpp"); FS.Files[FooCpp] = ""; @@ -194,12 +196,13 @@ StringWithPos Completion = parseTextMarker( formatv("{0} int main() { {1}{{complete}} }", Body, Query).str(), "complete"); - Server.addDocument(FooCpp, Completion.Text); + Server.addDocument(FooCpp, Completion.Text, buildCtx()); return Server .codeComplete(FooCpp, Completion.MarkerPos, - clangd::CodeCompleteOptions(), StringRef(Completion.Text)) + clangd::CodeCompleteOptions(), buildCtx(), + StringRef(Completion.Text)) .get() - .Value; + .first.Value; }; auto Foba = Complete("S().Foba"); @@ -291,11 +294,10 @@ auto TestWithOpts = [&](clangd::CodeCompleteOptions Opts) { ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - EmptyLogger::getInstance()); + /*StorePreamblesInMemory=*/true); // No need to sync reparses here as there are no asserts on diagnostics (or // other async operations). - Server.addDocument(FooCpp, GlobalCompletion.Text); + Server.addDocument(FooCpp, GlobalCompletion.Text, buildCtx()); StringRef MethodItemText = Opts.EnableSnippets ? "method()" : "method"; StringRef GlobalFuncItemText = @@ -303,11 +305,12 @@ /// For after-dot completion we must always get consistent results. { - auto Results = Server - .codeComplete(FooCpp, MemberCompletion.MarkerPos, Opts, - StringRef(MemberCompletion.Text)) - .get() - .Value; + auto Results = + Server + .codeComplete(FooCpp, MemberCompletion.MarkerPos, Opts, + buildCtx(), StringRef(MemberCompletion.Text)) + .get() + .first.Value; // Class members. The only items that must be present in after-dor // completion. @@ -339,11 +342,12 @@ } // Global completion differs based on the Opts that were passed. { - auto Results = Server - .codeComplete(FooCpp, GlobalCompletion.MarkerPos, Opts, - StringRef(GlobalCompletion.Text)) - .get() - .Value; + auto Results = + Server + .codeComplete(FooCpp, GlobalCompletion.MarkerPos, Opts, + buildCtx(), StringRef(GlobalCompletion.Text)) + .get() + .first.Value; // Class members. Should never be present in global completions. EXPECT_FALSE(ContainsItem(Results, MethodItemText));