forked from OSchip/llvm-project
[clangd] Pass Context implicitly using TLS.
Summary: Instead of passing Context explicitly around, we now have a thread-local Context object `Context::current()` which is an implicit argument to every function. Most manipulation of this should use the WithContextValue helper, which augments the current Context to add a single KV pair, and restores the old context on destruction. Advantages are: - less boilerplate in functions that just propagate contexts - reading most code doesn't require understanding context at all, and using context as values in fewer places still - fewer options to pass the "wrong" context when it changes within a scope (e.g. when using Span) - contexts pass through interfaces we can't modify, such as VFS - propagating contexts across threads was slightly tricky (e.g. copy vs move, no move-init in lambdas), and is now encapsulated in the threadpool Disadvantages are all the usual TLS stuff - hidden magic, and potential for higher memory usage on threads that don't use the context. (In practice, it's just one pointer) Reviewers: ilya-biryukov Subscribers: klimek, jkorous-apple, ioeric, cfe-commits Differential Revision: https://reviews.llvm.org/D42517 llvm-svn: 323872
This commit is contained in:
parent
dd48c6b519
commit
d1a7a37c22
|
@ -46,8 +46,8 @@ std::vector<TextEdit> replacementsToEdits(StringRef Code,
|
|||
|
||||
} // 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 @@ void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
|
|||
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 @@ void ClangdLSPServer::onCommand(Ctx C, ExecuteCommandParams &Params) {
|
|||
|
||||
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 @@ void ClangdLSPServer::onRename(Ctx C, RenameParams &Params) {
|
|||
std::vector<TextEdit> 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 @@ void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {
|
|||
});
|
||||
}
|
||||
}
|
||||
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<CompletionList> 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<Path> 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 @@ bool ClangdLSPServer::run(std::istream &In) {
|
|||
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 @@ std::vector<TextEdit> ClangdLSPServer::getFixIts(StringRef File,
|
|||
}
|
||||
|
||||
void ClangdLSPServer::onDiagnosticsReady(
|
||||
const Context &Ctx, PathRef File,
|
||||
Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
|
||||
PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
|
||||
json::ary DiagnosticsJSON;
|
||||
|
||||
DiagnosticToReplacementMap LocalFixIts; // Temporary storage
|
||||
|
|
|
@ -50,32 +50,30 @@ public:
|
|||
private:
|
||||
// Implement DiagnosticsConsumer.
|
||||
virtual void
|
||||
onDiagnosticsReady(const Context &Ctx, PathRef File,
|
||||
onDiagnosticsReady(PathRef File,
|
||||
Tagged<std::vector<DiagWithFixIts>> 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<TextEdit> getFixIts(StringRef File, const clangd::Diagnostic &D);
|
||||
|
||||
|
|
|
@ -116,11 +116,11 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB,
|
|||
// 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 @@ void ClangdServer::setRootPath(PathRef RootPath) {
|
|||
this->RootPath = NewRootPath;
|
||||
}
|
||||
|
||||
std::future<Context> ClangdServer::addDocument(Context Ctx, PathRef File,
|
||||
StringRef Contents) {
|
||||
std::future<void> 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<Context> ClangdServer::removeDocument(Context Ctx, PathRef File) {
|
||||
std::future<void> ClangdServer::removeDocument(PathRef File) {
|
||||
DraftMgr.removeDraft(File);
|
||||
CompileArgs.invalidate(File);
|
||||
|
||||
std::promise<Context> DonePromise;
|
||||
std::future<Context> DoneFuture = DonePromise.get_future();
|
||||
std::promise<void> DonePromise;
|
||||
std::future<void> DoneFuture = DonePromise.get_future();
|
||||
|
||||
auto Callback = BindWithForward(
|
||||
[](Context Ctx, std::promise<Context> DonePromise, llvm::Error Err) {
|
||||
[](std::promise<void> 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<Context> ClangdServer::forceReparse(Context Ctx, PathRef File) {
|
||||
std::future<void> ClangdServer::forceReparse(PathRef File) {
|
||||
auto FileContents = DraftMgr.getDraft(File);
|
||||
assert(FileContents.Draft &&
|
||||
"forceReparse() was called for non-added document");
|
||||
|
@ -177,38 +175,34 @@ std::future<Context> ClangdServer::forceReparse(Context Ctx, PathRef File) {
|
|||
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<std::pair<Context, Tagged<CompletionList>>>
|
||||
ClangdServer::codeComplete(Context Ctx, PathRef File, Position Pos,
|
||||
std::future<Tagged<CompletionList>>
|
||||
ClangdServer::codeComplete(PathRef File, Position Pos,
|
||||
const clangd::CodeCompleteOptions &Opts,
|
||||
llvm::Optional<StringRef> OverridenContents,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) {
|
||||
using ResultType = std::pair<Context, Tagged<CompletionList>>;
|
||||
|
||||
std::promise<ResultType> ResultPromise;
|
||||
|
||||
auto Callback = [](std::promise<ResultType> ResultPromise, Context Ctx,
|
||||
std::promise<Tagged<CompletionList>> ResultPromise;
|
||||
auto Callback = [](std::promise<Tagged<CompletionList>> ResultPromise,
|
||||
Tagged<CompletionList> Result) -> void {
|
||||
ResultPromise.set_value({std::move(Ctx), std::move(Result)});
|
||||
ResultPromise.set_value(std::move(Result));
|
||||
};
|
||||
|
||||
std::future<ResultType> 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<void(Context, Tagged<CompletionList>)> Callback,
|
||||
PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts,
|
||||
UniqueFunction<void(Tagged<CompletionList>)> Callback,
|
||||
llvm::Optional<StringRef> OverridenContents,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) {
|
||||
using CallbackType = UniqueFunction<void(Context, Tagged<CompletionList>)>;
|
||||
using CallbackType = UniqueFunction<void(Tagged<CompletionList>)>;
|
||||
|
||||
auto TaggedFS = FSProvider.getTaggedFileSystem(File);
|
||||
if (UsedFS)
|
||||
|
@ -231,8 +225,8 @@ void ClangdServer::codeComplete(
|
|||
// Copy PCHs to avoid accessing this->PCHs concurrently
|
||||
std::shared_ptr<PCHContainerOperations> PCHs = this->PCHs;
|
||||
auto Task = [PCHs, Pos, TaggedFS, CodeCompleteOpts](
|
||||
Context Ctx, std::string Contents, Path File,
|
||||
CallbackType Callback, llvm::Expected<InputsAndPreamble> IP) {
|
||||
std::string Contents, Path File, CallbackType Callback,
|
||||
llvm::Expected<InputsAndPreamble> IP) {
|
||||
assert(IP && "error when trying to read preamble for codeComplete");
|
||||
auto PreambleData = IP->Preamble;
|
||||
auto &Command = IP->Inputs.CompileCommand;
|
||||
|
@ -240,20 +234,19 @@ void ClangdServer::codeComplete(
|
|||
// 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<Tagged<SignatureHelp>>
|
||||
ClangdServer::signatureHelp(const Context &Ctx, PathRef File, Position Pos,
|
||||
ClangdServer::signatureHelp(PathRef File, Position Pos,
|
||||
llvm::Optional<StringRef> OverridenContents,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) {
|
||||
auto TaggedFS = FSProvider.getTaggedFileSystem(File);
|
||||
|
@ -272,7 +265,7 @@ ClangdServer::signatureHelp(const Context &Ctx, PathRef File, Position Pos,
|
|||
Contents = std::move(*Latest.Draft);
|
||||
}
|
||||
|
||||
auto Action = [=, &Ctx](llvm::Expected<InputsAndPreamble> IP)
|
||||
auto Action = [=](llvm::Expected<InputsAndPreamble> IP)
|
||||
-> Expected<Tagged<SignatureHelp>> {
|
||||
if (!IP)
|
||||
return IP.takeError();
|
||||
|
@ -280,7 +273,7 @@ ClangdServer::signatureHelp(const Context &Ctx, PathRef File, Position Pos,
|
|||
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 @@ ClangdServer::formatOnType(StringRef Code, PathRef File, Position Pos) {
|
|||
}
|
||||
|
||||
Expected<std::vector<tooling::Replacement>>
|
||||
ClangdServer::rename(const Context &Ctx, PathRef File, Position Pos,
|
||||
llvm::StringRef NewName) {
|
||||
ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName) {
|
||||
using RetType = Expected<std::vector<tooling::Replacement>>;
|
||||
auto Action = [=](Expected<InputsAndAST> InpAST) -> RetType {
|
||||
if (!InpAST)
|
||||
|
@ -396,14 +388,14 @@ std::string ClangdServer::dumpAST(PathRef File) {
|
|||
}
|
||||
|
||||
llvm::Expected<Tagged<std::vector<Location>>>
|
||||
ClangdServer::findDefinitions(const Context &Ctx, PathRef File, Position Pos) {
|
||||
ClangdServer::findDefinitions(PathRef File, Position Pos) {
|
||||
auto TaggedFS = FSProvider.getTaggedFileSystem(File);
|
||||
|
||||
using RetType = llvm::Expected<Tagged<std::vector<Location>>>;
|
||||
auto Action = [=, &Ctx](llvm::Expected<InputsAndAST> InpAST) -> RetType {
|
||||
auto Action = [=](llvm::Expected<InputsAndAST> 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<RetType>(WorkScheduler, File, Action);
|
||||
|
@ -483,8 +475,7 @@ ClangdServer::formatCode(llvm::StringRef Code, PathRef File,
|
|||
}
|
||||
|
||||
llvm::Expected<Tagged<std::vector<DocumentHighlight>>>
|
||||
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<llvm::StringError>(
|
||||
|
@ -494,17 +485,17 @@ ClangdServer::findDocumentHighlights(const Context &Ctx, PathRef File,
|
|||
auto TaggedFS = FSProvider.getTaggedFileSystem(File);
|
||||
|
||||
using RetType = llvm::Expected<Tagged<std::vector<DocumentHighlight>>>;
|
||||
auto Action = [=, &Ctx](llvm::Expected<InputsAndAST> InpAST) -> RetType {
|
||||
auto Action = [=](llvm::Expected<InputsAndAST> 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<RetType>(WorkScheduler, File, Action);
|
||||
}
|
||||
|
||||
std::future<Context> ClangdServer::scheduleReparseAndDiags(
|
||||
Context Ctx, PathRef File, VersionedDraft Contents,
|
||||
std::future<void> ClangdServer::scheduleReparseAndDiags(
|
||||
PathRef File, VersionedDraft Contents,
|
||||
Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS) {
|
||||
tooling::CompileCommand Command = CompileArgs.getCompileCommand(File);
|
||||
|
||||
|
@ -514,14 +505,12 @@ std::future<Context> ClangdServer::scheduleReparseAndDiags(
|
|||
Path FileStr = File.str();
|
||||
VFSTag Tag = std::move(TaggedFS.Tag);
|
||||
|
||||
std::promise<Context> DonePromise;
|
||||
std::future<Context> DoneFuture = DonePromise.get_future();
|
||||
std::promise<void> DonePromise;
|
||||
std::future<void> DoneFuture = DonePromise.get_future();
|
||||
|
||||
auto Callback = [this, Version, FileStr,
|
||||
Tag](std::promise<Context> 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<void> 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 @@ std::future<Context> ClangdServer::scheduleReparseAndDiags(
|
|||
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)},
|
||||
|
|
|
@ -71,7 +71,7 @@ public:
|
|||
|
||||
/// Called by ClangdServer when \p Diagnostics for \p File are ready.
|
||||
virtual void
|
||||
onDiagnosticsReady(const Context &Ctx, PathRef File,
|
||||
onDiagnosticsReady(PathRef File,
|
||||
Tagged<std::vector<DiagWithFixIts>> Diagnostics) = 0;
|
||||
};
|
||||
|
||||
|
@ -95,8 +95,6 @@ public:
|
|||
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 @@ public:
|
|||
/// diagnostics) is finished.
|
||||
/// FIXME: don't return futures here, LSP does not require a response for this
|
||||
/// request.
|
||||
std::future<Context> addDocument(Context Ctx, PathRef File,
|
||||
StringRef Contents);
|
||||
std::future<void> 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<Context> removeDocument(Context Ctx, PathRef File);
|
||||
std::future<void> 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<Context> forceReparse(Context Ctx, PathRef File);
|
||||
std::future<void> 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 @@ public:
|
|||
/// 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<std::pair<Context, Tagged<CompletionList>>>
|
||||
codeComplete(Context Ctx, PathRef File, Position Pos,
|
||||
const clangd::CodeCompleteOptions &Opts,
|
||||
llvm::Optional<StringRef> OverridenContents = llvm::None,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> *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<void(Tagged<CompletionList>)> Callback,
|
||||
llvm::Optional<StringRef> OverridenContents = llvm::None,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
|
||||
|
||||
/// DEPRECATED: Please use the callback-based version of codeComplete.
|
||||
std::future<Tagged<CompletionList>>
|
||||
codeComplete(PathRef File, Position Pos,
|
||||
const clangd::CodeCompleteOptions &Opts,
|
||||
UniqueFunction<void(Context, Tagged<CompletionList>)> Callback,
|
||||
llvm::Optional<StringRef> OverridenContents = llvm::None,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
|
||||
|
||||
|
@ -213,13 +207,13 @@ public:
|
|||
/// vfs::FileSystem used for signature help. This method should only be called
|
||||
/// for currently tracked files.
|
||||
llvm::Expected<Tagged<SignatureHelp>>
|
||||
signatureHelp(const Context &Ctx, PathRef File, Position Pos,
|
||||
signatureHelp(PathRef File, Position Pos,
|
||||
llvm::Optional<StringRef> OverridenContents = llvm::None,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
|
||||
|
||||
/// Get definition of symbol at a specified \p Line and \p Column in \p File.
|
||||
llvm::Expected<Tagged<std::vector<Location>>>
|
||||
findDefinitions(const Context &Ctx, PathRef File, Position Pos);
|
||||
llvm::Expected<Tagged<std::vector<Location>>> 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 @@ public:
|
|||
|
||||
/// Get document highlights for a given position.
|
||||
llvm::Expected<Tagged<std::vector<DocumentHighlight>>>
|
||||
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<tooling::Replacements> formatRange(StringRef Code,
|
||||
|
@ -244,8 +238,7 @@ public:
|
|||
|
||||
/// Rename all occurrences of the symbol at the \p Pos in \p File to
|
||||
/// \p NewName.
|
||||
Expected<std::vector<tooling::Replacement>> rename(const Context &Ctx,
|
||||
PathRef File, Position Pos,
|
||||
Expected<std::vector<tooling::Replacement>> 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 @@ private:
|
|||
formatCode(llvm::StringRef Code, PathRef File,
|
||||
ArrayRef<tooling::Range> Ranges);
|
||||
|
||||
std::future<Context>
|
||||
scheduleReparseAndDiags(Context Ctx, PathRef File, VersionedDraft Contents,
|
||||
std::future<void>
|
||||
scheduleReparseAndDiags(PathRef File, VersionedDraft Contents,
|
||||
Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS);
|
||||
|
||||
CompileArgsCache CompileArgs;
|
||||
|
|
|
@ -229,8 +229,7 @@ void clangd::dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) {
|
|||
}
|
||||
|
||||
llvm::Optional<ParsedAST>
|
||||
ParsedAST::Build(const Context &Ctx,
|
||||
std::unique_ptr<clang::CompilerInvocation> CI,
|
||||
ParsedAST::Build(std::unique_ptr<clang::CompilerInvocation> CI,
|
||||
std::shared_ptr<const PreambleData> Preamble,
|
||||
std::unique_ptr<llvm::MemoryBuffer> Buffer,
|
||||
std::shared_ptr<PCHContainerOperations> PCHs,
|
||||
|
@ -254,12 +253,12 @@ ParsedAST::Build(const Context &Ctx,
|
|||
auto Action = llvm::make_unique<ClangdFrontendAction>();
|
||||
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 @@ CppFile::CppFile(PathRef FileName, bool StorePreamblesInMemory,
|
|||
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<std::mutex> Lock(Mutex);
|
||||
LatestAvailablePreamble = nullptr;
|
||||
|
@ -442,11 +440,11 @@ UniqueFunction<void()> CppFile::deferCancelRebuild() {
|
|||
}
|
||||
|
||||
llvm::Optional<std::vector<DiagWithFixIts>>
|
||||
CppFile::rebuild(const Context &Ctx, ParseInputs &&Inputs) {
|
||||
return deferRebuild(std::move(Inputs))(Ctx);
|
||||
CppFile::rebuild(ParseInputs &&Inputs) {
|
||||
return deferRebuild(std::move(Inputs))();
|
||||
}
|
||||
|
||||
UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>(const Context &)>
|
||||
UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>()>
|
||||
CppFile::deferRebuild(ParseInputs &&Inputs) {
|
||||
std::shared_ptr<const PreambleData> OldPreamble;
|
||||
std::shared_ptr<PCHContainerOperations> PCHs;
|
||||
|
@ -483,13 +481,11 @@ CppFile::deferRebuild(ParseInputs &&Inputs) {
|
|||
std::shared_ptr<CppFile> 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<std::vector<DiagWithFixIts>> {
|
||||
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 @@ CppFile::deferRebuild(ParseInputs &&Inputs) {
|
|||
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<DiagWithFixIts> PreambleDiags;
|
||||
StoreDiagsConsumer PreambleDiagnosticsConsumer(/*ref*/ PreambleDiags);
|
||||
|
@ -566,17 +562,15 @@ CppFile::deferRebuild(ParseInputs &&Inputs) {
|
|||
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<PreambleData>(
|
||||
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 @@ CppFile::deferRebuild(ParseInputs &&Inputs) {
|
|||
// Compute updated AST.
|
||||
llvm::Optional<ParsedAST> 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();
|
||||
|
|
|
@ -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 @@ public:
|
|||
/// Attempts to run Clang and store parsed AST. If \p Preamble is non-null
|
||||
/// it is reused during parsing.
|
||||
static llvm::Optional<ParsedAST>
|
||||
Build(const Context &Ctx, std::unique_ptr<clang::CompilerInvocation> CI,
|
||||
Build(std::unique_ptr<clang::CompilerInvocation> CI,
|
||||
std::shared_ptr<const PreambleData> Preamble,
|
||||
std::unique_ptr<llvm::MemoryBuffer> Buffer,
|
||||
std::shared_ptr<PCHContainerOperations> PCHs,
|
||||
|
@ -148,8 +147,7 @@ private:
|
|||
mutable llvm::Optional<ParsedAST> AST;
|
||||
};
|
||||
|
||||
using ASTParsedCallback =
|
||||
std::function<void(const Context &Ctx, PathRef Path, ParsedAST *)>;
|
||||
using ASTParsedCallback = std::function<void(PathRef Path, ParsedAST *)>;
|
||||
|
||||
/// Manages resources, required by clangd. Allows to rebuild file with new
|
||||
/// contents, and provides AST and Preamble for it.
|
||||
|
@ -186,8 +184,7 @@ public:
|
|||
/// 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<std::vector<DiagWithFixIts>> rebuild(const Context &Ctx,
|
||||
ParseInputs &&Inputs);
|
||||
llvm::Optional<std::vector<DiagWithFixIts>> 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 @@ public:
|
|||
/// 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<llvm::Optional<std::vector<DiagWithFixIts>>(const Context &)>
|
||||
UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>()>
|
||||
deferRebuild(ParseInputs &&Inputs);
|
||||
|
||||
/// Returns a future to get the most fresh PreambleData for a file. The
|
||||
|
|
|
@ -633,12 +633,11 @@ struct SemaCompleteInput {
|
|||
|
||||
// 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<CodeCompleteConsumer> Consumer,
|
||||
bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
|
||||
const clang::CodeCompleteOptions &Options,
|
||||
const SemaCompleteInput &Input,
|
||||
llvm::function_ref<void()> Callback = nullptr) {
|
||||
auto Tracer = llvm::make_unique<trace::Span>(Ctx, "Sema completion");
|
||||
auto Tracer = llvm::make_unique<trace::Span>("Sema completion");
|
||||
std::vector<const char *> ArgStrs;
|
||||
for (const auto &S : Input.Command.CommandLine)
|
||||
ArgStrs.push_back(S.c_str());
|
||||
|
@ -690,13 +689,12 @@ bool semaCodeComplete(const Context &Ctx,
|
|||
|
||||
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 @@ bool semaCodeComplete(const Context &Ctx,
|
|||
if (Callback)
|
||||
Callback();
|
||||
|
||||
Tracer = llvm::make_unique<trace::Span>(Ctx, "Sema completion cleanup");
|
||||
Tracer = llvm::make_unique<trace::Span>("Sema completion cleanup");
|
||||
Action.EndSourceFile();
|
||||
|
||||
return true;
|
||||
|
@ -801,8 +799,6 @@ clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
|
|||
// 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<CompletionRecorder> RecorderOwner;
|
||||
|
@ -813,21 +809,22 @@ class CodeCompleteFlow {
|
|||
|
||||
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 @@ public:
|
|||
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 @@ private:
|
|||
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 @@ private:
|
|||
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 @@ private:
|
|||
std::vector<std::pair<CompletionCandidate, CompletionItemScores>>
|
||||
mergeResults(const std::vector<CodeCompletionResult> &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<const Symbol *> UsedIndexResults;
|
||||
|
@ -960,18 +953,18 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
CompletionList codeComplete(const Context &Ctx, PathRef FileName,
|
||||
CompletionList codeComplete(PathRef FileName,
|
||||
const tooling::CompileCommand &Command,
|
||||
PrecompiledPreamble const *Preamble,
|
||||
StringRef Contents, Position Pos,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> VFS,
|
||||
std::shared_ptr<PCHContainerOperations> 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 @@ SignatureHelp signatureHelp(const Context &Ctx, PathRef FileName,
|
|||
Options.IncludeMacros = false;
|
||||
Options.IncludeCodePatterns = false;
|
||||
Options.IncludeBriefComments = true;
|
||||
semaCodeComplete(
|
||||
Ctx, llvm::make_unique<SignatureHelpCollector>(Options, Result), Options,
|
||||
{FileName, Command, Preamble, Contents, Pos, std::move(VFS),
|
||||
std::move(PCHs)});
|
||||
semaCodeComplete(llvm::make_unique<SignatureHelpCollector>(Options, Result),
|
||||
Options,
|
||||
{FileName, Command, Preamble, Contents, Pos, std::move(VFS),
|
||||
std::move(PCHs)});
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ struct CodeCompleteOptions {
|
|||
};
|
||||
|
||||
/// 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 @@ CompletionList codeComplete(const Context &Ctx, PathRef FileName,
|
|||
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,
|
||||
|
|
|
@ -20,5 +20,19 @@ Context::Context(std::shared_ptr<const Data> DataPtr)
|
|||
|
||||
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
|
||||
|
|
|
@ -16,15 +16,28 @@
|
|||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONTEXT_H_
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
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<T> serves two purposes:
|
||||
/// - it provides a lookup key for the context (each Key is unique),
|
||||
/// - it makes lookup type-safe: a Key<T> can only map to a T (or nothing).
|
||||
///
|
||||
/// Example:
|
||||
/// Key<int> RequestID;
|
||||
/// Key<int> 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 Type> class Key {
|
||||
public:
|
||||
static_assert(!std::is_reference<Type>::value,
|
||||
|
@ -45,53 +58,24 @@ public:
|
|||
/// Conceptually, a context is a heterogeneous map<Key<T>, 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<T> class).
|
||||
/// Key<T> 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<int> RequestID;
|
||||
/// Key<int> 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 @@ public:
|
|||
/// (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 @@ public:
|
|||
}
|
||||
|
||||
/// 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 <class Type>
|
||||
Context derive(const Key<Type> &Key,
|
||||
typename std::decay<Type>::type Value) const & {
|
||||
|
@ -198,7 +182,40 @@ private:
|
|||
};
|
||||
|
||||
std::shared_ptr<const Data> 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 <typename T>
|
||||
WithContextValue(const Key<T> &K, typename std::decay<T>::type V)
|
||||
: Restore(Context::current().derive(K, std::move(V))) {}
|
||||
|
||||
// Anonymous values can be used for the destructor side-effect.
|
||||
template <typename T>
|
||||
WithContextValue(T &&V)
|
||||
: Restore(Context::current().derive(std::forward<T>(V))) {}
|
||||
|
||||
private:
|
||||
WithContext Restore;
|
||||
};
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
|
@ -38,8 +38,7 @@ DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -34,14 +34,14 @@ class RequestSpan {
|
|||
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<RequestSpan>(
|
||||
new RequestSpan(Span.Args)));
|
||||
return Context::current().derive(
|
||||
RSKey, std::unique_ptr<RequestSpan>(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 <typename Func> static void attach(const Context &Ctx, Func &&F) {
|
||||
auto *RequestArgs = Ctx.get(RSKey);
|
||||
template <typename Func> static void attach(Func &&F) {
|
||||
auto *RequestArgs = Context::current().get(RSKey);
|
||||
if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args)
|
||||
return;
|
||||
std::lock_guard<std::mutex> Lock((*RequestArgs)->Mu);
|
||||
|
@ -70,8 +70,8 @@ void JSONOutput::writeMessage(const json::Expr &Message) {
|
|||
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<std::mutex> Guard(StreamMutex);
|
||||
Logs << Message << '\n';
|
||||
Logs.flush();
|
||||
|
@ -85,16 +85,15 @@ void JSONOutput::mirrorInput(const Twine &Message) {
|
|||
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::reply(const Context &Ctx, json::Expr &&Result) {
|
|||
});
|
||||
}
|
||||
|
||||
void clangd::replyError(const Context &Ctx, ErrorCode code,
|
||||
const llvm::StringRef &Message) {
|
||||
log(Ctx, "Error " + Twine(static_cast<int>(code)) + ": " + Message);
|
||||
RequestSpan::attach(Ctx, [&](json::obj &Args) {
|
||||
void clangd::replyError(ErrorCode code, const llvm::StringRef &Message) {
|
||||
log("Error " + Twine(static_cast<int>(code)) + ": " + Message);
|
||||
RequestSpan::attach([&](json::obj &Args) {
|
||||
Args["Error"] =
|
||||
json::obj{{"code", static_cast<int>(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::replyError(const Context &Ctx, ErrorCode code,
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ bool JSONRPCDispatcher::call(const json::Expr &Message, JSONOutput &Out) const {
|
|||
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<WithContextValue> 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 @@ void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
|
|||
// 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 @@ void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
|
|||
// 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 @@ void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
|
|||
// 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 @@ void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
|
|||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ public:
|
|||
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 @@ private:
|
|||
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<void(Context, const json::Expr &)>;
|
||||
using Handler = std::function<void(const json::Expr &)>;
|
||||
|
||||
/// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown
|
||||
/// method is received.
|
||||
|
|
|
@ -25,9 +25,9 @@ LoggingSession::LoggingSession(clangd::Logger &Instance) {
|
|||
|
||||
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<std::mutex> Guard(Mu);
|
||||
|
|
|
@ -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 @@ namespace clangd {
|
|||
/// 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 @@ public:
|
|||
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.
|
||||
|
|
|
@ -28,18 +28,16 @@ bool fromJSON(const json::Expr &E, URIForFile &R) {
|
|||
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;
|
||||
|
|
|
@ -24,19 +24,17 @@ namespace {
|
|||
// FooParams should have a fromJSON function.
|
||||
struct HandlerRegisterer {
|
||||
template <typename Param>
|
||||
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<Param>::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<Param>::type P;
|
||||
if (fromJSON(RawParams, P)) {
|
||||
(Callbacks->*Handler)(P);
|
||||
} else {
|
||||
log("Failed to decode " + Method + " request.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
JSONRPCDispatcher &Dispatcher;
|
||||
|
|
|
@ -29,34 +29,28 @@ namespace clangd {
|
|||
// 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,
|
||||
|
|
|
@ -23,9 +23,8 @@ TUScheduler::TUScheduler(unsigned AsyncThreadsCount,
|
|||
Threads(AsyncThreadsCount) {}
|
||||
|
||||
void TUScheduler::update(
|
||||
Context Ctx, PathRef File, ParseInputs Inputs,
|
||||
UniqueFunction<void(Context Ctx,
|
||||
llvm::Optional<std::vector<DiagWithFixIts>>)>
|
||||
PathRef File, ParseInputs Inputs,
|
||||
UniqueFunction<void(llvm::Optional<std::vector<DiagWithFixIts>>)>
|
||||
OnUpdated) {
|
||||
CachedInputs[File] = Inputs;
|
||||
|
||||
|
@ -33,12 +32,12 @@ void TUScheduler::update(
|
|||
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,
|
||||
|
|
|
@ -50,10 +50,9 @@ public:
|
|||
/// 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<void(Context, llvm::Optional<std::vector<DiagWithFixIts>>)>
|
||||
OnUpdated);
|
||||
void update(PathRef File, ParseInputs Inputs,
|
||||
UniqueFunction<void(llvm::Optional<std::vector<DiagWithFixIts>>)>
|
||||
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.
|
||||
|
|
|
@ -17,6 +17,7 @@ ThreadPool::ThreadPool(unsigned AsyncThreadsCount)
|
|||
llvm::set_thread_name(llvm::formatv("scheduler/{0}", I));
|
||||
while (true) {
|
||||
UniqueFunction<void()> Request;
|
||||
Context Ctx;
|
||||
|
||||
// Pick request from the queue
|
||||
{
|
||||
|
@ -33,10 +34,11 @@ ThreadPool::ThreadPool(unsigned AsyncThreadsCount)
|
|||
// 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();
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -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 <condition_variable>
|
||||
#include <deque>
|
||||
|
@ -42,8 +43,9 @@ public:
|
|||
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(Mutex);
|
||||
RequestQueue.push_front(
|
||||
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...));
|
||||
RequestQueue.emplace_front(
|
||||
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...),
|
||||
Context::current().clone());
|
||||
}
|
||||
RequestCV.notify_one();
|
||||
}
|
||||
|
@ -58,8 +60,9 @@ public:
|
|||
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(Mutex);
|
||||
RequestQueue.push_back(
|
||||
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...));
|
||||
RequestQueue.emplace_back(
|
||||
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...),
|
||||
Context::current().clone());
|
||||
}
|
||||
RequestCV.notify_one();
|
||||
}
|
||||
|
@ -74,7 +77,7 @@ private:
|
|||
/// Setting Done to true will make the worker threads terminate.
|
||||
bool Done = false;
|
||||
/// A queue of requests.
|
||||
std::deque<UniqueFunction<void()>> RequestQueue;
|
||||
std::deque<std::pair<UniqueFunction<void()>, Context>> RequestQueue;
|
||||
/// Condition variable to wake up worker threads.
|
||||
std::condition_variable RequestCV;
|
||||
};
|
||||
|
|
|
@ -46,16 +46,14 @@ public:
|
|||
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 @@ std::unique_ptr<EventTracer> createJSONTracer(llvm::raw_ostream &OS,
|
|||
return llvm::make_unique<JSONTracer>(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<json::obj>(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<json::obj>(Args)), Name,
|
||||
Args)
|
||||
: Ctx.clone()) {}
|
||||
RestoreCtx(makeSpanContext(Name, Args)) {}
|
||||
|
||||
} // namespace trace
|
||||
} // namespace clangd
|
||||
|
|
|
@ -39,12 +39,10 @@ public:
|
|||
/// 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 @@ std::unique_ptr<EventTracer> createJSONTracer(llvm::raw_ostream &OS,
|
|||
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 @@ void log(const Context &Ctx, const llvm::Twine &Name);
|
|||
/// 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.
|
||||
|
|
|
@ -148,8 +148,7 @@ getDeclarationLocation(ParsedAST &AST, const SourceRange &ValSourceRange) {
|
|||
|
||||
} // namespace
|
||||
|
||||
std::vector<Location> findDefinitions(const Context &Ctx, ParsedAST &AST,
|
||||
Position Pos) {
|
||||
std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos) {
|
||||
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
|
||||
const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
|
||||
if (!FE)
|
||||
|
@ -260,8 +259,8 @@ private:
|
|||
|
||||
} // namespace
|
||||
|
||||
std::vector<DocumentHighlight>
|
||||
findDocumentHighlights(const Context &Ctx, ParsedAST &AST, Position Pos) {
|
||||
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
|
||||
Position Pos) {
|
||||
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
|
||||
const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
|
||||
if (!FE)
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XREFS_H
|
||||
|
||||
#include "ClangdUnit.h"
|
||||
#include "Context.h"
|
||||
#include "Protocol.h"
|
||||
#include <vector>
|
||||
|
||||
|
@ -22,12 +21,11 @@ namespace clang {
|
|||
namespace clangd {
|
||||
|
||||
/// Get definition of symbol at a specified \p Pos.
|
||||
std::vector<Location> findDefinitions(const Context &Ctx, ParsedAST &AST,
|
||||
Position Pos);
|
||||
std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos);
|
||||
|
||||
/// Returns highlights for all usages of a symbol at \p Pos.
|
||||
std::vector<DocumentHighlight>
|
||||
findDocumentHighlights(const Context &Ctx, ParsedAST &AST, Position Pos);
|
||||
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
|
||||
Position Pos);
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
|
@ -69,7 +69,7 @@ std::shared_ptr<std::vector<const Symbol *>> FileSymbols::allSymbols() {
|
|||
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 @@ void FileIndex::update(const Context &Ctx, PathRef Path, ParsedAST *AST) {
|
|||
}
|
||||
|
||||
bool FileIndex::fuzzyFind(
|
||||
const Context &Ctx, const FuzzyFindRequest &Req,
|
||||
const FuzzyFindRequest &Req,
|
||||
llvm::function_ref<void(const Symbol &)> Callback) const {
|
||||
return Index.fuzzyFind(Ctx, Req, Callback);
|
||||
return Index.fuzzyFind(Req, Callback);
|
||||
}
|
||||
|
||||
} // namespace clangd
|
||||
|
|
|
@ -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 @@ class FileIndex : public SymbolIndex {
|
|||
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<void(const Symbol &)> Callback) const override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -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 @@ public:
|
|||
/// 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<void(const Symbol &)> Callback) const = 0;
|
||||
|
||||
// FIXME: add interfaces for more index use cases:
|
||||
|
|
|
@ -29,7 +29,7 @@ void MemIndex::build(std::shared_ptr<std::vector<const Symbol *>> Syms) {
|
|||
}
|
||||
|
||||
bool MemIndex::fuzzyFind(
|
||||
const Context &Ctx, const FuzzyFindRequest &Req,
|
||||
const FuzzyFindRequest &Req,
|
||||
llvm::function_ref<void(const Symbol &)> Callback) const {
|
||||
assert(!StringRef(Req.Query).contains("::") &&
|
||||
"There must be no :: in query.");
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
static std::unique_ptr<SymbolIndex> build(SymbolSlab Slab);
|
||||
|
||||
bool
|
||||
fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
|
||||
fuzzyFind(const FuzzyFindRequest &Req,
|
||||
llvm::function_ref<void(const Symbol &)> Callback) const override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -24,7 +24,7 @@ class MergedIndex : public SymbolIndex {
|
|||
// - 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<void(const Symbol &)> 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 @@ class MergedIndex : public SymbolIndex {
|
|||
// 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<SymbolID> 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);
|
||||
|
|
|
@ -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 @@ static bool diagsContainErrors(ArrayRef<DiagWithFixIts> Diagnostics) {
|
|||
class ErrorCheckingDiagConsumer : public DiagnosticsConsumer {
|
||||
public:
|
||||
void
|
||||
onDiagnosticsReady(const Context &Ctx, PathRef File,
|
||||
onDiagnosticsReady(PathRef File,
|
||||
Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {
|
||||
bool HadError = diagsContainErrors(Diagnostics.Value);
|
||||
|
||||
|
@ -143,8 +142,7 @@ protected:
|
|||
|
||||
// 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 @@ int b = a;
|
|||
FS.ExpectedFile = FooCpp;
|
||||
|
||||
// To sync reparses before checking for errors.
|
||||
std::future<Context> ParseFuture;
|
||||
std::future<void> 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 @@ int b = a;
|
|||
FS.ExpectedFile = FooCpp;
|
||||
|
||||
// To sync reparses before checking for errors.
|
||||
std::future<Context> ParseFuture;
|
||||
std::future<void> 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 @@ TEST_F(ClangdVFSTest, CheckVersions) {
|
|||
// 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 @@ mock_string x;
|
|||
|
||||
// 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 <string>
|
||||
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 @@ struct bar { T x; };
|
|||
|
||||
// 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 @@ struct Something {
|
|||
|
||||
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 @@ int d;
|
|||
TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
|
||||
|
||||
void onDiagnosticsReady(
|
||||
const Context &Ctx, PathRef File,
|
||||
PathRef File,
|
||||
Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {
|
||||
StringRef FileIndexStr = llvm::sys::path::stem(File);
|
||||
ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
|
||||
|
@ -547,7 +539,7 @@ int d;
|
|||
unsigned RequestsWithErrors = 0;
|
||||
bool LastContentsHadErrors = false;
|
||||
bool FileIsRemoved = true;
|
||||
std::future<Context> LastRequestFuture;
|
||||
std::future<void> LastRequestFuture;
|
||||
};
|
||||
|
||||
std::vector<RequestStats> ReqStats;
|
||||
|
@ -574,7 +566,7 @@ int d;
|
|||
|
||||
// Some helpers.
|
||||
auto UpdateStatsOnAddDocument = [&](unsigned FileIndex, bool HadErrors,
|
||||
std::future<Context> Future) {
|
||||
std::future<void> Future) {
|
||||
auto &Stats = ReqStats[FileIndex];
|
||||
|
||||
if (HadErrors)
|
||||
|
@ -587,7 +579,7 @@ int d;
|
|||
};
|
||||
|
||||
auto UpdateStatsOnRemoveDocument = [&](unsigned FileIndex,
|
||||
std::future<Context> Future) {
|
||||
std::future<void> Future) {
|
||||
auto &Stats = ReqStats[FileIndex];
|
||||
|
||||
Stats.FileIsRemoved = true;
|
||||
|
@ -595,7 +587,7 @@ int d;
|
|||
};
|
||||
|
||||
auto UpdateStatsOnForceReparse = [&](unsigned FileIndex,
|
||||
std::future<Context> Future) {
|
||||
std::future<void> Future) {
|
||||
auto &Stats = ReqStats[FileIndex];
|
||||
|
||||
Stats.LastRequestFuture = std::move(Future);
|
||||
|
@ -607,10 +599,9 @@ int d;
|
|||
|
||||
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 @@ int d;
|
|||
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 @@ int d;
|
|||
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 @@ int d;
|
|||
// 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 @@ int d;
|
|||
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<std::function<void()>> AsyncRequests = {
|
||||
|
@ -803,7 +792,7 @@ TEST_F(ClangdThreadingTest, NoConcurrentDiagnostics) {
|
|||
: StartSecondReparse(std::move(StartSecondReparse)) {}
|
||||
|
||||
void onDiagnosticsReady(
|
||||
const Context &Ctx, PathRef File,
|
||||
PathRef File,
|
||||
Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {
|
||||
|
||||
std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
|
||||
|
@ -851,11 +840,10 @@ int d;
|
|||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -43,9 +43,9 @@ ParsedAST build(StringRef Code, std::vector<const char*> Flags = {}) {
|
|||
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<PCHContainerOperations>(), vfs::getRealFileSystem());
|
||||
auto AST = ParsedAST::Build(std::move(CI), nullptr, std::move(Buf),
|
||||
std::make_shared<PCHContainerOperations>(),
|
||||
vfs::getRealFileSystem());
|
||||
assert(AST.hasValue());
|
||||
return std::move(*AST);
|
||||
}
|
||||
|
|
|
@ -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::UnorderedElementsAre;
|
|||
using ::testing::Field;
|
||||
|
||||
class IgnoreDiagnostics : public DiagnosticsConsumer {
|
||||
void
|
||||
onDiagnosticsReady(const Context &Ctx, PathRef File,
|
||||
Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {
|
||||
}
|
||||
void onDiagnosticsReady(
|
||||
PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {}
|
||||
};
|
||||
|
||||
// GMock helpers for matching completion items.
|
||||
|
@ -123,11 +120,9 @@ CompletionList completions(StringRef Text,
|
|||
/*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 @@ TEST(CompletionTest, CheckContentsOverride) {
|
|||
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 @@ TEST(CompletionTest, IndexSuppressesPreambleCompletions) {
|
|||
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 @@ TEST(CompletionTest, DynamicIndexMultiFile) {
|
|||
/*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 @@ TEST(CompletionTest, DynamicIndexMultiFile) {
|
|||
}
|
||||
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 @@ SignatureHelp signatures(StringRef Text) {
|
|||
/*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 @@ TEST(SignatureHelpTest, ActiveArg) {
|
|||
class IndexRequestCollector : public SymbolIndex {
|
||||
public:
|
||||
bool
|
||||
fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
|
||||
fuzzyFind(const FuzzyFindRequest &Req,
|
||||
llvm::function_ref<void(const Symbol &)> Callback) const override {
|
||||
Requests.push_back(Req);
|
||||
return false;
|
||||
|
|
|
@ -76,8 +76,7 @@ TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
|
|||
std::vector<std::string> match(const SymbolIndex &I,
|
||||
const FuzzyFindRequest &Req) {
|
||||
std::vector<std::string> 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 @@ std::vector<std::string> match(const SymbolIndex &I,
|
|||
|
||||
/// Create an ParsedAST for \p Code. Returns None if \p Code is empty.
|
||||
llvm::Optional<ParsedAST> 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 @@ llvm::Optional<ParsedAST> build(std::string Path, llvm::StringRef Code) {
|
|||
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<PCHContainerOperations>(),
|
||||
vfs::getRealFileSystem());
|
||||
assert(AST.hasValue());
|
||||
|
@ -102,9 +100,8 @@ llvm::Optional<ParsedAST> build(std::string Path, llvm::StringRef Code) {
|
|||
|
||||
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, IndexAST) {
|
|||
|
||||
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, NoLocal) {
|
|||
|
||||
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, IndexMultiASTAndDeduplicate) {
|
|||
|
||||
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 @@ TEST(FileIndexTest, RemoveAST) {
|
|||
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());
|
||||
|
||||
|
|
|
@ -92,8 +92,7 @@ generateNumSymbols(int Begin, int End,
|
|||
std::vector<std::string> match(const SymbolIndex &I,
|
||||
const FuzzyFindRequest &Req) {
|
||||
std::vector<std::string> 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());
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Context.h"
|
||||
#include "TUScheduler.h"
|
||||
#include "TestFS.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
@ -20,7 +21,7 @@ namespace clangd {
|
|||
using ::testing::Pair;
|
||||
using ::testing::Pointee;
|
||||
|
||||
void ignoreUpdate(Context, llvm::Optional<std::vector<DiagWithFixIts>>) {}
|
||||
void ignoreUpdate(llvm::Optional<std::vector<DiagWithFixIts>>) {}
|
||||
void ignoreError(llvm::Error Err) {
|
||||
handleAllErrors(std::move(Err), [](const llvm::ErrorInfoBase &) {});
|
||||
}
|
||||
|
@ -52,7 +53,7 @@ TEST_F(TUSchedulerTests, MissingFiles) {
|
|||
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 @@ TEST_F(TUSchedulerTests, ManyUpdates) {
|
|||
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<int> 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<std::pair<int, int>> 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<std::vector<DiagWithFixIts>> Diags) {
|
||||
EXPECT_THAT(Ctx.get(FileAndUpdateKey),
|
||||
Pointee(Pair(FileI, UpdateI)));
|
||||
|
||||
std::lock_guard<std::mutex> Lock(Mut);
|
||||
++TotalUpdates;
|
||||
});
|
||||
{
|
||||
WithContextValue WithNonce(NonceKey, ++Nonce);
|
||||
S.update(File, Inputs,
|
||||
[Nonce, &Mut, &TotalUpdates](
|
||||
llvm::Optional<std::vector<DiagWithFixIts>> Diags) {
|
||||
EXPECT_THAT(Context::current().get(NonceKey),
|
||||
Pointee(Nonce));
|
||||
|
||||
S.runWithAST(File, [Inputs, &Mut,
|
||||
&TotalASTReads](llvm::Expected<InputsAndAST> AST) {
|
||||
ASSERT_TRUE((bool)AST);
|
||||
EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
|
||||
EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
|
||||
std::lock_guard<std::mutex> Lock(Mut);
|
||||
++TotalUpdates;
|
||||
});
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(Mut);
|
||||
++TotalASTReads;
|
||||
});
|
||||
{
|
||||
WithContextValue WithNonce(NonceKey, ++Nonce);
|
||||
S.runWithAST(File, [Inputs, Nonce, &Mut, &TotalASTReads](
|
||||
llvm::Expected<InputsAndAST> AST) {
|
||||
EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
|
||||
|
||||
S.runWithPreamble(
|
||||
File, [Inputs, &Mut, &TotalPreambleReads](
|
||||
llvm::Expected<InputsAndPreamble> Preamble) {
|
||||
ASSERT_TRUE((bool)Preamble);
|
||||
EXPECT_EQ(Preamble->Inputs.FS, Inputs.FS);
|
||||
EXPECT_EQ(Preamble->Inputs.Contents, Inputs.Contents);
|
||||
ASSERT_TRUE((bool)AST);
|
||||
EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
|
||||
EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
|
||||
|
||||
std::lock_guard<std::mutex> Lock(Mut);
|
||||
++TotalPreambleReads;
|
||||
});
|
||||
std::lock_guard<std::mutex> Lock(Mut);
|
||||
++TotalASTReads;
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
WithContextValue WithNonce(NonceKey, ++Nonce);
|
||||
S.runWithPreamble(
|
||||
File, [Inputs, Nonce, &Mut, &TotalPreambleReads](
|
||||
llvm::Expected<InputsAndPreamble> 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<std::mutex> Lock(Mut);
|
||||
++TotalPreambleReads;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} // TUScheduler destructor waits for all operations to finish.
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Context.h"
|
||||
#include "Trace.h"
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
@ -78,8 +77,8 @@ TEST(TraceTest, SmokeTest) {
|
|||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,9 +43,9 @@ ParsedAST build(StringRef Code) {
|
|||
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<PCHContainerOperations>(), vfs::getRealFileSystem());
|
||||
auto AST = ParsedAST::Build(std::move(CI), nullptr, std::move(Buf),
|
||||
std::make_shared<PCHContainerOperations>(),
|
||||
vfs::getRealFileSystem());
|
||||
assert(AST.hasValue());
|
||||
return std::move(*AST);
|
||||
}
|
||||
|
@ -101,8 +101,7 @@ TEST(HighlightsTest, All) {
|
|||
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 @@ TEST(GoToDefinition, All) {
|
|||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue