Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -50,32 +50,30 @@ private: // Implement DiagnosticsConsumer. virtual void - onDiagnosticsReady(const Context &Ctx, PathRef File, + onDiagnosticsReady(PathRef File, Tagged> Diagnostics) override; // Implement ProtocolCallbacks. - void onInitialize(Ctx C, InitializeParams &Params) override; - void onShutdown(Ctx C, ShutdownParams &Params) override; - void onExit(Ctx C, ExitParams &Params) override; - void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) override; - void onDocumentDidChange(Ctx C, DidChangeTextDocumentParams &Params) override; - void onDocumentDidClose(Ctx C, DidCloseTextDocumentParams &Params) override; + void onInitialize(InitializeParams &Params) override; + void onShutdown(ShutdownParams &Params) override; + void onExit(ExitParams &Params) override; + void onDocumentDidOpen(DidOpenTextDocumentParams &Params) override; + void onDocumentDidChange(DidChangeTextDocumentParams &Params) override; + void onDocumentDidClose(DidCloseTextDocumentParams &Params) override; void - onDocumentOnTypeFormatting(Ctx C, - DocumentOnTypeFormattingParams &Params) override; + onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams &Params) override; void - onDocumentRangeFormatting(Ctx C, - DocumentRangeFormattingParams &Params) override; - void onDocumentFormatting(Ctx C, DocumentFormattingParams &Params) override; - void onCodeAction(Ctx C, CodeActionParams &Params) override; - void onCompletion(Ctx C, TextDocumentPositionParams &Params) override; - void onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) override; - void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) override; - void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) override; - void onDocumentHighlight(Ctx C, TextDocumentPositionParams &Params) override; - void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) override; - void onCommand(Ctx C, ExecuteCommandParams &Params) override; - void onRename(Ctx C, RenameParams &Parames) override; + onDocumentRangeFormatting(DocumentRangeFormattingParams &Params) override; + void onDocumentFormatting(DocumentFormattingParams &Params) override; + void onCodeAction(CodeActionParams &Params) override; + void onCompletion(TextDocumentPositionParams &Params) override; + void onSignatureHelp(TextDocumentPositionParams &Params) override; + void onGoToDefinition(TextDocumentPositionParams &Params) override; + void onSwitchSourceHeader(TextDocumentIdentifier &Params) override; + void onDocumentHighlight(TextDocumentPositionParams &Params) override; + void onFileEvent(DidChangeWatchedFilesParams &Params) override; + void onCommand(ExecuteCommandParams &Params) override; + void onRename(RenameParams &Parames) override; std::vector getFixIts(StringRef File, const clangd::Diagnostic &D); Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -46,8 +46,8 @@ } // namespace -void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) { - reply(C, json::obj{ +void ClangdLSPServer::onInitialize(InitializeParams &Params) { + reply(json::obj{ {{"capabilities", json::obj{ {"textDocumentSync", 1}, @@ -82,38 +82,35 @@ Server.setRootPath(*Params.rootPath); } -void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) { +void ClangdLSPServer::onShutdown(ShutdownParams &Params) { // Do essentially nothing, just say we're ready to exit. ShutdownRequestReceived = true; - reply(C, nullptr); + reply(nullptr); } -void ClangdLSPServer::onExit(Ctx C, ExitParams &Params) { IsDone = true; } +void ClangdLSPServer::onExit(ExitParams &Params) { IsDone = true; } -void ClangdLSPServer::onDocumentDidOpen(Ctx C, - DidOpenTextDocumentParams &Params) { +void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) { if (Params.metadata && !Params.metadata->extraFlags.empty()) CDB.setExtraFlagsForFile(Params.textDocument.uri.file, std::move(Params.metadata->extraFlags)); - Server.addDocument(std::move(C), Params.textDocument.uri.file, - Params.textDocument.text); + Server.addDocument(Params.textDocument.uri.file, Params.textDocument.text); } -void ClangdLSPServer::onDocumentDidChange(Ctx C, - DidChangeTextDocumentParams &Params) { +void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams &Params) { if (Params.contentChanges.size() != 1) - return replyError(C, ErrorCode::InvalidParams, + return replyError(ErrorCode::InvalidParams, "can only apply one change at a time"); // We only support full syncing right now. - Server.addDocument(std::move(C), Params.textDocument.uri.file, + Server.addDocument(Params.textDocument.uri.file, Params.contentChanges[0].text); } -void ClangdLSPServer::onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) { +void ClangdLSPServer::onFileEvent(DidChangeWatchedFilesParams &Params) { Server.onFileEvent(Params); } -void ClangdLSPServer::onCommand(Ctx C, ExecuteCommandParams &Params) { +void ClangdLSPServer::onCommand(ExecuteCommandParams &Params) { if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND && Params.workspaceEdit) { // The flow for "apply-fix" : @@ -127,31 +124,31 @@ ApplyWorkspaceEditParams ApplyEdit; ApplyEdit.edit = *Params.workspaceEdit; - reply(C, "Fix applied."); + reply("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. - call(C, "workspace/applyEdit", ApplyEdit); + call("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. replyError( - C, ErrorCode::InvalidParams, + ErrorCode::InvalidParams, llvm::formatv("Unsupported command \"{0}\".", Params.command).str()); } } -void ClangdLSPServer::onRename(Ctx C, RenameParams &Params) { +void ClangdLSPServer::onRename(RenameParams &Params) { auto File = Params.textDocument.uri.file; auto Code = Server.getDocument(File); if (!Code) - return replyError(C, ErrorCode::InvalidParams, + return replyError(ErrorCode::InvalidParams, "onRename called for non-added file"); - auto Replacements = Server.rename(C, File, Params.position, Params.newName); + auto Replacements = Server.rename(File, Params.position, Params.newName); if (!Replacements) { - replyError(C, ErrorCode::InternalError, + replyError(ErrorCode::InternalError, llvm::toString(Replacements.takeError())); return; } @@ -159,68 +156,66 @@ std::vector Edits = replacementsToEdits(*Code, *Replacements); WorkspaceEdit WE; WE.changes = {{Params.textDocument.uri.uri(), Edits}}; - reply(C, WE); + reply(WE); } -void ClangdLSPServer::onDocumentDidClose(Ctx C, - DidCloseTextDocumentParams &Params) { - Server.removeDocument(std::move(C), Params.textDocument.uri.file); +void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams &Params) { + Server.removeDocument(Params.textDocument.uri.file); } void ClangdLSPServer::onDocumentOnTypeFormatting( - Ctx C, DocumentOnTypeFormattingParams &Params) { + DocumentOnTypeFormattingParams &Params) { auto File = Params.textDocument.uri.file; auto Code = Server.getDocument(File); if (!Code) - return replyError(C, ErrorCode::InvalidParams, + return replyError(ErrorCode::InvalidParams, "onDocumentOnTypeFormatting called for non-added file"); auto ReplacementsOrError = Server.formatOnType(*Code, File, Params.position); if (ReplacementsOrError) - reply(C, json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); + reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); else - replyError(C, ErrorCode::UnknownErrorCode, + replyError(ErrorCode::UnknownErrorCode, llvm::toString(ReplacementsOrError.takeError())); } void ClangdLSPServer::onDocumentRangeFormatting( - Ctx C, DocumentRangeFormattingParams &Params) { + DocumentRangeFormattingParams &Params) { auto File = Params.textDocument.uri.file; auto Code = Server.getDocument(File); if (!Code) - return replyError(C, ErrorCode::InvalidParams, + return replyError(ErrorCode::InvalidParams, "onDocumentRangeFormatting called for non-added file"); auto ReplacementsOrError = Server.formatRange(*Code, File, Params.range); if (ReplacementsOrError) - reply(C, json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); + reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); else - replyError(C, ErrorCode::UnknownErrorCode, + replyError(ErrorCode::UnknownErrorCode, llvm::toString(ReplacementsOrError.takeError())); } -void ClangdLSPServer::onDocumentFormatting(Ctx C, - DocumentFormattingParams &Params) { +void ClangdLSPServer::onDocumentFormatting(DocumentFormattingParams &Params) { auto File = Params.textDocument.uri.file; auto Code = Server.getDocument(File); if (!Code) - return replyError(C, ErrorCode::InvalidParams, + return replyError(ErrorCode::InvalidParams, "onDocumentFormatting called for non-added file"); auto ReplacementsOrError = Server.formatFile(*Code, File); if (ReplacementsOrError) - reply(C, json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); + reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); else - replyError(C, ErrorCode::UnknownErrorCode, + replyError(ErrorCode::UnknownErrorCode, llvm::toString(ReplacementsOrError.takeError())); } -void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) { +void ClangdLSPServer::onCodeAction(CodeActionParams &Params) { // We provide a code action for each diagnostic at the requested location // which has FixIts available. auto Code = Server.getDocument(Params.textDocument.uri.file); if (!Code) - return replyError(C, ErrorCode::InvalidParams, + return replyError(ErrorCode::InvalidParams, "onCodeAction called for non-added file"); json::ary Commands; @@ -236,67 +231,53 @@ }); } } - reply(C, std::move(Commands)); + reply(std::move(Commands)); } -void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) { - 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::onCompletion(TextDocumentPositionParams &Params) { + Server.codeComplete(Params.textDocument.uri.file, + Position{Params.position.line, Params.position.character}, + CCOpts, + [](Tagged List) { reply(List.Value); }); } -void ClangdLSPServer::onSignatureHelp(Ctx C, - TextDocumentPositionParams &Params) { +void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) { auto SignatureHelp = Server.signatureHelp( - C, Params.textDocument.uri.file, + Params.textDocument.uri.file, Position{Params.position.line, Params.position.character}); if (!SignatureHelp) - return replyError(C, ErrorCode::InvalidParams, + return replyError(ErrorCode::InvalidParams, llvm::toString(SignatureHelp.takeError())); - reply(C, SignatureHelp->Value); + reply(SignatureHelp->Value); } -void ClangdLSPServer::onGoToDefinition(Ctx C, - TextDocumentPositionParams &Params) { +void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams &Params) { auto Items = Server.findDefinitions( - C, Params.textDocument.uri.file, + Params.textDocument.uri.file, Position{Params.position.line, Params.position.character}); if (!Items) - return replyError(C, ErrorCode::InvalidParams, + return replyError(ErrorCode::InvalidParams, llvm::toString(Items.takeError())); - reply(C, json::ary(Items->Value)); + reply(json::ary(Items->Value)); } -void ClangdLSPServer::onSwitchSourceHeader(Ctx C, - TextDocumentIdentifier &Params) { +void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier &Params) { llvm::Optional Result = Server.switchSourceHeader(Params.uri.file); - reply(C, Result ? URI::createFile(*Result).toString() : ""); + reply(Result ? URI::createFile(*Result).toString() : ""); } -void ClangdLSPServer::onDocumentHighlight(Ctx C, - TextDocumentPositionParams &Params) { - +void ClangdLSPServer::onDocumentHighlight(TextDocumentPositionParams &Params) { auto Highlights = Server.findDocumentHighlights( - C, Params.textDocument.uri.file, + Params.textDocument.uri.file, Position{Params.position.line, Params.position.character}); if (!Highlights) { - replyError(C, ErrorCode::InternalError, + replyError(ErrorCode::InternalError, llvm::toString(Highlights.takeError())); return; } - reply(C, json::ary(Highlights->Value)); + reply(json::ary(Highlights->Value)); } ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount, @@ -315,8 +296,8 @@ assert(!IsDone && "Run was called before"); // Set up JSONRPCDispatcher. - JSONRPCDispatcher Dispatcher([](Context Ctx, const json::Expr &Params) { - replyError(Ctx, ErrorCode::MethodNotFound, "method not found"); + JSONRPCDispatcher Dispatcher([](const json::Expr &Params) { + replyError(ErrorCode::MethodNotFound, "method not found"); }); registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this); @@ -346,8 +327,7 @@ } void ClangdLSPServer::onDiagnosticsReady( - const Context &Ctx, PathRef File, - Tagged> Diagnostics) { + PathRef File, Tagged> Diagnostics) { json::ary DiagnosticsJSON; DiagnosticToReplacementMap LocalFixIts; // Temporary storage Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -71,7 +71,7 @@ /// Called by ClangdServer when \p Diagnostics for \p File are ready. virtual void - onDiagnosticsReady(const Context &Ctx, PathRef File, + onDiagnosticsReady(PathRef File, Tagged> Diagnostics) = 0; }; @@ -95,8 +95,6 @@ getTaggedFileSystem(PathRef File) override; }; -class ClangdServer; - /// Provides API to manage ASTs for a collection of C++ files and request /// various language features. /// Currently supports async diagnostics, code completion, formatting and goto @@ -156,30 +154,26 @@ /// diagnostics) is finished. /// FIXME: don't return futures here, LSP does not require a response for this /// request. - std::future addDocument(Context Ctx, PathRef File, - StringRef Contents); + std::future addDocument(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. /// FIXME: don't return futures here, LSP does not require a response for this /// request. - std::future removeDocument(Context Ctx, PathRef File); + std::future removeDocument(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. /// FIXME: don't return futures here, LSP does not require a response for this /// request. - std::future forceReparse(Context Ctx, PathRef File); + std::future forceReparse(PathRef File); - /// DEPRECATED. Please use a callback-based version, this API is deprecated - /// and will soon be removed. - /// /// Run code completion for \p File at \p Pos. - /// - /// Request is processed asynchronously. You can use the returned future to - /// wait for the results of the async request. + /// Request is processed asynchronously. /// /// If \p OverridenContents is not None, they will used only for code /// completion, i.e. no diagnostics update will be scheduled and a draft for @@ -190,18 +184,18 @@ /// 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(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(Context Ctx, PathRef File, Position Pos, + void codeComplete(PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + UniqueFunction)> Callback, + llvm::Optional OverridenContents = llvm::None, + IntrusiveRefCntPtr *UsedFS = nullptr); + + /// DEPRECATED: Please use the callback-based version of codeComplete. + std::future> + codeComplete(PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, - UniqueFunction)> Callback, llvm::Optional OverridenContents = llvm::None, IntrusiveRefCntPtr *UsedFS = nullptr); @@ -213,13 +207,13 @@ /// vfs::FileSystem used for signature help. This method should only be called /// for currently tracked files. llvm::Expected> - signatureHelp(const Context &Ctx, PathRef File, Position Pos, + signatureHelp(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(const Context &Ctx, PathRef File, Position Pos); + llvm::Expected>> findDefinitions(PathRef File, + Position Pos); /// Helper function that returns a path to the corresponding source file when /// given a header file and vice versa. @@ -227,7 +221,7 @@ /// Get document highlights for a given position. llvm::Expected>> - findDocumentHighlights(const Context &Ctx, PathRef File, Position Pos); + findDocumentHighlights(PathRef File, Position Pos); /// Run formatting for \p Rng inside \p File with content \p Code. llvm::Expected formatRange(StringRef Code, @@ -244,8 +238,7 @@ /// Rename all occurrences of the symbol at the \p Pos in \p File to /// \p NewName. - Expected> rename(const Context &Ctx, - PathRef File, Position Pos, + Expected> rename(PathRef File, Position Pos, llvm::StringRef NewName); /// Gets current document contents for \p File. Returns None if \p File is not @@ -277,8 +270,8 @@ formatCode(llvm::StringRef Code, PathRef File, ArrayRef Ranges); - std::future - scheduleReparseAndDiags(Context Ctx, PathRef File, VersionedDraft Contents, + std::future + scheduleReparseAndDiags(PathRef File, VersionedDraft Contents, Tagged> TaggedFS); CompileArgsCache CompileArgs; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -116,11 +116,11 @@ // is parsed. // FIXME(ioeric): this can be slow and we may be able to index on less // critical paths. - WorkScheduler( - AsyncThreadsCount, StorePreamblesInMemory, - FileIdx ? [this](const Context &Ctx, PathRef Path, - ParsedAST *AST) { FileIdx->update(Ctx, Path, AST); } - : ASTParsedCallback()) { + WorkScheduler(AsyncThreadsCount, StorePreamblesInMemory, + FileIdx + ? [this](PathRef Path, + ParsedAST *AST) { FileIdx->update(Path, AST); } + : ASTParsedCallback()) { if (FileIdx && StaticIdx) { MergedIndex = mergeIndex(FileIdx.get(), StaticIdx); Index = MergedIndex.get(); @@ -139,35 +139,33 @@ this->RootPath = NewRootPath; } -std::future ClangdServer::addDocument(Context Ctx, PathRef File, - StringRef Contents) { +std::future ClangdServer::addDocument(PathRef File, StringRef Contents) { DocVersion Version = DraftMgr.updateDraft(File, Contents); auto TaggedFS = FSProvider.getTaggedFileSystem(File); - return scheduleReparseAndDiags(std::move(Ctx), File, - VersionedDraft{Version, Contents.str()}, + return scheduleReparseAndDiags(File, VersionedDraft{Version, Contents.str()}, std::move(TaggedFS)); } -std::future ClangdServer::removeDocument(Context Ctx, PathRef File) { +std::future ClangdServer::removeDocument(PathRef File) { DraftMgr.removeDraft(File); CompileArgs.invalidate(File); - std::promise DonePromise; - std::future DoneFuture = DonePromise.get_future(); + std::promise DonePromise; + std::future DoneFuture = DonePromise.get_future(); auto Callback = BindWithForward( - [](Context Ctx, std::promise DonePromise, llvm::Error Err) { + [](std::promise DonePromise, llvm::Error Err) { if (Err) ignoreError(std::move(Err)); - DonePromise.set_value(std::move(Ctx)); + DonePromise.set_value(); }, - std::move(Ctx), std::move(DonePromise)); + std::move(DonePromise)); WorkScheduler.remove(File, std::move(Callback)); return DoneFuture; } -std::future ClangdServer::forceReparse(Context Ctx, PathRef File) { +std::future ClangdServer::forceReparse(PathRef File) { auto FileContents = DraftMgr.getDraft(File); assert(FileContents.Draft && "forceReparse() was called for non-added document"); @@ -177,38 +175,34 @@ CompileArgs.invalidate(File); auto TaggedFS = FSProvider.getTaggedFileSystem(File); - return scheduleReparseAndDiags(std::move(Ctx), File, std::move(FileContents), + return scheduleReparseAndDiags(File, std::move(FileContents), std::move(TaggedFS)); } -std::future>> -ClangdServer::codeComplete(Context Ctx, PathRef File, Position Pos, +std::future> +ClangdServer::codeComplete(PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { - using ResultType = std::pair>; - - std::promise ResultPromise; - - auto Callback = [](std::promise ResultPromise, Context Ctx, + std::promise> ResultPromise; + auto Callback = [](std::promise> ResultPromise, Tagged Result) -> void { - ResultPromise.set_value({std::move(Ctx), std::move(Result)}); + ResultPromise.set_value(std::move(Result)); }; - std::future ResultFuture = ResultPromise.get_future(); - codeComplete(std::move(Ctx), File, Pos, Opts, + auto ResultFuture = ResultPromise.get_future(); + codeComplete(File, Pos, Opts, BindWithForward(Callback, std::move(ResultPromise)), OverridenContents, UsedFS); return ResultFuture; } void ClangdServer::codeComplete( - Context Ctx, PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, - UniqueFunction)> Callback, + PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, + UniqueFunction)> Callback, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { - using CallbackType = UniqueFunction)>; + using CallbackType = UniqueFunction)>; auto TaggedFS = FSProvider.getTaggedFileSystem(File); if (UsedFS) @@ -231,8 +225,8 @@ // Copy PCHs to avoid accessing this->PCHs concurrently std::shared_ptr PCHs = this->PCHs; auto Task = [PCHs, Pos, TaggedFS, CodeCompleteOpts]( - Context Ctx, std::string Contents, Path File, - CallbackType Callback, llvm::Expected IP) { + std::string Contents, Path File, CallbackType Callback, + llvm::Expected IP) { assert(IP && "error when trying to read preamble for codeComplete"); auto PreambleData = IP->Preamble; auto &Command = IP->Inputs.CompileCommand; @@ -240,20 +234,19 @@ // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CompletionList Result = clangd::codeComplete( - Ctx, File, Command, PreambleData ? &PreambleData->Preamble : nullptr, + File, Command, PreambleData ? &PreambleData->Preamble : nullptr, Contents, Pos, TaggedFS.Value, PCHs, CodeCompleteOpts); - Callback(std::move(Ctx), - make_tagged(std::move(Result), std::move(TaggedFS.Tag))); + Callback(make_tagged(std::move(Result), std::move(TaggedFS.Tag))); }; - WorkScheduler.runWithPreamble( - File, BindWithForward(Task, std::move(Ctx), std::move(Contents), - File.str(), std::move(Callback))); + WorkScheduler.runWithPreamble(File, BindWithForward(Task, std::move(Contents), + File.str(), + std::move(Callback))); } llvm::Expected> -ClangdServer::signatureHelp(const Context &Ctx, PathRef File, Position Pos, +ClangdServer::signatureHelp(PathRef File, Position Pos, llvm::Optional OverridenContents, IntrusiveRefCntPtr *UsedFS) { auto TaggedFS = FSProvider.getTaggedFileSystem(File); @@ -272,7 +265,7 @@ Contents = std::move(*Latest.Draft); } - auto Action = [=, &Ctx](llvm::Expected IP) + auto Action = [=](llvm::Expected IP) -> Expected> { if (!IP) return IP.takeError(); @@ -280,7 +273,7 @@ auto &Command = IP->Inputs.CompileCommand; return make_tagged( - clangd::signatureHelp(Ctx, File, Command, + clangd::signatureHelp(File, Command, PreambleData ? &PreambleData->Preamble : nullptr, Contents, Pos, TaggedFS.Value, PCHs), TaggedFS.Tag); @@ -316,8 +309,7 @@ } Expected> -ClangdServer::rename(const Context &Ctx, PathRef File, Position Pos, - llvm::StringRef NewName) { +ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName) { using RetType = Expected>; auto Action = [=](Expected InpAST) -> RetType { if (!InpAST) @@ -396,14 +388,14 @@ } llvm::Expected>> -ClangdServer::findDefinitions(const Context &Ctx, PathRef File, Position Pos) { +ClangdServer::findDefinitions(PathRef File, Position Pos) { auto TaggedFS = FSProvider.getTaggedFileSystem(File); using RetType = llvm::Expected>>; - auto Action = [=, &Ctx](llvm::Expected InpAST) -> RetType { + auto Action = [=](llvm::Expected InpAST) -> RetType { if (!InpAST) return InpAST.takeError(); - auto Result = clangd::findDefinitions(Ctx, InpAST->AST, Pos); + auto Result = clangd::findDefinitions(InpAST->AST, Pos); return make_tagged(std::move(Result), TaggedFS.Tag); }; return blockingRunWithAST(WorkScheduler, File, Action); @@ -483,8 +475,7 @@ } llvm::Expected>> -ClangdServer::findDocumentHighlights(const Context &Ctx, PathRef File, - Position Pos) { +ClangdServer::findDocumentHighlights(PathRef File, Position Pos) { auto FileContents = DraftMgr.getDraft(File); if (!FileContents.Draft) return llvm::make_error( @@ -494,17 +485,17 @@ auto TaggedFS = FSProvider.getTaggedFileSystem(File); using RetType = llvm::Expected>>; - auto Action = [=, &Ctx](llvm::Expected InpAST) -> RetType { + auto Action = [=](llvm::Expected InpAST) -> RetType { if (!InpAST) return InpAST.takeError(); - auto Result = clangd::findDocumentHighlights(Ctx, InpAST->AST, Pos); + auto Result = clangd::findDocumentHighlights(InpAST->AST, Pos); return make_tagged(std::move(Result), TaggedFS.Tag); }; return blockingRunWithAST(WorkScheduler, File, Action); } -std::future ClangdServer::scheduleReparseAndDiags( - Context Ctx, PathRef File, VersionedDraft Contents, +std::future ClangdServer::scheduleReparseAndDiags( + PathRef File, VersionedDraft Contents, Tagged> TaggedFS) { tooling::CompileCommand Command = CompileArgs.getCompileCommand(File); @@ -514,14 +505,12 @@ Path FileStr = File.str(); VFSTag Tag = std::move(TaggedFS.Tag); - std::promise DonePromise; - std::future DoneFuture = DonePromise.get_future(); + std::promise DonePromise; + std::future DoneFuture = DonePromise.get_future(); - auto Callback = [this, Version, FileStr, - Tag](std::promise DonePromise, Context Ctx, - OptDiags Diags) { - auto Guard = - llvm::make_scope_exit([&]() { DonePromise.set_value(std::move(Ctx)); }); + auto Callback = [this, Version, FileStr, Tag](std::promise DonePromise, + OptDiags Diags) { + auto Guard = llvm::make_scope_exit([&]() { DonePromise.set_value(); }); if (!Diags) return; // A new reparse was requested before this one completed. @@ -538,10 +527,10 @@ LastReportedDiagsVersion = Version; DiagConsumer.onDiagnosticsReady( - Ctx, FileStr, make_tagged(std::move(*Diags), std::move(Tag))); + FileStr, make_tagged(std::move(*Diags), std::move(Tag))); }; - WorkScheduler.update(std::move(Ctx), File, + WorkScheduler.update(File, ParseInputs{std::move(Command), std::move(TaggedFS.Value), std::move(*Contents.Draft)}, Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -10,7 +10,6 @@ #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" @@ -71,7 +70,7 @@ /// Attempts to run Clang and store parsed AST. If \p Preamble is non-null /// it is reused during parsing. static llvm::Optional - Build(const Context &Ctx, std::unique_ptr CI, + Build(std::unique_ptr CI, std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, @@ -148,8 +147,7 @@ mutable llvm::Optional AST; }; -using ASTParsedCallback = - std::function; +using ASTParsedCallback = std::function; /// Manages resources, required by clangd. Allows to rebuild file with new /// contents, and provides AST and Preamble for it. @@ -186,8 +184,7 @@ /// Returns a list of diagnostics or a llvm::None, if another rebuild was /// requested in parallel (effectively cancelling this rebuild) before /// diagnostics were produced. - llvm::Optional> rebuild(const Context &Ctx, - ParseInputs &&Inputs); + llvm::Optional> rebuild(ParseInputs &&Inputs); /// Schedule a rebuild and return a deferred computation that will finish the /// rebuild, that can be called on a different thread. @@ -203,7 +200,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>(const Context &)> + UniqueFunction>()> deferRebuild(ParseInputs &&Inputs); /// Returns a future to get the most fresh PreambleData for a file. The Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -229,8 +229,7 @@ } llvm::Optional -ParsedAST::Build(const Context &Ctx, - std::unique_ptr CI, +ParsedAST::Build(std::unique_ptr CI, std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, @@ -254,12 +253,12 @@ auto Action = llvm::make_unique(); const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0]; if (!Action->BeginSourceFile(*Clang, MainInput)) { - log(Ctx, "BeginSourceFile() failed when building AST for " + - MainInput.getFile()); + log("BeginSourceFile() failed when building AST for " + + MainInput.getFile()); return llvm::None; } if (!Action->Execute()) - log(Ctx, "Execute() failed when building AST for " + MainInput.getFile()); + log("Execute() failed when building AST for " + MainInput.getFile()); // UnitDiagsConsumer is local, we can not store it in CompilerInstance that // has a longer lifetime. @@ -387,8 +386,7 @@ RebuildCounter(0), RebuildInProgress(false), ASTMemUsage(0), PreambleMemUsage(0), PCHs(std::move(PCHs)), ASTCallback(std::move(ASTCallback)) { - // FIXME(ibiryukov): we should pass a proper Context here. - log(Context::empty(), "Created CppFile for " + FileName); + log("Created CppFile for " + FileName); std::lock_guard Lock(Mutex); LatestAvailablePreamble = nullptr; @@ -442,11 +440,11 @@ } llvm::Optional> -CppFile::rebuild(const Context &Ctx, ParseInputs &&Inputs) { - return deferRebuild(std::move(Inputs))(Ctx); +CppFile::rebuild(ParseInputs &&Inputs) { + return deferRebuild(std::move(Inputs))(); } -UniqueFunction>(const Context &)> +UniqueFunction>()> CppFile::deferRebuild(ParseInputs &&Inputs) { std::shared_ptr OldPreamble; std::shared_ptr PCHs; @@ -483,13 +481,11 @@ std::shared_ptr That = shared_from_this(); auto FinishRebuild = [OldPreamble, RequestRebuildCounter, PCHs, - That](ParseInputs Inputs, - const Context &Ctx) mutable /* to allow changing OldPreamble. */ + That](ParseInputs Inputs) mutable /* to allow changing OldPreamble. */ -> llvm::Optional> { - log(Context::empty(), - "Rebuilding file " + That->FileName + " with command [" + - Inputs.CompileCommand.Directory + "] " + - llvm::join(Inputs.CompileCommand.CommandLine, " ")); + log("Rebuilding file " + That->FileName + " with command [" + + Inputs.CompileCommand.Directory + "] " + + llvm::join(Inputs.CompileCommand.CommandLine, " ")); // Only one execution of this method is possible at a time. // RebuildGuard will wait for any ongoing rebuilds to finish and will put us @@ -532,16 +528,16 @@ if (OldPreamble && OldPreamble->Preamble.CanReuse(*CI, ContentsBuffer.get(), Bounds, Inputs.FS.get())) { - log(Ctx, "Reusing preamble for file " + Twine(That->FileName)); + log("Reusing preamble for file " + Twine(That->FileName)); return OldPreamble; } - log(Ctx, "Premble for file " + Twine(That->FileName) + - " cannot be reused. Attempting to rebuild it."); + log("Premble for file " + Twine(That->FileName) + + " cannot be reused. Attempting to rebuild it."); // We won't need the OldPreamble anymore, release it so it can be // deleted (if there are no other references to it). OldPreamble.reset(); - trace::Span Tracer(Ctx, "Preamble"); + trace::Span Tracer("Preamble"); SPAN_ATTACH(Tracer, "File", That->FileName); std::vector PreambleDiags; StoreDiagsConsumer PreambleDiagnosticsConsumer(/*ref*/ PreambleDiags); @@ -566,17 +562,15 @@ CI->getFrontendOpts().SkipFunctionBodies = false; if (BuiltPreamble) { - log(Tracer.Ctx, "Built preamble of size " + - Twine(BuiltPreamble->getSize()) + " for file " + - Twine(That->FileName)); + log("Built preamble of size " + Twine(BuiltPreamble->getSize()) + + " for file " + Twine(That->FileName)); return std::make_shared( std::move(*BuiltPreamble), SerializedDeclsCollector.takeTopLevelDeclIDs(), std::move(PreambleDiags)); } else { - log(Tracer.Ctx, - "Could not build a preamble for file " + Twine(That->FileName)); + log("Could not build a preamble for file " + Twine(That->FileName)); return nullptr; } }; @@ -606,18 +600,17 @@ // Compute updated AST. llvm::Optional NewAST; { - trace::Span Tracer(Ctx, "Build"); + trace::Span Tracer("Build"); SPAN_ATTACH(Tracer, "File", That->FileName); - NewAST = - ParsedAST::Build(Tracer.Ctx, std::move(CI), std::move(NewPreamble), - std::move(ContentsBuffer), PCHs, Inputs.FS); + NewAST = ParsedAST::Build(std::move(CI), std::move(NewPreamble), + std::move(ContentsBuffer), PCHs, Inputs.FS); } if (NewAST) { Diagnostics.insert(Diagnostics.end(), NewAST->getDiagnostics().begin(), NewAST->getDiagnostics().end()); if (That->ASTCallback) - That->ASTCallback(Ctx, That->FileName, NewAST.getPointer()); + That->ASTCallback(That->FileName, NewAST.getPointer()); } else { // Don't report even Preamble diagnostics if we coulnd't build AST. Diagnostics.clear(); Index: clangd/CodeComplete.h =================================================================== --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -15,7 +15,6 @@ #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,7 +66,7 @@ }; /// Get code completions at a specified \p Pos in \p FileName. -CompletionList codeComplete(const Context &Ctx, PathRef FileName, +CompletionList codeComplete(PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, @@ -76,7 +75,7 @@ CodeCompleteOptions Opts); /// Get signature help at a specified \p Pos in \p FileName. -SignatureHelp signatureHelp(const Context &Ctx, PathRef FileName, +SignatureHelp signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -633,12 +633,11 @@ // Invokes Sema code completion on a file. // Callback will be invoked once completion is done, but before cleaning up. -bool semaCodeComplete(const Context &Ctx, - std::unique_ptr Consumer, +bool semaCodeComplete(std::unique_ptr Consumer, const clang::CodeCompleteOptions &Options, const SemaCompleteInput &Input, llvm::function_ref Callback = nullptr) { - auto Tracer = llvm::make_unique(Ctx, "Sema completion"); + auto Tracer = llvm::make_unique("Sema completion"); std::vector ArgStrs; for (const auto &S : Input.Command.CommandLine) ArgStrs.push_back(S.c_str()); @@ -690,13 +689,12 @@ SyntaxOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { - log(Ctx, "BeginSourceFile() failed when running codeComplete for " + - Input.FileName); + log("BeginSourceFile() failed when running codeComplete for " + + Input.FileName); return false; } if (!Action.Execute()) { - log(Ctx, - "Execute() failed when running codeComplete for " + Input.FileName); + log("Execute() failed when running codeComplete for " + Input.FileName); return false; } Tracer.reset(); @@ -704,7 +702,7 @@ if (Callback) Callback(); - Tracer = llvm::make_unique(Ctx, "Sema completion cleanup"); + Tracer = llvm::make_unique("Sema completion cleanup"); Action.EndSourceFile(); return true; @@ -801,8 +799,6 @@ // This score is combined with the result quality score for the final score. // - TopN determines the results with the best score. class CodeCompleteFlow { - trace::Span Tracer; - const Context &Ctx; const CodeCompleteOptions &Opts; // Sema takes ownership of Recorder. Recorder is valid until Sema cleanup. std::unique_ptr RecorderOwner; @@ -813,21 +809,22 @@ public: // A CodeCompleteFlow object is only useful for calling run() exactly once. - CodeCompleteFlow(const Context &Ctx, const CodeCompleteOptions &Opts) - : Tracer(Ctx, "CodeCompleteFlow"), Ctx(Tracer.Ctx), Opts(Opts), - RecorderOwner(new CompletionRecorder(Opts)), Recorder(*RecorderOwner) {} + CodeCompleteFlow(const CodeCompleteOptions &Opts) + : Opts(Opts), RecorderOwner(new CompletionRecorder(Opts)), + Recorder(*RecorderOwner) {} CompletionList run(const SemaCompleteInput &SemaCCInput) && { + trace::Span Tracer("CodeCompleteFlow"); // We run Sema code completion first. It builds an AST and calculates: // - completion results based on the AST. These are saved for merging. // - partial identifier and context. We need these for the index query. CompletionList Output; - semaCodeComplete(Ctx, std::move(RecorderOwner), Opts.getClangCompleteOpts(), + semaCodeComplete(std::move(RecorderOwner), Opts.getClangCompleteOpts(), SemaCCInput, [&] { if (Recorder.CCSema) Output = runWithSema(); else - log(Ctx, "Code complete: no Sema callback, 0 results"); + log("Code complete: no Sema callback, 0 results"); }); SPAN_ATTACH(Tracer, "sema_results", NSema); @@ -835,8 +832,7 @@ SPAN_ATTACH(Tracer, "merged_results", NBoth); SPAN_ATTACH(Tracer, "returned_results", Output.items.size()); SPAN_ATTACH(Tracer, "incomplete", Output.isIncomplete); - log(Ctx, - llvm::formatv("Code complete: {0} results from Sema, {1} from Index, " + log(llvm::formatv("Code complete: {0} results from Sema, {1} from Index, " "{2} matched, {3} returned{4}.", NSema, NIndex, NBoth, Output.items.size(), Output.isIncomplete ? " (incomplete)" : "")); @@ -871,7 +867,7 @@ SymbolSlab queryIndex() { if (!Opts.Index || !allowIndex(Recorder.CCContext.getKind())) return SymbolSlab(); - trace::Span Tracer(Ctx, "Query index"); + trace::Span Tracer("Query index"); SPAN_ATTACH(Tracer, "limit", Opts.Limit); SymbolSlab::Builder ResultsBuilder; @@ -882,15 +878,12 @@ Req.Query = Filter->pattern(); Req.Scopes = getQueryScopes(Recorder.CCContext, Recorder.CCSema->getSourceManager()); - log(Tracer.Ctx, - llvm::formatv("Code complete: fuzzyFind(\"{0}\", Scopes: [{1}]", + log(llvm::formatv("Code complete: fuzzyFind(\"{0}\", scopes=[{1}])", Req.Query, llvm::join(Req.Scopes.begin(), Req.Scopes.end(), ","))); // Run the query against the index. - Incomplete |= - !Opts.Index->fuzzyFind(Tracer.Ctx, Req, [&](const Symbol &Sym) { - ResultsBuilder.insert(Sym); - }); + Incomplete |= !Opts.Index->fuzzyFind( + Req, [&](const Symbol &Sym) { ResultsBuilder.insert(Sym); }); return std::move(ResultsBuilder).build(); } @@ -899,7 +892,7 @@ std::vector> mergeResults(const std::vector &SemaResults, const SymbolSlab &IndexResults) { - trace::Span Tracer(Ctx, "Merge and score results"); + trace::Span Tracer("Merge and score results"); // We only keep the best N results at any time, in "native" format. TopN Top(Opts.Limit == 0 ? TopN::Unbounded : Opts.Limit); llvm::DenseSet UsedIndexResults; @@ -960,18 +953,18 @@ } }; -CompletionList codeComplete(const Context &Ctx, PathRef FileName, +CompletionList codeComplete(PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, CodeCompleteOptions Opts) { - return CodeCompleteFlow(Ctx, Opts).run( + return CodeCompleteFlow(Opts).run( {FileName, Command, Preamble, Contents, Pos, VFS, PCHs}); } -SignatureHelp signatureHelp(const Context &Ctx, PathRef FileName, +SignatureHelp signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, @@ -983,10 +976,10 @@ Options.IncludeMacros = false; Options.IncludeCodePatterns = false; Options.IncludeBriefComments = true; - semaCodeComplete( - Ctx, llvm::make_unique(Options, Result), Options, - {FileName, Command, Preamble, Contents, Pos, std::move(VFS), - std::move(PCHs)}); + semaCodeComplete(llvm::make_unique(Options, Result), + Options, + {FileName, Command, Preamble, Contents, Pos, std::move(VFS), + std::move(PCHs)}); return Result; } Index: clangd/Context.h =================================================================== --- clangd/Context.h +++ clangd/Context.h @@ -16,15 +16,28 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONTEXT_H_ #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" #include #include namespace clang { namespace clangd { -/// A key for a value of type \p Type, stored inside a context. Keys are -/// non-movable and non-copyable. See documentation of the Context class for -/// more details and usage examples. +/// Values in a Context are indexed by typed keys. +/// Key serves two purposes: +/// - it provides a lookup key for the context (each Key is unique), +/// - it makes lookup type-safe: a Key can only map to a T (or nothing). +/// +/// Example: +/// Key RequestID; +/// Key Version; +/// +/// Context Ctx = Context::empty().derive(RequestID, 10).derive(Version, 3); +/// assert(*Ctx.get(RequestID) == 10); +/// assert(*Ctx.get(Version) == 3); +/// +/// Keys are typically used across multiple functions, so most of the time you +/// would want to make them static class members or global variables. template class Key { public: static_assert(!std::is_reference::value, @@ -45,53 +58,24 @@ /// Conceptually, a context is a heterogeneous map, T>. Each key has /// an associated value type, which allows the map to be typesafe. /// +/// There is an "ambient" context for each thread, Context::current(). +/// Most functions should read from this, and use WithContextValue or +/// WithContext to extend or replace the context within a block scope. +/// Only code dealing with threads and extension points should need to use +/// other Context objects. +/// /// You can't add data to an existing context, instead you create a new /// immutable context derived from it with extra data added. When you retrieve /// data, the context will walk up the parent chain until the key is found. -/// -/// Contexts should be: -/// - passed by reference when calling synchronous functions -/// - passed by value (move) when calling asynchronous functions. The result -/// callback of async operations will receive the context again. -/// - cloned only when 'forking' an asynchronous computation that we don't wait -/// for. -/// -/// Copy operations for this class are deleted, use an explicit clone() method -/// when you need a copy of the context instead. -/// -/// To derive a child context use derive() function, e.g. -/// Context ChildCtx = ParentCtx.derive(RequestIdKey, 123); -/// -/// To create a new root context, derive() from empty Context. -/// e.g.: -/// Context Ctx = Context::empty().derive(RequestIdKey, 123); -/// -/// Values in the context are indexed by typed keys (instances of Key class). -/// Key serves two purposes: -/// - it provides a lookup key for the context (each instance of a key is -/// unique), -/// - it keeps the type information about the value stored in the context map -/// in the template arguments. -/// This provides a type-safe interface to store and access values of multiple -/// types inside a single context. -/// For example, -/// Key RequestID; -/// Key Version; -/// -/// Context Ctx = Context::empty().derive(RequestID, 10).derive(Version, 3); -/// assert(*Ctx.get(RequestID) == 10); -/// assert(*Ctx.get(Version) == 3); -/// -/// Keys are typically used across multiple functions, so most of the time you -/// would want to make them static class members or global variables. -/// -/// FIXME: Rather than manual plumbing, pass Context using thread-local storage -/// by default, and make thread boundaries deal with propagation explicitly. class Context { public: - /// Returns an empty context that contains no data. Useful for calling - /// functions that require a context when no explicit context is available. + /// Returns an empty root context that contains no data. static Context empty(); + /// Returns the context for the current thread, creating it if needed. + static const Context ¤t(); + // Sets the current() context to Replacement, and returns the old context. + // Prefer to use WithContext or WithContextValue to do this safely. + static Context swapCurrent(Context Replacement); private: struct Data; @@ -103,7 +87,8 @@ /// (arguments of std::future<> must be default-construcitble in MSVC). Context() = default; - /// Move-only. + /// Copy operations for this class are deleted, use an explicit clone() method + /// when you need a copy of the context instead. Context(Context const &) = delete; Context &operator=(const Context &) = delete; @@ -131,9 +116,8 @@ } /// Derives a child context - /// It is safe to move or destroy a parent context after calling derive() from - /// it. The child context will continue to have access to the data stored in - /// the parent context. + /// It is safe to move or destroy a parent context after calling derive(). + /// The child will keep its parent alive, and its data remains accessible. template Context derive(const Key &Key, typename std::decay::type Value) const & { @@ -198,7 +182,40 @@ }; std::shared_ptr DataPtr; -}; // namespace clangd +}; + +/// WithContext replaces Context::current() with a provided scope. +/// When the WithContext is destroyed, the original scope is restored. +/// For extending the current context with new value, prefer WithContextValue. +class LLVM_NODISCARD WithContext { +public: + WithContext(Context C) : Restore(Context::swapCurrent(std::move(C))) {} + ~WithContext() { Context::swapCurrent(std::move(Restore)); } + WithContext(const WithContext &) = delete; + WithContext &operator=(const WithContext &) = delete; + WithContext(WithContext &&) = delete; + WithContext &operator=(WithContext &&) = delete; + +private: + Context Restore; +}; + +/// WithContextValue extends Context::current() with a single value. +/// When the WithContextValue is destroyed, the original scope is restored. +class LLVM_NODISCARD WithContextValue { +public: + template + WithContextValue(const Key &K, typename std::decay::type V) + : Restore(Context::current().derive(K, std::move(V))) {} + + // Anonymous values can be used for the destructor side-effect. + template + WithContextValue(T &&V) + : Restore(Context::current().derive(std::forward(V))) {} + +private: + WithContext Restore; +}; } // namespace clangd } // namespace clang Index: clangd/Context.cpp =================================================================== --- clangd/Context.cpp +++ clangd/Context.cpp @@ -20,5 +20,19 @@ Context Context::clone() const { return Context(DataPtr); } +// The thread-local Context is scoped in a function to avoid +// initialization-order issues. It's created when first needed. +static Context ¤tContext() { + static thread_local Context C = Context::empty(); + return C; +} + +const Context &Context::current() { return currentContext(); } + +Context Context::swapCurrent(Context Replacement) { + std::swap(Replacement, currentContext()); + return Replacement; +} + } // namespace clangd } // namespace clang Index: clangd/GlobalCompilationDatabase.cpp =================================================================== --- clangd/GlobalCompilationDatabase.cpp +++ clangd/GlobalCompilationDatabase.cpp @@ -38,8 +38,7 @@ return std::move(Candidates.front()); } } else { - log(Context::empty(), // FIXME(ibiryukov): pass a proper Context here. - "Failed to find compilation database for " + Twine(File)); + log("Failed to find compilation database for " + Twine(File)); } return llvm::None; } Index: clangd/JSONRPCDispatcher.h =================================================================== --- clangd/JSONRPCDispatcher.h +++ clangd/JSONRPCDispatcher.h @@ -10,7 +10,6 @@ #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" @@ -38,7 +37,7 @@ void writeMessage(const json::Expr &Result); /// Write a line to the logging stream. - void log(const Context &Ctx, const Twine &Message) override; + void log(const Twine &Message) override; /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is /// null. @@ -56,23 +55,22 @@ std::mutex StreamMutex; }; -/// 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); +/// Sends a successful reply. +/// Current context must derive from JSONRPCDispatcher::Handler. +void reply(json::Expr &&Result); +/// Sends an error response to the client, and logs it. +/// Current context must derive from JSONRPCDispatcher::Handler. +void replyError(ErrorCode code, const llvm::StringRef &Message); +/// Sends a request to the client. +/// Current context must derive from JSONRPCDispatcher::Handler. +void call(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 @@ -34,14 +34,14 @@ public: // Return a context that's aware of the enclosing request, identified by Span. static Context stash(const trace::Span &Span) { - return Span.Ctx.derive(RSKey, std::unique_ptr( - new RequestSpan(Span.Args))); + return Context::current().derive( + RSKey, std::unique_ptr(new RequestSpan(Span.Args))); } // If there's an enclosing request and the tracer is interested, calls \p F // with a json::obj where request info can be added. - template static void attach(const Context &Ctx, Func &&F) { - auto *RequestArgs = Ctx.get(RSKey); + template static void attach(Func &&F) { + auto *RequestArgs = Context::current().get(RSKey); if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args) return; std::lock_guard Lock((*RequestArgs)->Mu); @@ -70,8 +70,8 @@ Outs.flush(); } -void JSONOutput::log(const Context &Ctx, const Twine &Message) { - trace::log(Ctx, Message); +void JSONOutput::log(const Twine &Message) { + trace::log(Message); std::lock_guard Guard(StreamMutex); Logs << Message << '\n'; Logs.flush(); @@ -85,16 +85,15 @@ InputMirror->flush(); } -void clangd::reply(const Context &Ctx, json::Expr &&Result) { - auto ID = Ctx.get(RequestID); +void clangd::reply(json::Expr &&Result) { + auto ID = Context::current().get(RequestID); if (!ID) { - log(Ctx, "Attempted to reply to a notification!"); + log("Attempted to reply to a notification!"); return; } - - RequestSpan::attach(Ctx, [&](json::obj &Args) { Args["Reply"] = Result; }); - - Ctx.getExisting(RequestOut) + RequestSpan::attach([&](json::obj &Args) { Args["Reply"] = Result; }); + Context::current() + .getExisting(RequestOut) ->writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", *ID}, @@ -102,16 +101,16 @@ }); } -void clangd::replyError(const Context &Ctx, ErrorCode code, - const llvm::StringRef &Message) { - log(Ctx, "Error " + Twine(static_cast(code)) + ": " + Message); - RequestSpan::attach(Ctx, [&](json::obj &Args) { +void clangd::replyError(ErrorCode code, const llvm::StringRef &Message) { + log("Error " + Twine(static_cast(code)) + ": " + Message); + RequestSpan::attach([&](json::obj &Args) { Args["Error"] = json::obj{{"code", static_cast(code)}, {"message", Message.str()}}; }); - if (auto ID = Ctx.get(RequestID)) { - Ctx.getExisting(RequestOut) + if (auto ID = Context::current().get(RequestID)) { + Context::current() + .getExisting(RequestOut) ->writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", *ID}, @@ -121,13 +120,14 @@ } } -void clangd::call(const Context &Ctx, StringRef Method, json::Expr &&Params) { +void clangd::call(StringRef Method, json::Expr &&Params) { // FIXME: Generate/Increment IDs for every request so that we can get proper // replies once we need to. - RequestSpan::attach(Ctx, [&](json::obj &Args) { + RequestSpan::attach([&](json::obj &Args) { Args["Call"] = json::obj{{"method", Method.str()}, {"params", Params}}; }); - Ctx.getExisting(RequestOut) + Context::current() + .getExisting(RequestOut) ->writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", 1}, @@ -163,18 +163,20 @@ auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; // Create a Context that contains request information. - auto Ctx = Context::empty().derive(RequestOut, &Out); + WithContextValue WithRequestOut(RequestOut, &Out); + llvm::Optional WithID; if (ID) - Ctx = std::move(Ctx).derive(RequestID, *ID); + WithID.emplace(RequestID, *ID); // Create a tracing Span covering the whole request lifetime. - trace::Span Tracer(Ctx, *Method); + trace::Span Tracer(*Method); if (ID) SPAN_ATTACH(Tracer, "ID", *ID); SPAN_ATTACH(Tracer, "Params", Params); // Stash a reference to the span args, so later calls can add metadata. - Handler(RequestSpan::stash(Tracer), std::move(Params)); + WithContext WithRequestSpan(RequestSpan::stash(Tracer)); + Handler(std::move(Params)); return true; } @@ -216,10 +218,9 @@ // The end of headers is signified by an empty line. if (LineRef.consume_front("Content-Length: ")) { if (ContentLength != 0) { - log(Context::empty(), - "Warning: Duplicate Content-Length header received. " + log("Warning: Duplicate Content-Length header received. " "The previous value for this message (" + - llvm::Twine(ContentLength) + ") was ignored.\n"); + llvm::Twine(ContentLength) + ") was ignored.\n"); } llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength); @@ -238,8 +239,8 @@ // and we don't want to crash downstream because of it. if (ContentLength > 1 << 30) { // 1024M In.ignore(ContentLength); - log(Context::empty(), "Skipped overly large message of " + - Twine(ContentLength) + " bytes.\n"); + log("Skipped overly large message of " + Twine(ContentLength) + + " bytes.\n"); continue; } @@ -253,9 +254,8 @@ // If the stream is aborted before we read ContentLength bytes, In // will have eofbit and failbit set. if (!In) { - log(Context::empty(), - "Input was aborted. Read only " + llvm::Twine(In.gcount()) + - " bytes of expected " + llvm::Twine(ContentLength) + ".\n"); + log("Input was aborted. Read only " + llvm::Twine(In.gcount()) + + " bytes of expected " + llvm::Twine(ContentLength) + ".\n"); break; } @@ -264,24 +264,22 @@ if (auto Doc = json::parse(JSONRef)) { // Log the formatted message. - log(Context::empty(), - llvm::formatv(Out.Pretty ? "<-- {0:2}\n" : "<-- {0}\n", *Doc)); + log(llvm::formatv(Out.Pretty ? "<-- {0:2}\n" : "<-- {0}\n", *Doc)); // Finally, execute the action for this JSON message. if (!Dispatcher.call(*Doc, Out)) - log(Context::empty(), "JSON dispatch failed!\n"); + log("JSON dispatch failed!\n"); } else { // Parse error. Log the raw message. - log(Context::empty(), "<-- " + JSONRef + "\n"); - log(Context::empty(), llvm::Twine("JSON parse error: ") + - llvm::toString(Doc.takeError()) + "\n"); + log("<-- " + JSONRef + "\n"); + log(llvm::Twine("JSON parse error: ") + + llvm::toString(Doc.takeError()) + "\n"); } // If we're done, exit the loop. if (IsDone) break; } else { - log(Context::empty(), - "Warning: Missing Content-Length header, or message has zero " + log("Warning: Missing Content-Length header, or message has zero " "length.\n"); } } Index: clangd/Logger.h =================================================================== --- clangd/Logger.h +++ clangd/Logger.h @@ -10,7 +10,6 @@ #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 { @@ -19,7 +18,7 @@ /// Main logging function. /// Logs messages to a global logger, which can be set up by LoggingSesssion. /// If no logger is registered, writes to llvm::errs(). -void log(const Context &Ctx, const llvm::Twine &Message); +void log(const llvm::Twine &Message); /// Interface to allow custom logging in clangd. class Logger { @@ -27,7 +26,7 @@ virtual ~Logger() = default; /// Implementations of this method must be thread-safe. - virtual void log(const Context &Ctx, const llvm::Twine &Message) = 0; + virtual void log(const llvm::Twine &Message) = 0; }; /// Only one LoggingSession can be active at a time. Index: clangd/Logger.cpp =================================================================== --- clangd/Logger.cpp +++ clangd/Logger.cpp @@ -25,9 +25,9 @@ LoggingSession::~LoggingSession() { L = nullptr; } -void log(const Context &Ctx, const llvm::Twine &Message) { +void log(const llvm::Twine &Message) { if (L) - L->log(Ctx, Message); + L->log(Message); else { static std::mutex Mu; std::lock_guard Guard(Mu); Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -28,18 +28,16 @@ if (auto S = E.asString()) { auto U = URI::parse(*S); if (!U) { - log(Context::empty(), - "Failed to parse URI " + *S + ": " + llvm::toString(U.takeError())); + log("Failed to parse URI " + *S + ": " + llvm::toString(U.takeError())); return false; } if (U->scheme() != "file") { - log(Context::empty(), - "Clangd only supports 'file' URI scheme for workspace files: " + *S); + log("Clangd only supports 'file' URI scheme for workspace files: " + *S); return false; } auto Path = URI::resolve(*U); if (!Path) { - log(Context::empty(), llvm::toString(Path.takeError())); + log(llvm::toString(Path.takeError())); return false; } R.file = *Path; Index: clangd/ProtocolHandlers.h =================================================================== --- clangd/ProtocolHandlers.h +++ clangd/ProtocolHandlers.h @@ -29,34 +29,28 @@ // The interface implemented by ClangLSPServer to handle incoming requests. class ProtocolCallbacks { public: - // FIXME(ibiryukov): remove this typedef, inline its usages. - using Ctx = Context; virtual ~ProtocolCallbacks() = default; - virtual void onInitialize(Ctx C, InitializeParams &Params) = 0; - virtual void onShutdown(Ctx C, ShutdownParams &Params) = 0; - virtual void onExit(Ctx C, ExitParams &Params) = 0; - virtual void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) = 0; - virtual void onDocumentDidChange(Ctx C, - DidChangeTextDocumentParams &Params) = 0; - virtual void onDocumentDidClose(Ctx C, - DidCloseTextDocumentParams &Params) = 0; - virtual void onDocumentFormatting(Ctx C, - DocumentFormattingParams &Params) = 0; + virtual void onInitialize(InitializeParams &Params) = 0; + virtual void onShutdown(ShutdownParams &Params) = 0; + virtual void onExit(ExitParams &Params) = 0; + virtual void onDocumentDidOpen(DidOpenTextDocumentParams &Params) = 0; + virtual void onDocumentDidChange(DidChangeTextDocumentParams &Params) = 0; + virtual void onDocumentDidClose(DidCloseTextDocumentParams &Params) = 0; + virtual void onDocumentFormatting(DocumentFormattingParams &Params) = 0; virtual void - onDocumentOnTypeFormatting(Ctx C, DocumentOnTypeFormattingParams &Params) = 0; + onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams &Params) = 0; virtual void - onDocumentRangeFormatting(Ctx C, DocumentRangeFormattingParams &Params) = 0; - virtual void onCodeAction(Ctx C, CodeActionParams &Params) = 0; - virtual void onCompletion(Ctx C, TextDocumentPositionParams &Params) = 0; - virtual void onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) = 0; - virtual void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) = 0; - virtual void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) = 0; - virtual void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) = 0; - virtual void onCommand(Ctx C, ExecuteCommandParams &Params) = 0; - virtual void onRename(Ctx C, RenameParams &Parames) = 0; - virtual void onDocumentHighlight(Ctx C, - TextDocumentPositionParams &Params) = 0; + onDocumentRangeFormatting(DocumentRangeFormattingParams &Params) = 0; + virtual void onCodeAction(CodeActionParams &Params) = 0; + virtual void onCompletion(TextDocumentPositionParams &Params) = 0; + virtual void onSignatureHelp(TextDocumentPositionParams &Params) = 0; + virtual void onGoToDefinition(TextDocumentPositionParams &Params) = 0; + virtual void onSwitchSourceHeader(TextDocumentIdentifier &Params) = 0; + virtual void onFileEvent(DidChangeWatchedFilesParams &Params) = 0; + virtual void onCommand(ExecuteCommandParams &Params) = 0; + virtual void onRename(RenameParams &Parames) = 0; + virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0; }; void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out, Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -24,19 +24,17 @@ // FooParams should have a fromJSON function. struct HandlerRegisterer { template - void operator()(StringRef Method, - void (ProtocolCallbacks::*Handler)(Context, Param)) { + void operator()(StringRef Method, void (ProtocolCallbacks::*Handler)(Param)) { // Capture pointers by value, as the lambda will outlive this object. auto *Callbacks = this->Callbacks; - Dispatcher.registerHandler( - Method, [=](Context C, const json::Expr &RawParams) { - typename std::remove_reference::type P; - if (fromJSON(RawParams, P)) { - (Callbacks->*Handler)(std::move(C), P); - } else { - log(C, "Failed to decode " + Method + " request."); - } - }); + Dispatcher.registerHandler(Method, [=](const json::Expr &RawParams) { + typename std::remove_reference::type P; + if (fromJSON(RawParams, P)) { + (Callbacks->*Handler)(P); + } else { + log("Failed to decode " + Method + " request."); + } + }); } JSONRPCDispatcher &Dispatcher; Index: clangd/TUScheduler.h =================================================================== --- clangd/TUScheduler.h +++ clangd/TUScheduler.h @@ -50,10 +50,9 @@ /// Schedule an update for \p File. Adds \p File to a list of tracked files if /// \p File was not part of it before. /// FIXME(ibiryukov): remove the callback from this function. - void update( - Context Ctx, PathRef File, ParseInputs Inputs, - UniqueFunction>)> - OnUpdated); + void update(PathRef File, ParseInputs Inputs, + UniqueFunction>)> + OnUpdated); /// Remove \p File from the list of tracked files and schedule removal of its /// resources. \p Action will be called when resources are freed. Index: clangd/TUScheduler.cpp =================================================================== --- clangd/TUScheduler.cpp +++ clangd/TUScheduler.cpp @@ -23,9 +23,8 @@ Threads(AsyncThreadsCount) {} void TUScheduler::update( - Context Ctx, PathRef File, ParseInputs Inputs, - UniqueFunction>)> + PathRef File, ParseInputs Inputs, + UniqueFunction>)> OnUpdated) { CachedInputs[File] = Inputs; @@ -33,12 +32,12 @@ auto DeferredRebuild = Resources->deferRebuild(std::move(Inputs)); Threads.addToFront( - [](Context Ctx, decltype(OnUpdated) OnUpdated, + [](decltype(OnUpdated) OnUpdated, decltype(DeferredRebuild) DeferredRebuild) { - auto Diags = DeferredRebuild(Ctx); - OnUpdated(std::move(Ctx), Diags); + auto Diags = DeferredRebuild(); + OnUpdated(Diags); }, - std::move(Ctx), std::move(OnUpdated), std::move(DeferredRebuild)); + std::move(OnUpdated), std::move(DeferredRebuild)); } void TUScheduler::remove(PathRef File, Index: clangd/Threading.h =================================================================== --- clangd/Threading.h +++ clangd/Threading.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_THREADING_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_THREADING_H +#include "Context.h" #include "Function.h" #include #include @@ -42,8 +43,9 @@ { std::lock_guard Lock(Mutex); - RequestQueue.push_front( - BindWithForward(std::forward(F), std::forward(As)...)); + RequestQueue.emplace_front( + BindWithForward(std::forward(F), std::forward(As)...), + Context::current().clone()); } RequestCV.notify_one(); } @@ -58,8 +60,9 @@ { std::lock_guard Lock(Mutex); - RequestQueue.push_back( - BindWithForward(std::forward(F), std::forward(As)...)); + RequestQueue.emplace_back( + BindWithForward(std::forward(F), std::forward(As)...), + Context::current().clone()); } RequestCV.notify_one(); } @@ -74,7 +77,7 @@ /// Setting Done to true will make the worker threads terminate. bool Done = false; /// A queue of requests. - std::deque> RequestQueue; + std::deque, Context>> RequestQueue; /// Condition variable to wake up worker threads. std::condition_variable RequestCV; }; Index: clangd/Threading.cpp =================================================================== --- clangd/Threading.cpp +++ clangd/Threading.cpp @@ -17,6 +17,7 @@ llvm::set_thread_name(llvm::formatv("scheduler/{0}", I)); while (true) { UniqueFunction Request; + Context Ctx; // Pick request from the queue { @@ -33,10 +34,11 @@ // ThreadPool have a way to prioritise their requests by putting // them to the either side of the queue (using either addToEnd or // addToFront). - Request = std::move(RequestQueue.front()); + std::tie(Request, Ctx) = std::move(RequestQueue.front()); RequestQueue.pop_front(); } // unlock Mutex + WithContext WithCtx(std::move(Ctx)); Request(); } })); Index: clangd/Trace.h =================================================================== --- clangd/Trace.h +++ clangd/Trace.h @@ -39,12 +39,10 @@ /// Usually implementations will store an object in the returned context /// whose destructor records the end of the event. /// The args are *Args, only complete when the event ends. - virtual Context beginSpan(const Context &Ctx, llvm::StringRef Name, - json::obj *Args) = 0; + virtual Context beginSpan(llvm::StringRef Name, json::obj *Args) = 0; /// Called for instant events. - virtual void instant(const Context &Ctx, llvm::StringRef Name, - json::obj &&Args) = 0; + virtual void instant(llvm::StringRef Name, json::obj &&Args) = 0; }; /// Sets up a global EventTracer that consumes events produced by Span and @@ -65,7 +63,7 @@ bool Pretty = false); /// Records a single instant event, associated with the current thread. -void log(const Context &Ctx, const llvm::Twine &Name); +void log(const llvm::Twine &Name); /// Records an event whose duration is the lifetime of the Span object. /// This lifetime is extended when the span's context is reused. @@ -78,18 +76,19 @@ /// SomeJSONExpr is evaluated and copied only if actually needed. class Span { public: - Span(const Context &Ctx, llvm::StringRef Name); + Span(llvm::StringRef Name); /// Mutable metadata, if this span is interested. /// Prefer to use SPAN_ATTACH rather than accessing this directly. json::obj *const Args; - /// Propagating this context will keep the span alive. - const Context Ctx; + +private: + WithContext RestoreCtx; }; /// Returns mutable span metadata if this span is interested. /// Prefer to use SPAN_ATTACH rather than accessing this directly. -json::obj *spanArgs(const Context &Ctx); +json::obj *spanArgs(); /// Attach a key-value pair to a Span event. /// This is not threadsafe when used with the same Span. Index: clangd/Trace.cpp =================================================================== --- clangd/Trace.cpp +++ clangd/Trace.cpp @@ -46,16 +46,14 @@ Out.flush(); } - Context beginSpan(const Context &Ctx, llvm::StringRef Name, - json::obj *Args) override { + Context beginSpan(llvm::StringRef Name, json::obj *Args) override { jsonEvent("B", json::obj{{"name", Name}}); - return Ctx.derive(make_scope_exit([this, Args] { + return Context::current().derive(make_scope_exit([this, Args] { jsonEvent("E", json::obj{{"args", std::move(*Args)}}); })); } - void instant(const Context &Ctx, llvm::StringRef Name, - json::obj &&Args) override { + void instant(llvm::StringRef Name, json::obj &&Args) override { jsonEvent("i", json::obj{{"name", Name}, {"args", std::move(Args)}}); } @@ -120,20 +118,26 @@ return llvm::make_unique(OS, Pretty); } -void log(const Context &Ctx, const Twine &Message) { +void log(const Twine &Message) { if (!T) return; - T->instant(Ctx, "Log", json::obj{{"Message", Message.str()}}); + T->instant("Log", json::obj{{"Message", Message.str()}}); +} + +// Returned context owns Args. +static Context makeSpanContext(llvm::StringRef Name, json::obj *Args) { + if (!T) + return Context::current().clone(); + WithContextValue WithArgs{std::unique_ptr(Args)}; + return T->beginSpan(Name, Args); } // Span keeps a non-owning pointer to the args, which is how users access them. // The args are owned by the context though. They stick around until the // beginSpan() context is destroyed, when the tracing engine will consume them. -Span::Span(const Context &Ctx, llvm::StringRef Name) +Span::Span(llvm::StringRef Name) : Args(T ? new json::obj() : nullptr), - Ctx(T ? T->beginSpan(Ctx.derive(std::unique_ptr(Args)), Name, - Args) - : Ctx.clone()) {} + RestoreCtx(makeSpanContext(Name, Args)) {} } // namespace trace } // namespace clangd Index: clangd/XRefs.h =================================================================== --- clangd/XRefs.h +++ clangd/XRefs.h @@ -14,7 +14,6 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XREFS_H #include "ClangdUnit.h" -#include "Context.h" #include "Protocol.h" #include @@ -22,12 +21,11 @@ namespace clangd { /// Get definition of symbol at a specified \p Pos. -std::vector findDefinitions(const Context &Ctx, ParsedAST &AST, - Position Pos); +std::vector findDefinitions(ParsedAST &AST, Position Pos); /// Returns highlights for all usages of a symbol at \p Pos. -std::vector -findDocumentHighlights(const Context &Ctx, ParsedAST &AST, Position Pos); +std::vector findDocumentHighlights(ParsedAST &AST, + Position Pos); } // namespace clangd } // namespace clang Index: clangd/XRefs.cpp =================================================================== --- clangd/XRefs.cpp +++ clangd/XRefs.cpp @@ -148,8 +148,7 @@ } // namespace -std::vector findDefinitions(const Context &Ctx, ParsedAST &AST, - Position Pos) { +std::vector findDefinitions(ParsedAST &AST, Position Pos) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); if (!FE) @@ -260,8 +259,8 @@ } // namespace -std::vector -findDocumentHighlights(const Context &Ctx, ParsedAST &AST, Position Pos) { +std::vector findDocumentHighlights(ParsedAST &AST, + Position Pos) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); if (!FE) Index: clangd/index/FileIndex.h =================================================================== --- clangd/index/FileIndex.h +++ clangd/index/FileIndex.h @@ -17,7 +17,6 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEINDEX_H #include "../ClangdUnit.h" -#include "../Context.h" #include "Index.h" #include "MemIndex.h" @@ -58,10 +57,10 @@ public: /// \brief Update symbols in \p Path with symbols in \p AST. If \p AST is /// nullptr, this removes all symbols in the file - void update(const Context &Ctx, PathRef Path, ParsedAST *AST); + void update(PathRef Path, ParsedAST *AST); bool - fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req, + fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const override; private: Index: clangd/index/FileIndex.cpp =================================================================== --- clangd/index/FileIndex.cpp +++ clangd/index/FileIndex.cpp @@ -69,7 +69,7 @@ return {std::move(Snap), Pointers}; } -void FileIndex::update(const Context &Ctx, PathRef Path, ParsedAST *AST) { +void FileIndex::update(PathRef Path, ParsedAST *AST) { if (!AST) { FSymbols.update(Path, nullptr); } else { @@ -82,9 +82,9 @@ } bool FileIndex::fuzzyFind( - const Context &Ctx, const FuzzyFindRequest &Req, + const FuzzyFindRequest &Req, llvm::function_ref Callback) const { - return Index.fuzzyFind(Ctx, Req, Callback); + return Index.fuzzyFind(Req, Callback); } } // namespace clangd Index: clangd/index/Index.h =================================================================== --- clangd/index/Index.h +++ clangd/index/Index.h @@ -10,7 +10,6 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H -#include "../Context.h" #include "clang/Index/IndexSymbol.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" @@ -244,7 +243,7 @@ /// Returns true if the result list is complete, false if it was truncated due /// to MaxCandidateCount virtual bool - fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req, + fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const = 0; // FIXME: add interfaces for more index use cases: Index: clangd/index/MemIndex.h =================================================================== --- clangd/index/MemIndex.h +++ clangd/index/MemIndex.h @@ -28,7 +28,7 @@ static std::unique_ptr build(SymbolSlab Slab); bool - fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req, + fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const override; private: Index: clangd/index/MemIndex.cpp =================================================================== --- clangd/index/MemIndex.cpp +++ clangd/index/MemIndex.cpp @@ -29,7 +29,7 @@ } bool MemIndex::fuzzyFind( - const Context &Ctx, const FuzzyFindRequest &Req, + const FuzzyFindRequest &Req, llvm::function_ref Callback) const { assert(!StringRef(Req.Query).contains("::") && "There must be no :: in query."); Index: clangd/index/Merge.cpp =================================================================== --- clangd/index/Merge.cpp +++ clangd/index/Merge.cpp @@ -24,7 +24,7 @@ // - find the generating file from each Symbol which is Static-only // - ask Dynamic if it has that file (needs new SymbolIndex method) // - if so, drop the Symbol. - bool fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req, + bool fuzzyFind(const FuzzyFindRequest &Req, function_ref Callback) const override { // We can't step through both sources in parallel. So: // 1) query all dynamic symbols, slurping results into a slab @@ -34,13 +34,12 @@ // 3) now yield all the dynamic symbols we haven't processed. bool More = false; // We'll be incomplete if either source was. SymbolSlab::Builder DynB; - More |= - Dynamic->fuzzyFind(Ctx, Req, [&](const Symbol &S) { DynB.insert(S); }); + More |= Dynamic->fuzzyFind(Req, [&](const Symbol &S) { DynB.insert(S); }); SymbolSlab Dyn = std::move(DynB).build(); DenseSet SeenDynamicSymbols; Symbol::Details Scratch; - More |= Static->fuzzyFind(Ctx, Req, [&](const Symbol &S) { + More |= Static->fuzzyFind(Req, [&](const Symbol &S) { auto DynS = Dyn.find(S.ID); if (DynS == Dyn.end()) return Callback(S); Index: unittests/clangd/ClangdTests.cpp =================================================================== --- unittests/clangd/ClangdTests.cpp +++ unittests/clangd/ClangdTests.cpp @@ -9,7 +9,6 @@ #include "ClangdLSPServer.h" #include "ClangdServer.h" -#include "Context.h" #include "TestFS.h" #include "clang/Config/config.h" #include "llvm/ADT/SmallVector.h" @@ -58,7 +57,7 @@ class ErrorCheckingDiagConsumer : public DiagnosticsConsumer { public: void - onDiagnosticsReady(const Context &Ctx, PathRef File, + onDiagnosticsReady(PathRef File, Tagged> Diagnostics) override { bool HadError = diagsContainErrors(Diagnostics.Value); @@ -143,8 +142,7 @@ // Have to sync reparses because requests are processed on the calling // thread. - auto AddDocFuture = - Server.addDocument(Context::empty(), SourceFilename, SourceContents); + auto AddDocFuture = Server.addDocument(SourceFilename, SourceContents); auto Result = dumpASTWithoutMemoryLocs(Server, SourceFilename); @@ -211,21 +209,21 @@ FS.ExpectedFile = FooCpp; // To sync reparses before checking for errors. - std::future ParseFuture; + std::future ParseFuture; - ParseFuture = Server.addDocument(Context::empty(), FooCpp, SourceContents); + ParseFuture = Server.addDocument(FooCpp, SourceContents); auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); - ParseFuture = Server.addDocument(Context::empty(), FooCpp, ""); + ParseFuture = Server.addDocument(FooCpp, ""); auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); - ParseFuture = Server.addDocument(Context::empty(), FooCpp, SourceContents); + ParseFuture = Server.addDocument(FooCpp, SourceContents); auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); @@ -256,23 +254,23 @@ FS.ExpectedFile = FooCpp; // To sync reparses before checking for errors. - std::future ParseFuture; + std::future ParseFuture; - ParseFuture = Server.addDocument(Context::empty(), FooCpp, SourceContents); + ParseFuture = Server.addDocument(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(Context::empty(), FooCpp); + ParseFuture = Server.forceReparse(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(Context::empty(), FooCpp); + ParseFuture = Server.forceReparse(FooCpp); auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_EQ(ParseFuture.wait_for(DefaultFutureTimeout), std::future_status::ready); @@ -302,22 +300,16 @@ // No need to sync reparses, because requests are processed on the calling // thread. FS.Tag = "123"; - Server.addDocument(Context::empty(), FooCpp, SourceContents); - EXPECT_EQ( - Server.codeComplete(Context::empty(), FooCpp, Position{0, 0}, CCOpts) - .get() - .second.Tag, - FS.Tag); + Server.addDocument(FooCpp, SourceContents); + EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}, CCOpts).get().Tag, + FS.Tag); EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag); FS.Tag = "321"; - Server.addDocument(Context::empty(), FooCpp, SourceContents); + Server.addDocument(FooCpp, SourceContents); EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag); - EXPECT_EQ( - Server.codeComplete(Context::empty(), FooCpp, Position{0, 0}, CCOpts) - .get() - .second.Tag, - FS.Tag); + EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}, CCOpts).get().Tag, + FS.Tag); } // Only enable this test on Unix @@ -365,14 +357,14 @@ // No need to sync reparses, because requests are processed on the calling // thread. - Server.addDocument(Context::empty(), FooCpp, SourceContents); + Server.addDocument(FooCpp, SourceContents); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); const auto SourceContentsWithError = R"cpp( #include std::string x; )cpp"; - Server.addDocument(Context::empty(), FooCpp, SourceContentsWithError); + Server.addDocument(FooCpp, SourceContentsWithError); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); } #endif // LLVM_ON_UNIX @@ -402,26 +394,26 @@ // First parse files in C mode and check they produce errors. CDB.ExtraClangFlags = {"-xc"}; - Server.addDocument(Context::empty(), FooCpp, SourceContents1); + Server.addDocument(FooCpp, SourceContents1); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(Context::empty(), FooCpp, SourceContents2); + Server.addDocument(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(Context::empty(), FooCpp, SourceContents1); + Server.addDocument(FooCpp, SourceContents1); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(Context::empty(), FooCpp, SourceContents2); + Server.addDocument(FooCpp, SourceContents2); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); // But forceReparse should reparse the file with proper flags. - Server.forceReparse(Context::empty(), FooCpp); + Server.forceReparse(FooCpp); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); // Subsequent addDocument calls should finish without errors too. - Server.addDocument(Context::empty(), FooCpp, SourceContents1); + Server.addDocument(FooCpp, SourceContents1); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); - Server.addDocument(Context::empty(), FooCpp, SourceContents2); + Server.addDocument(FooCpp, SourceContents2); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); } @@ -448,16 +440,16 @@ EXPECT_THAT(Server.getUsedBytesPerFile(), IsEmpty()); - Server.addDocument(Context::empty(), FooCpp, SourceContents); - Server.addDocument(Context::empty(), BarCpp, SourceContents); + Server.addDocument(FooCpp, SourceContents); + Server.addDocument(BarCpp, SourceContents); EXPECT_THAT(Server.getUsedBytesPerFile(), UnorderedElementsAre(Pair(FooCpp, Gt(0u)), Pair(BarCpp, Gt(0u)))); - Server.removeDocument(Context::empty(), FooCpp); + Server.removeDocument(FooCpp); EXPECT_THAT(Server.getUsedBytesPerFile(), ElementsAre(Pair(BarCpp, Gt(0u)))); - Server.removeDocument(Context::empty(), BarCpp); + Server.removeDocument(BarCpp); EXPECT_THAT(Server.getUsedBytesPerFile(), IsEmpty()); } @@ -515,7 +507,7 @@ TestDiagConsumer() : Stats(FilesCount, FileStat()) {} void onDiagnosticsReady( - const Context &Ctx, PathRef File, + PathRef File, Tagged> Diagnostics) override { StringRef FileIndexStr = llvm::sys::path::stem(File); ASSERT_TRUE(FileIndexStr.consume_front("Foo")); @@ -547,7 +539,7 @@ unsigned RequestsWithErrors = 0; bool LastContentsHadErrors = false; bool FileIsRemoved = true; - std::future LastRequestFuture; + std::future LastRequestFuture; }; std::vector ReqStats; @@ -574,7 +566,7 @@ // Some helpers. auto UpdateStatsOnAddDocument = [&](unsigned FileIndex, bool HadErrors, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; if (HadErrors) @@ -587,7 +579,7 @@ }; auto UpdateStatsOnRemoveDocument = [&](unsigned FileIndex, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; Stats.FileIsRemoved = true; @@ -595,7 +587,7 @@ }; auto UpdateStatsOnForceReparse = [&](unsigned FileIndex, - std::future Future) { + std::future Future) { auto &Stats = ReqStats[FileIndex]; Stats.LastRequestFuture = std::move(Future); @@ -607,10 +599,9 @@ auto AddDocument = [&](unsigned FileIndex) { bool ShouldHaveErrors = ShouldHaveErrorsDist(RandGen); - auto Future = - Server.addDocument(Context::empty(), FilePaths[FileIndex], - ShouldHaveErrors ? SourceContentsWithErrors - : SourceContentsWithoutErrors); + auto Future = Server.addDocument( + FilePaths[FileIndex], ShouldHaveErrors ? SourceContentsWithErrors + : SourceContentsWithoutErrors); UpdateStatsOnAddDocument(FileIndex, ShouldHaveErrors, std::move(Future)); }; @@ -626,7 +617,7 @@ if (ReqStats[FileIndex].FileIsRemoved) AddDocument(FileIndex); - auto Future = Server.forceReparse(Context::empty(), FilePaths[FileIndex]); + auto Future = Server.forceReparse(FilePaths[FileIndex]); UpdateStatsOnForceReparse(FileIndex, std::move(Future)); }; @@ -636,8 +627,7 @@ if (ReqStats[FileIndex].FileIsRemoved) AddDocument(FileIndex); - auto Future = - Server.removeDocument(Context::empty(), FilePaths[FileIndex]); + auto Future = Server.removeDocument(FilePaths[FileIndex]); UpdateStatsOnRemoveDocument(FileIndex, std::move(Future)); }; @@ -655,7 +645,7 @@ // cancelled by any subsequent AddDocument/RemoveDocument request to the // same file. Server - .codeComplete(Context::empty(), FilePaths[FileIndex], Pos, + .codeComplete(FilePaths[FileIndex], Pos, clangd::CodeCompleteOptions()) .wait(); }; @@ -667,8 +657,7 @@ AddDocument(FileIndex); Position Pos{LineDist(RandGen), ColumnDist(RandGen)}; - ASSERT_TRUE(!!Server.findDefinitions(Context::empty(), - FilePaths[FileIndex], Pos)); + ASSERT_TRUE(!!Server.findDefinitions(FilePaths[FileIndex], Pos)); }; std::vector> AsyncRequests = { @@ -803,7 +792,7 @@ : StartSecondReparse(std::move(StartSecondReparse)) {} void onDiagnosticsReady( - const Context &Ctx, PathRef File, + PathRef File, Tagged> Diagnostics) override { std::unique_lock Lock(Mutex, std::try_to_lock_t()); @@ -851,11 +840,10 @@ MockCompilationDatabase CDB; ClangdServer Server(CDB, DiagConsumer, FS, 4, /*StorePreamblesInMemory=*/true); - Server.addDocument(Context::empty(), FooCpp, SourceContentsWithErrors); + Server.addDocument(FooCpp, SourceContentsWithErrors); StartSecondReparse.wait(); - auto Future = - Server.addDocument(Context::empty(), FooCpp, SourceContentsWithoutErrors); + auto Future = Server.addDocument(FooCpp, SourceContentsWithoutErrors); Future.wait(); } Index: unittests/clangd/ClangdUnitTests.cpp =================================================================== --- unittests/clangd/ClangdUnitTests.cpp +++ unittests/clangd/ClangdUnitTests.cpp @@ -43,9 +43,9 @@ Cmd.insert(Cmd.begin() + 1, Flags.begin(), Flags.end()); auto CI = createInvocationFromCommandLine(Cmd); auto Buf = MemoryBuffer::getMemBuffer(Code); - auto AST = ParsedAST::Build( - Context::empty(), std::move(CI), nullptr, std::move(Buf), - std::make_shared(), vfs::getRealFileSystem()); + auto AST = ParsedAST::Build(std::move(CI), nullptr, std::move(Buf), + std::make_shared(), + vfs::getRealFileSystem()); assert(AST.hasValue()); return std::move(*AST); } Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -11,7 +11,6 @@ #include "ClangdServer.h" #include "CodeComplete.h" #include "Compiler.h" -#include "Context.h" #include "Matchers.h" #include "Protocol.h" #include "SourceCode.h" @@ -61,10 +60,8 @@ using ::testing::Field; class IgnoreDiagnostics : public DiagnosticsConsumer { - void - onDiagnosticsReady(const Context &Ctx, PathRef File, - Tagged> Diagnostics) override { - } + void onDiagnosticsReady( + PathRef File, Tagged> Diagnostics) override {} }; // GMock helpers for matching completion items. @@ -123,11 +120,9 @@ /*StorePreamblesInMemory=*/true); auto File = getVirtualTestFilePath("foo.cpp"); Annotations Test(Text); - Server.addDocument(Context::empty(), File, Test.code()).wait(); + Server.addDocument(File, Test.code()).wait(); auto CompletionList = - Server.codeComplete(Context::empty(), File, Test.point(), Opts) - .get() - .second.Value; + Server.codeComplete(File, Test.point(), Opts).get().Value; // Sanity-check that filterText is valid. EXPECT_THAT(CompletionList.items, Each(NameContainsFilter())); return CompletionList; @@ -349,15 +344,15 @@ ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), /*StorePreamblesInMemory=*/true); auto File = getVirtualTestFilePath("foo.cpp"); - Server.addDocument(Context::empty(), File, "ignored text!"); + Server.addDocument(File, "ignored text!"); Annotations Example("int cbc; int b = ^;"); - auto Results = Server - .codeComplete(Context::empty(), File, Example.point(), - clangd::CodeCompleteOptions(), - StringRef(Example.code())) - .get() - .second.Value; + auto Results = + Server + .codeComplete(File, Example.point(), clangd::CodeCompleteOptions(), + StringRef(Example.code())) + .get() + .Value; EXPECT_THAT(Results.items, Contains(Named("cbc"))); } @@ -558,28 +553,20 @@ void f() { ns::^; } void f() { ns::preamble().$2^; } )cpp"); - Server.addDocument(Context::empty(), File, Test.code()).wait(); + Server.addDocument(File, Test.code()).wait(); clangd::CodeCompleteOptions Opts = {}; - auto WithoutIndex = - Server.codeComplete(Context::empty(), File, Test.point(), Opts) - .get() - .second.Value; + auto WithoutIndex = Server.codeComplete(File, Test.point(), Opts).get().Value; EXPECT_THAT(WithoutIndex.items, UnorderedElementsAre(Named("local"), Named("preamble"))); auto I = memIndex({var("ns::index")}); Opts.Index = I.get(); - auto WithIndex = - Server.codeComplete(Context::empty(), File, Test.point(), Opts) - .get() - .second.Value; + auto WithIndex = Server.codeComplete(File, Test.point(), Opts).get().Value; EXPECT_THAT(WithIndex.items, UnorderedElementsAre(Named("local"), Named("index"))); auto ClassFromPreamble = - Server.codeComplete(Context::empty(), File, Test.point("2"), Opts) - .get() - .second.Value; + Server.codeComplete(File, Test.point("2"), Opts).get().Value; EXPECT_THAT(ClassFromPreamble.items, Contains(Named("member"))); } @@ -592,7 +579,7 @@ /*BuildDynamicSymbolIndex=*/true); Server - .addDocument(Context::empty(), getVirtualTestFilePath("foo.cpp"), R"cpp( + .addDocument(getVirtualTestFilePath("foo.cpp"), R"cpp( namespace ns { class XYZ {}; void foo(int x) {} } )cpp") .wait(); @@ -606,11 +593,9 @@ } void f() { ns::^ } )cpp"); - Server.addDocument(Context::empty(), File, Test.code()).wait(); + Server.addDocument(File, Test.code()).wait(); - auto Results = Server.codeComplete(Context::empty(), File, Test.point(), {}) - .get() - .second.Value; + auto Results = Server.codeComplete(File, Test.point(), {}).get().Value; // "XYZ" and "foo" are not included in the file being completed but are still // visible through the index. EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class)); @@ -637,8 +622,8 @@ /*StorePreamblesInMemory=*/true); auto File = getVirtualTestFilePath("foo.cpp"); Annotations Test(Text); - Server.addDocument(Context::empty(), File, Test.code()); - auto R = Server.signatureHelp(Context::empty(), File, Test.point()); + Server.addDocument(File, Test.code()); + auto R = Server.signatureHelp(File, Test.point()); assert(R); return R.get().Value; } @@ -707,7 +692,7 @@ class IndexRequestCollector : public SymbolIndex { public: bool - fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req, + fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const override { Requests.push_back(Req); return false; Index: unittests/clangd/FileIndexTests.cpp =================================================================== --- unittests/clangd/FileIndexTests.cpp +++ unittests/clangd/FileIndexTests.cpp @@ -76,8 +76,7 @@ std::vector match(const SymbolIndex &I, const FuzzyFindRequest &Req) { std::vector Matches; - auto Ctx = Context::empty(); - I.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) { + I.fuzzyFind(Req, [&](const Symbol &Sym) { Matches.push_back((Sym.Scope + Sym.Name).str()); }); return Matches; @@ -85,7 +84,6 @@ /// Create an ParsedAST for \p Code. Returns None if \p Code is empty. llvm::Optional build(std::string Path, llvm::StringRef Code) { - Context Ctx = Context::empty(); if (Code.empty()) return llvm::None; const char *Args[] = {"clang", "-xc++", Path.c_str()}; @@ -93,7 +91,7 @@ auto CI = createInvocationFromCommandLine(Args); auto Buf = llvm::MemoryBuffer::getMemBuffer(Code); - auto AST = ParsedAST::Build(Ctx, std::move(CI), nullptr, std::move(Buf), + auto AST = ParsedAST::Build(std::move(CI), nullptr, std::move(Buf), std::make_shared(), vfs::getRealFileSystem()); assert(AST.hasValue()); @@ -102,9 +100,8 @@ TEST(FileIndexTest, IndexAST) { FileIndex M; - auto Ctx = Context::empty(); M.update( - Ctx, "f1", + "f1", build("f1", "namespace ns { void f() {} class X {}; }").getPointer()); FuzzyFindRequest Req; @@ -115,9 +112,8 @@ TEST(FileIndexTest, NoLocal) { FileIndex M; - auto Ctx = Context::empty(); M.update( - Ctx, "f1", + "f1", build("f1", "namespace ns { void f() { int local = 0; } class X {}; }") .getPointer()); @@ -128,12 +124,11 @@ TEST(FileIndexTest, IndexMultiASTAndDeduplicate) { FileIndex M; - auto Ctx = Context::empty(); M.update( - Ctx, "f1", + "f1", build("f1", "namespace ns { void f() {} class X {}; }").getPointer()); M.update( - Ctx, "f2", + "f2", build("f2", "namespace ns { void ff() {} class X {}; }").getPointer()); FuzzyFindRequest Req; @@ -144,9 +139,8 @@ TEST(FileIndexTest, RemoveAST) { FileIndex M; - auto Ctx = Context::empty(); M.update( - Ctx, "f1", + "f1", build("f1", "namespace ns { void f() {} class X {}; }").getPointer()); FuzzyFindRequest Req; @@ -154,21 +148,19 @@ Req.Scopes = {"ns::"}; EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X")); - M.update(Ctx, "f1", nullptr); + M.update("f1", nullptr); EXPECT_THAT(match(M, Req), UnorderedElementsAre()); } TEST(FileIndexTest, RemoveNonExisting) { FileIndex M; - auto Ctx = Context::empty(); - M.update(Ctx, "no", nullptr); + M.update("no", nullptr); EXPECT_THAT(match(M, FuzzyFindRequest()), UnorderedElementsAre()); } TEST(FileIndexTest, IgnoreClassMembers) { FileIndex M; - auto Ctx = Context::empty(); - M.update(Ctx, "f1", + M.update("f1", build("f1", "class X { static int m1; int m2; static void f(); };") .getPointer()); Index: unittests/clangd/IndexTests.cpp =================================================================== --- unittests/clangd/IndexTests.cpp +++ unittests/clangd/IndexTests.cpp @@ -92,8 +92,7 @@ std::vector match(const SymbolIndex &I, const FuzzyFindRequest &Req) { std::vector Matches; - auto Ctx = Context::empty(); - I.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) { + I.fuzzyFind(Req, [&](const Symbol &Sym) { Matches.push_back( (Sym.Scope + (Sym.Scope.empty() ? "" : "::") + Sym.Name).str()); }); Index: unittests/clangd/TUSchedulerTests.cpp =================================================================== --- unittests/clangd/TUSchedulerTests.cpp +++ unittests/clangd/TUSchedulerTests.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "Context.h" #include "TUScheduler.h" #include "TestFS.h" #include "gmock/gmock.h" @@ -20,7 +21,7 @@ using ::testing::Pair; using ::testing::Pointee; -void ignoreUpdate(Context, llvm::Optional>) {} +void ignoreUpdate(llvm::Optional>) {} void ignoreError(llvm::Error Err) { handleAllErrors(std::move(Err), [](const llvm::ErrorInfoBase &) {}); } @@ -52,7 +53,7 @@ auto Missing = getVirtualTestFilePath("missing.cpp"); changeFile(Missing, ""); - S.update(Context::empty(), Added, getInputs(Added, ""), ignoreUpdate); + S.update(Added, getInputs(Added, ""), ignoreUpdate); // Assert each operation for missing file is an error (even if it's available // in VFS). @@ -122,46 +123,61 @@ llvm::StringRef AllContents[] = {Contents1, Contents2, Contents3}; const int AllContentsSize = 3; + // Scheduler may run tasks asynchronously, but should propagate the context. + // We stash a nonce in the context, and verify it in the task. + static Key NonceKey; + int Nonce = 0; + for (int FileI = 0; FileI < FilesCount; ++FileI) { for (int UpdateI = 0; UpdateI < UpdatesPerFile; ++UpdateI) { auto Contents = AllContents[(FileI + UpdateI) % AllContentsSize]; auto File = Files[FileI]; auto Inputs = getInputs(File, Contents.str()); - static Key> FileAndUpdateKey; - auto Ctx = Context::empty().derive(FileAndUpdateKey, - std::make_pair(FileI, UpdateI)); - S.update(std::move(Ctx), File, Inputs, - [FileI, UpdateI, &Mut, &TotalUpdates]( - Context Ctx, - llvm::Optional> Diags) { - EXPECT_THAT(Ctx.get(FileAndUpdateKey), - Pointee(Pair(FileI, UpdateI))); - - std::lock_guard Lock(Mut); - ++TotalUpdates; - }); - - S.runWithAST(File, [Inputs, &Mut, - &TotalASTReads](llvm::Expected AST) { - ASSERT_TRUE((bool)AST); - EXPECT_EQ(AST->Inputs.FS, Inputs.FS); - EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents); - - std::lock_guard Lock(Mut); - ++TotalASTReads; - }); - - S.runWithPreamble( - File, [Inputs, &Mut, &TotalPreambleReads]( - llvm::Expected Preamble) { - ASSERT_TRUE((bool)Preamble); - EXPECT_EQ(Preamble->Inputs.FS, Inputs.FS); - EXPECT_EQ(Preamble->Inputs.Contents, Inputs.Contents); - - std::lock_guard Lock(Mut); - ++TotalPreambleReads; - }); + + { + WithContextValue WithNonce(NonceKey, ++Nonce); + S.update(File, Inputs, + [Nonce, &Mut, &TotalUpdates]( + llvm::Optional> Diags) { + EXPECT_THAT(Context::current().get(NonceKey), + Pointee(Nonce)); + + std::lock_guard Lock(Mut); + ++TotalUpdates; + }); + } + + { + WithContextValue WithNonce(NonceKey, ++Nonce); + S.runWithAST(File, [Inputs, Nonce, &Mut, &TotalASTReads]( + llvm::Expected AST) { + EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce)); + + ASSERT_TRUE((bool)AST); + EXPECT_EQ(AST->Inputs.FS, Inputs.FS); + EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents); + + std::lock_guard Lock(Mut); + ++TotalASTReads; + }); + } + + { + WithContextValue WithNonce(NonceKey, ++Nonce); + S.runWithPreamble( + File, [Inputs, Nonce, &Mut, &TotalPreambleReads]( + llvm::Expected Preamble) { + EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce)); + + ASSERT_TRUE((bool)Preamble); + EXPECT_EQ(Preamble->Inputs.FS, Inputs.FS); + EXPECT_EQ(Preamble->Inputs.Contents, Inputs.Contents); + + std::lock_guard Lock(Mut); + ++TotalPreambleReads; + }); + } } } } // TUScheduler destructor waits for all operations to finish. Index: unittests/clangd/TraceTests.cpp =================================================================== --- unittests/clangd/TraceTests.cpp +++ unittests/clangd/TraceTests.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "Context.h" #include "Trace.h" #include "llvm/ADT/DenseMap.h" @@ -78,8 +77,8 @@ auto JSONTracer = trace::createJSONTracer(OS); trace::Session Session(*JSONTracer); { - trace::Span Tracer(Context::empty(), "A"); - trace::log(Tracer.Ctx, "B"); + trace::Span Tracer("A"); + trace::log("B"); } } Index: unittests/clangd/XRefsTests.cpp =================================================================== --- unittests/clangd/XRefsTests.cpp +++ unittests/clangd/XRefsTests.cpp @@ -43,9 +43,9 @@ auto CI = createInvocationFromCommandLine({"clang", "-xc++", TestFile.c_str()}); auto Buf = MemoryBuffer::getMemBuffer(Code); - auto AST = ParsedAST::Build( - Context::empty(), std::move(CI), nullptr, std::move(Buf), - std::make_shared(), vfs::getRealFileSystem()); + auto AST = ParsedAST::Build(std::move(CI), nullptr, std::move(Buf), + std::make_shared(), + vfs::getRealFileSystem()); assert(AST.hasValue()); return std::move(*AST); } @@ -101,8 +101,7 @@ for (const char *Test : Tests) { Annotations T(Test); auto AST = build(T.code()); - EXPECT_THAT(findDocumentHighlights(Context::empty(), AST, T.point()), - HighlightsFrom(T)) + EXPECT_THAT(findDocumentHighlights(AST, T.point()), HighlightsFrom(T)) << Test; } } @@ -222,7 +221,7 @@ for (const char *Test : Tests) { Annotations T(Test); auto AST = build(T.code()); - EXPECT_THAT(findDefinitions(Context::empty(), AST, T.point()), + EXPECT_THAT(findDefinitions(AST, T.point()), ElementsAre(RangeIs(T.range()))) << Test; }