[clangd] Wait for compile command in ASTWorker instead of ClangdServer
Summary: This makes addDocument non-blocking and would also allow code completion (in fallback mode) to run when worker waits for the compile command. Reviewers: sammccall, ilya-biryukov Reviewed By: ilya-biryukov Subscribers: javed.absar, MaskRay, jkorous, arphaman, kadircet, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D60607 llvm-svn: 358400
This commit is contained in:
parent
2be3f868f9
commit
9ef03dd20a
|
@ -109,7 +109,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
|
|||
const FileSystemProvider &FSProvider,
|
||||
DiagnosticsConsumer &DiagConsumer,
|
||||
const Options &Opts)
|
||||
: CDB(CDB), FSProvider(FSProvider),
|
||||
: FSProvider(FSProvider),
|
||||
DynamicIdx(Opts.BuildDynamicSymbolIndex
|
||||
? new FileIndex(Opts.HeavyweightDynamicSymbolIndex)
|
||||
: nullptr),
|
||||
|
@ -121,7 +121,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
|
|||
// is parsed.
|
||||
// FIXME(ioeric): this can be slow and we may be able to index on less
|
||||
// critical paths.
|
||||
WorkScheduler(Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory,
|
||||
WorkScheduler(CDB, Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory,
|
||||
llvm::make_unique<UpdateIndexCallbacks>(DynamicIdx.get(),
|
||||
DiagConsumer),
|
||||
Opts.UpdateDebounce, Opts.RetentionPolicy) {
|
||||
|
@ -155,12 +155,8 @@ void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
|
|||
Opts.ClangTidyOpts = ClangTidyOptProvider->getOptions(File);
|
||||
Opts.SuggestMissingIncludes = SuggestMissingIncludes;
|
||||
|
||||
// FIXME: some build systems like Bazel will take time to preparing
|
||||
// environment to build the file, it would be nice if we could emit a
|
||||
// "PreparingBuild" status to inform users, it is non-trivial given the
|
||||
// current implementation.
|
||||
// Compile command is set asynchronously during update, as it can be slow.
|
||||
ParseInputs Inputs;
|
||||
Inputs.CompileCommand = getCompileCommand(File);
|
||||
Inputs.FS = FSProvider.getFileSystem();
|
||||
Inputs.Contents = Contents;
|
||||
Inputs.Opts = std::move(Opts);
|
||||
|
@ -543,14 +539,6 @@ void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
|
|||
WorkScheduler.runWithAST("Type Hierarchy", File, Bind(Action, std::move(CB)));
|
||||
}
|
||||
|
||||
tooling::CompileCommand ClangdServer::getCompileCommand(PathRef File) {
|
||||
trace::Span Span("GetCompileCommand");
|
||||
llvm::Optional<tooling::CompileCommand> C = CDB.getCompileCommand(File);
|
||||
if (!C) // FIXME: Suppress diagnostics? Let the user know?
|
||||
C = CDB.getFallbackCommand(File);
|
||||
return std::move(*C);
|
||||
}
|
||||
|
||||
void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
|
||||
// FIXME: Do nothing for now. This will be used for indexing and potentially
|
||||
// invalidating other caches.
|
||||
|
|
|
@ -266,9 +266,6 @@ private:
|
|||
formatCode(llvm::StringRef Code, PathRef File,
|
||||
ArrayRef<tooling::Range> Ranges);
|
||||
|
||||
tooling::CompileCommand getCompileCommand(PathRef File);
|
||||
|
||||
const GlobalCompilationDatabase &CDB;
|
||||
const FileSystemProvider &FSProvider;
|
||||
|
||||
Path ResourceDir;
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H
|
||||
|
||||
#include "../clang-tidy/ClangTidyOptions.h"
|
||||
#include "GlobalCompilationDatabase.h"
|
||||
#include "index/Index.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/CompilerInvocation.h"
|
||||
#include "clang/Frontend/PrecompiledPreamble.h"
|
||||
#include "clang/Tooling/CompilationDatabase.h"
|
||||
|
||||
|
|
|
@ -63,10 +63,11 @@ GlobalCompilationDatabase::getFallbackCommand(PathRef File) const {
|
|||
if (llvm::sys::path::extension(File) == ".h")
|
||||
Argv.push_back("-xobjective-c++-header");
|
||||
Argv.push_back(File);
|
||||
return tooling::CompileCommand(llvm::sys::path::parent_path(File),
|
||||
llvm::sys::path::filename(File),
|
||||
std::move(Argv),
|
||||
/*Output=*/"");
|
||||
tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File),
|
||||
llvm::sys::path::filename(File), std::move(Argv),
|
||||
/*Output=*/"");
|
||||
Cmd.Heuristic = "clangd fallback";
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
DirectoryBasedGlobalCompilationDatabase::
|
||||
|
|
|
@ -43,10 +43,13 @@
|
|||
|
||||
#include "TUScheduler.h"
|
||||
#include "Cancellation.h"
|
||||
#include "Compiler.h"
|
||||
#include "GlobalCompilationDatabase.h"
|
||||
#include "Logger.h"
|
||||
#include "Trace.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang/Frontend/CompilerInvocation.h"
|
||||
#include "clang/Tooling/CompilationDatabase.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
|
@ -154,10 +157,10 @@ class ASTWorkerHandle;
|
|||
/// worker.
|
||||
class ASTWorker {
|
||||
friend class ASTWorkerHandle;
|
||||
ASTWorker(PathRef FileName, TUScheduler::ASTCache &LRUCache,
|
||||
Semaphore &Barrier, bool RunSync,
|
||||
steady_clock::duration UpdateDebounce,
|
||||
bool StorePreamblesInMemory, ParsingCallbacks &Callbacks);
|
||||
ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB,
|
||||
TUScheduler::ASTCache &LRUCache, Semaphore &Barrier, bool RunSync,
|
||||
steady_clock::duration UpdateDebounce, bool StorePreamblesInMemory,
|
||||
ParsingCallbacks &Callbacks);
|
||||
|
||||
public:
|
||||
/// Create a new ASTWorker and return a handle to it.
|
||||
|
@ -165,12 +168,11 @@ public:
|
|||
/// is null, all requests will be processed on the calling thread
|
||||
/// synchronously instead. \p Barrier is acquired when processing each
|
||||
/// request, it is used to limit the number of actively running threads.
|
||||
static ASTWorkerHandle create(PathRef FileName,
|
||||
TUScheduler::ASTCache &IdleASTs,
|
||||
AsyncTaskRunner *Tasks, Semaphore &Barrier,
|
||||
steady_clock::duration UpdateDebounce,
|
||||
bool StorePreamblesInMemory,
|
||||
ParsingCallbacks &Callbacks);
|
||||
static ASTWorkerHandle
|
||||
create(PathRef FileName, const GlobalCompilationDatabase &CDB,
|
||||
TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks,
|
||||
Semaphore &Barrier, steady_clock::duration UpdateDebounce,
|
||||
bool StorePreamblesInMemory, ParsingCallbacks &Callbacks);
|
||||
~ASTWorker();
|
||||
|
||||
void update(ParseInputs Inputs, WantDiagnostics);
|
||||
|
@ -185,6 +187,9 @@ public:
|
|||
/// It may be delivered immediately, or later on the worker thread.
|
||||
void getCurrentPreamble(
|
||||
llvm::unique_function<void(std::shared_ptr<const PreambleData>)>);
|
||||
/// Returns compile command from the current file inputs.
|
||||
tooling::CompileCommand getCurrentCompileCommand() const;
|
||||
|
||||
/// Wait for the first build of preamble to finish. Preamble itself can be
|
||||
/// accessed via getPossiblyStalePreamble(). Note that this function will
|
||||
/// return after an unsuccessful build of the preamble too, i.e. result of
|
||||
|
@ -215,6 +220,10 @@ private:
|
|||
Deadline scheduleLocked();
|
||||
/// Should the first task in the queue be skipped instead of run?
|
||||
bool shouldSkipHeadLocked() const;
|
||||
/// This is private because `FileInputs.FS` is not thread-safe and thus not
|
||||
/// safe to share. Callers should make sure not to expose `FS` via a public
|
||||
/// interface.
|
||||
std::shared_ptr<const ParseInputs> getCurrentFileInputs() const;
|
||||
|
||||
struct Request {
|
||||
llvm::unique_function<void()> Action;
|
||||
|
@ -231,6 +240,7 @@ private:
|
|||
const steady_clock::duration UpdateDebounce;
|
||||
/// File that ASTWorker is responsible for.
|
||||
const Path FileName;
|
||||
const GlobalCompilationDatabase &CDB;
|
||||
/// Whether to keep the built preambles in memory or on disk.
|
||||
const bool StorePreambleInMemory;
|
||||
/// Callback invoked when preamble or main file AST is built.
|
||||
|
@ -239,13 +249,15 @@ private:
|
|||
TUStatus Status;
|
||||
|
||||
Semaphore &Barrier;
|
||||
/// Inputs, corresponding to the current state of AST.
|
||||
ParseInputs FileInputs;
|
||||
/// Whether the diagnostics for the current FileInputs were reported to the
|
||||
/// users before.
|
||||
bool DiagsWereReported = false;
|
||||
/// Guards members used by both TUScheduler and the worker thread.
|
||||
mutable std::mutex Mutex;
|
||||
/// File inputs, currently being used by the worker.
|
||||
/// Inputs are written and read by the worker thread, compile command can also
|
||||
/// be consumed by clients of ASTWorker.
|
||||
std::shared_ptr<const ParseInputs> FileInputs; /* GUARDED_BY(Mutex) */
|
||||
std::shared_ptr<const PreambleData> LastBuiltPreamble; /* GUARDED_BY(Mutex) */
|
||||
/// Becomes ready when the first preamble build finishes.
|
||||
Notification PreambleWasBuilt;
|
||||
|
@ -306,14 +318,13 @@ private:
|
|||
std::shared_ptr<ASTWorker> Worker;
|
||||
};
|
||||
|
||||
ASTWorkerHandle ASTWorker::create(PathRef FileName,
|
||||
TUScheduler::ASTCache &IdleASTs,
|
||||
AsyncTaskRunner *Tasks, Semaphore &Barrier,
|
||||
steady_clock::duration UpdateDebounce,
|
||||
bool StorePreamblesInMemory,
|
||||
ParsingCallbacks &Callbacks) {
|
||||
ASTWorkerHandle
|
||||
ASTWorker::create(PathRef FileName, const GlobalCompilationDatabase &CDB,
|
||||
TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks,
|
||||
Semaphore &Barrier, steady_clock::duration UpdateDebounce,
|
||||
bool StorePreamblesInMemory, ParsingCallbacks &Callbacks) {
|
||||
std::shared_ptr<ASTWorker> Worker(
|
||||
new ASTWorker(FileName, IdleASTs, Barrier, /*RunSync=*/!Tasks,
|
||||
new ASTWorker(FileName, CDB, IdleASTs, Barrier, /*RunSync=*/!Tasks,
|
||||
UpdateDebounce, StorePreamblesInMemory, Callbacks));
|
||||
if (Tasks)
|
||||
Tasks->runAsync("worker:" + llvm::sys::path::filename(FileName),
|
||||
|
@ -322,15 +333,23 @@ ASTWorkerHandle ASTWorker::create(PathRef FileName,
|
|||
return ASTWorkerHandle(std::move(Worker));
|
||||
}
|
||||
|
||||
ASTWorker::ASTWorker(PathRef FileName, TUScheduler::ASTCache &LRUCache,
|
||||
Semaphore &Barrier, bool RunSync,
|
||||
steady_clock::duration UpdateDebounce,
|
||||
ASTWorker::ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB,
|
||||
TUScheduler::ASTCache &LRUCache, Semaphore &Barrier,
|
||||
bool RunSync, steady_clock::duration UpdateDebounce,
|
||||
bool StorePreamblesInMemory, ParsingCallbacks &Callbacks)
|
||||
: IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce),
|
||||
FileName(FileName), StorePreambleInMemory(StorePreamblesInMemory),
|
||||
FileName(FileName), CDB(CDB),
|
||||
StorePreambleInMemory(StorePreamblesInMemory),
|
||||
Callbacks(Callbacks), Status{TUAction(TUAction::Idle, ""),
|
||||
TUStatus::BuildDetails()},
|
||||
Barrier(Barrier), Done(false) {}
|
||||
Barrier(Barrier), Done(false) {
|
||||
auto Inputs = std::make_shared<ParseInputs>();
|
||||
// Set a fallback command because compile command can be accessed before
|
||||
// `Inputs` is initialized. Other fields are only used after initialization
|
||||
// from client inputs.
|
||||
Inputs->CompileCommand = CDB.getFallbackCommand(FileName);
|
||||
FileInputs = std::move(Inputs);
|
||||
}
|
||||
|
||||
ASTWorker::~ASTWorker() {
|
||||
// Make sure we remove the cached AST, if any.
|
||||
|
@ -345,14 +364,28 @@ ASTWorker::~ASTWorker() {
|
|||
void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
|
||||
llvm::StringRef TaskName = "Update";
|
||||
auto Task = [=]() mutable {
|
||||
// Get the actual command as `Inputs` does not have a command.
|
||||
// FIXME: some build systems like Bazel will take time to preparing
|
||||
// environment to build the file, it would be nice if we could emit a
|
||||
// "PreparingBuild" status to inform users, it is non-trivial given the
|
||||
// current implementation.
|
||||
if (auto Cmd = CDB.getCompileCommand(FileName))
|
||||
Inputs.CompileCommand = *Cmd;
|
||||
else
|
||||
// FIXME: consider using old command if it's not a fallback one.
|
||||
Inputs.CompileCommand = CDB.getFallbackCommand(FileName);
|
||||
auto PrevInputs = getCurrentFileInputs();
|
||||
// Will be used to check if we can avoid rebuilding the AST.
|
||||
bool InputsAreTheSame =
|
||||
std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==
|
||||
std::tie(PrevInputs->CompileCommand, PrevInputs->Contents) ==
|
||||
std::tie(Inputs.CompileCommand, Inputs.Contents);
|
||||
|
||||
tooling::CompileCommand OldCommand = std::move(FileInputs.CompileCommand);
|
||||
tooling::CompileCommand OldCommand = PrevInputs->CompileCommand;
|
||||
bool PrevDiagsWereReported = DiagsWereReported;
|
||||
FileInputs = Inputs;
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(Mutex);
|
||||
FileInputs = std::make_shared<ParseInputs>(Inputs);
|
||||
}
|
||||
DiagsWereReported = false;
|
||||
emitTUStatus({TUAction::BuildingPreamble, TaskName});
|
||||
log("Updating file {0} with command {1}\n[{2}]\n{3}", FileName,
|
||||
|
@ -475,15 +508,16 @@ void ASTWorker::runWithAST(
|
|||
if (isCancelled())
|
||||
return Action(llvm::make_error<CancelledError>());
|
||||
llvm::Optional<std::unique_ptr<ParsedAST>> AST = IdleASTs.take(this);
|
||||
auto CurrentInputs = getCurrentFileInputs();
|
||||
if (!AST) {
|
||||
std::unique_ptr<CompilerInvocation> Invocation =
|
||||
buildCompilerInvocation(FileInputs);
|
||||
buildCompilerInvocation(*CurrentInputs);
|
||||
// Try rebuilding the AST.
|
||||
llvm::Optional<ParsedAST> NewAST =
|
||||
Invocation
|
||||
? buildAST(FileName,
|
||||
llvm::make_unique<CompilerInvocation>(*Invocation),
|
||||
FileInputs, getPossiblyStalePreamble())
|
||||
*CurrentInputs, getPossiblyStalePreamble())
|
||||
: None;
|
||||
AST = NewAST ? llvm::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
|
||||
}
|
||||
|
@ -494,7 +528,7 @@ void ASTWorker::runWithAST(
|
|||
if (!*AST)
|
||||
return Action(llvm::make_error<llvm::StringError>(
|
||||
"invalid AST", llvm::errc::invalid_argument));
|
||||
Action(InputsAndAST{FileInputs, **AST});
|
||||
Action(InputsAndAST{*CurrentInputs, **AST});
|
||||
};
|
||||
startTask(Name, Bind(Task, std::move(Action)),
|
||||
/*UpdateType=*/None);
|
||||
|
@ -536,6 +570,16 @@ void ASTWorker::getCurrentPreamble(
|
|||
|
||||
void ASTWorker::waitForFirstPreamble() const { PreambleWasBuilt.wait(); }
|
||||
|
||||
std::shared_ptr<const ParseInputs> ASTWorker::getCurrentFileInputs() const {
|
||||
std::unique_lock<std::mutex> Lock(Mutex);
|
||||
return FileInputs;
|
||||
}
|
||||
|
||||
tooling::CompileCommand ASTWorker::getCurrentCompileCommand() const {
|
||||
std::unique_lock<std::mutex> Lock(Mutex);
|
||||
return FileInputs->CompileCommand;
|
||||
}
|
||||
|
||||
std::size_t ASTWorker::getUsedBytes() const {
|
||||
// Note that we don't report the size of ASTs currently used for processing
|
||||
// the in-flight requests. We used this information for debugging purposes
|
||||
|
@ -774,16 +818,16 @@ FileStatus TUStatus::render(PathRef File) const {
|
|||
struct TUScheduler::FileData {
|
||||
/// Latest inputs, passed to TUScheduler::update().
|
||||
std::string Contents;
|
||||
tooling::CompileCommand Command;
|
||||
ASTWorkerHandle Worker;
|
||||
};
|
||||
|
||||
TUScheduler::TUScheduler(unsigned AsyncThreadsCount,
|
||||
TUScheduler::TUScheduler(const GlobalCompilationDatabase &CDB,
|
||||
unsigned AsyncThreadsCount,
|
||||
bool StorePreamblesInMemory,
|
||||
std::unique_ptr<ParsingCallbacks> Callbacks,
|
||||
std::chrono::steady_clock::duration UpdateDebounce,
|
||||
ASTRetentionPolicy RetentionPolicy)
|
||||
: StorePreamblesInMemory(StorePreamblesInMemory),
|
||||
: CDB(CDB), StorePreamblesInMemory(StorePreamblesInMemory),
|
||||
Callbacks(Callbacks ? move(Callbacks)
|
||||
: llvm::make_unique<ParsingCallbacks>()),
|
||||
Barrier(AsyncThreadsCount),
|
||||
|
@ -822,13 +866,13 @@ void TUScheduler::update(PathRef File, ParseInputs Inputs,
|
|||
if (!FD) {
|
||||
// Create a new worker to process the AST-related tasks.
|
||||
ASTWorkerHandle Worker = ASTWorker::create(
|
||||
File, *IdleASTs, WorkerThreads ? WorkerThreads.getPointer() : nullptr,
|
||||
Barrier, UpdateDebounce, StorePreamblesInMemory, *Callbacks);
|
||||
FD = std::unique_ptr<FileData>(new FileData{
|
||||
Inputs.Contents, Inputs.CompileCommand, std::move(Worker)});
|
||||
File, CDB, *IdleASTs,
|
||||
WorkerThreads ? WorkerThreads.getPointer() : nullptr, Barrier,
|
||||
UpdateDebounce, StorePreamblesInMemory, *Callbacks);
|
||||
FD = std::unique_ptr<FileData>(
|
||||
new FileData{Inputs.Contents, std::move(Worker)});
|
||||
} else {
|
||||
FD->Contents = Inputs.Contents;
|
||||
FD->Command = Inputs.CompileCommand;
|
||||
}
|
||||
FD->Worker->update(std::move(Inputs), WantDiags);
|
||||
}
|
||||
|
@ -876,7 +920,8 @@ void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,
|
|||
SPAN_ATTACH(Tracer, "file", File);
|
||||
std::shared_ptr<const PreambleData> Preamble =
|
||||
It->second->Worker->getPossiblyStalePreamble();
|
||||
Action(InputsAndPreamble{It->second->Contents, It->second->Command,
|
||||
Action(InputsAndPreamble{It->second->Contents,
|
||||
It->second->Worker->getCurrentCompileCommand(),
|
||||
Preamble.get()});
|
||||
return;
|
||||
}
|
||||
|
@ -923,7 +968,7 @@ void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,
|
|||
PreambleTasks->runAsync(
|
||||
"task:" + llvm::sys::path::filename(File),
|
||||
Bind(Task, std::string(Name), std::string(File), It->second->Contents,
|
||||
It->second->Command,
|
||||
Worker->getCurrentCompileCommand(),
|
||||
Context::current().derive(kFileBeingProcessed, File),
|
||||
std::move(ConsistentPreamble), std::move(Action)));
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "ClangdUnit.h"
|
||||
#include "Function.h"
|
||||
#include "GlobalCompilationDatabase.h"
|
||||
#include "Threading.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
|
@ -125,7 +126,8 @@ public:
|
|||
/// FIXME(sammccall): pull out a scheduler options struct.
|
||||
class TUScheduler {
|
||||
public:
|
||||
TUScheduler(unsigned AsyncThreadsCount, bool StorePreamblesInMemory,
|
||||
TUScheduler(const GlobalCompilationDatabase &CDB, unsigned AsyncThreadsCount,
|
||||
bool StorePreamblesInMemory,
|
||||
std::unique_ptr<ParsingCallbacks> ASTCallbacks,
|
||||
std::chrono::steady_clock::duration UpdateDebounce,
|
||||
ASTRetentionPolicy RetentionPolicy);
|
||||
|
@ -141,10 +143,11 @@ public:
|
|||
std::vector<Path> getFilesWithCachedAST() const;
|
||||
|
||||
/// Schedule an update for \p File. Adds \p File to a list of tracked files if
|
||||
/// \p File was not part of it before.
|
||||
/// If diagnostics are requested (Yes), and the context is cancelled before
|
||||
/// they are prepared, they may be skipped if eventual-consistency permits it
|
||||
/// (i.e. WantDiagnostics is downgraded to Auto).
|
||||
/// \p File was not part of it before. The compile command in \p Inputs is
|
||||
/// ignored; worker queries CDB to get the actual compile command.
|
||||
/// If diagnostics are requested (Yes), and the context is cancelled
|
||||
/// before they are prepared, they may be skipped if eventual-consistency
|
||||
/// permits it (i.e. WantDiagnostics is downgraded to Auto).
|
||||
void update(PathRef File, ParseInputs Inputs, WantDiagnostics WD);
|
||||
|
||||
/// Remove \p File from the list of tracked files and schedule removal of its
|
||||
|
@ -217,6 +220,7 @@ public:
|
|||
static llvm::Optional<llvm::StringRef> getFileBeingProcessedInContext();
|
||||
|
||||
private:
|
||||
const GlobalCompilationDatabase &CDB;
|
||||
const bool StorePreamblesInMemory;
|
||||
std::unique_ptr<ParsingCallbacks> Callbacks; // not nullptr
|
||||
Semaphore Barrier;
|
||||
|
|
|
@ -1099,6 +1099,64 @@ TEST_F(ClangdVFSTest, FallbackWhenPreambleIsNotReady) {
|
|||
Field(&CodeCompletion::Scope, "ns::"))));
|
||||
}
|
||||
|
||||
TEST_F(ClangdVFSTest, FallbackWhenWaitingForCompileCommand) {
|
||||
MockFSProvider FS;
|
||||
ErrorCheckingDiagConsumer DiagConsumer;
|
||||
// Returns compile command only when notified.
|
||||
class DelayedCompilationDatabase : public GlobalCompilationDatabase {
|
||||
public:
|
||||
DelayedCompilationDatabase(Notification &CanReturnCommand)
|
||||
: CanReturnCommand(CanReturnCommand) {}
|
||||
|
||||
llvm::Optional<tooling::CompileCommand>
|
||||
getCompileCommand(PathRef File, ProjectInfo * = nullptr) const override {
|
||||
// FIXME: make this timeout and fail instead of waiting forever in case
|
||||
// something goes wrong.
|
||||
CanReturnCommand.wait();
|
||||
auto FileName = llvm::sys::path::filename(File);
|
||||
std::vector<std::string> CommandLine = {"clangd", "-ffreestanding", File};
|
||||
return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
|
||||
FileName, std::move(CommandLine), "")};
|
||||
}
|
||||
|
||||
std::vector<std::string> ExtraClangFlags;
|
||||
|
||||
private:
|
||||
Notification &CanReturnCommand;
|
||||
};
|
||||
|
||||
Notification CanReturnCommand;
|
||||
DelayedCompilationDatabase CDB(CanReturnCommand);
|
||||
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
||||
|
||||
auto FooCpp = testPath("foo.cpp");
|
||||
Annotations Code(R"cpp(
|
||||
namespace ns { int xyz; }
|
||||
using namespace ns;
|
||||
int main() {
|
||||
xy^
|
||||
})cpp");
|
||||
FS.Files[FooCpp] = FooCpp;
|
||||
Server.addDocument(FooCpp, Code.code());
|
||||
|
||||
// Sleep for some time to make sure code completion is not run because update
|
||||
// hasn't been scheduled.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
auto Opts = clangd::CodeCompleteOptions();
|
||||
Opts.AllowFallback = true;
|
||||
|
||||
auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts));
|
||||
EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);
|
||||
|
||||
CanReturnCommand.notify();
|
||||
ASSERT_TRUE(Server.blockUntilIdleForTest());
|
||||
EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Code.point(),
|
||||
clangd::CodeCompleteOptions()))
|
||||
.Completions,
|
||||
ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
|
||||
Field(&CodeCompletion::Scope, "ns::"))));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
|
@ -1386,7 +1386,7 @@ TEST(CompletionTest, NonDocComments) {
|
|||
// FIXME: Auto-completion in a template requires disabling delayed template
|
||||
// parsing.
|
||||
CDB.ExtraClangFlags.push_back("-fno-delayed-template-parsing");
|
||||
Server.addDocument(FooCpp, Source.code(), WantDiagnostics::Yes);
|
||||
runAddDocument(Server, FooCpp, Source.code(), WantDiagnostics::Yes);
|
||||
CodeCompleteResult Completions = cantFail(runCodeComplete(
|
||||
Server, FooCpp, Source.point(), clangd::CodeCompleteOptions()));
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@ namespace clang {
|
|||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AllOf;
|
||||
using ::testing::AnyOf;
|
||||
using ::testing::Each;
|
||||
using ::testing::ElementsAre;
|
||||
|
@ -103,7 +101,7 @@ Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
|
|||
TUSchedulerTests::DiagsCallbackKey;
|
||||
|
||||
TEST_F(TUSchedulerTests, MissingFiles) {
|
||||
TUScheduler S(getDefaultAsyncThreadsCount(),
|
||||
TUScheduler S(CDB, getDefaultAsyncThreadsCount(),
|
||||
/*StorePreamblesInMemory=*/true, /*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
|
@ -154,7 +152,7 @@ TEST_F(TUSchedulerTests, WantDiagnostics) {
|
|||
// thread until we've scheduled them all.
|
||||
Notification Ready;
|
||||
TUScheduler S(
|
||||
getDefaultAsyncThreadsCount(),
|
||||
CDB, getDefaultAsyncThreadsCount(),
|
||||
/*StorePreamblesInMemory=*/true, captureDiags(),
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
|
@ -184,7 +182,7 @@ TEST_F(TUSchedulerTests, WantDiagnostics) {
|
|||
TEST_F(TUSchedulerTests, Debounce) {
|
||||
std::atomic<int> CallbackCount(0);
|
||||
{
|
||||
TUScheduler S(getDefaultAsyncThreadsCount(),
|
||||
TUScheduler S(CDB, getDefaultAsyncThreadsCount(),
|
||||
/*StorePreamblesInMemory=*/true, captureDiags(),
|
||||
/*UpdateDebounce=*/std::chrono::seconds(1),
|
||||
ASTRetentionPolicy());
|
||||
|
@ -220,7 +218,7 @@ TEST_F(TUSchedulerTests, PreambleConsistency) {
|
|||
{
|
||||
Notification InconsistentReadDone; // Must live longest.
|
||||
TUScheduler S(
|
||||
getDefaultAsyncThreadsCount(), /*StorePreamblesInMemory=*/true,
|
||||
CDB, getDefaultAsyncThreadsCount(), /*StorePreamblesInMemory=*/true,
|
||||
/*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
|
@ -277,7 +275,7 @@ TEST_F(TUSchedulerTests, Cancellation) {
|
|||
{
|
||||
Notification Proceed; // Ensure we schedule everything.
|
||||
TUScheduler S(
|
||||
getDefaultAsyncThreadsCount(), /*StorePreamblesInMemory=*/true,
|
||||
CDB, getDefaultAsyncThreadsCount(), /*StorePreamblesInMemory=*/true,
|
||||
/*ASTCallbacks=*/captureDiags(),
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
|
@ -346,7 +344,7 @@ TEST_F(TUSchedulerTests, ManyUpdates) {
|
|||
|
||||
// Run TUScheduler and collect some stats.
|
||||
{
|
||||
TUScheduler S(getDefaultAsyncThreadsCount(),
|
||||
TUScheduler S(CDB, getDefaultAsyncThreadsCount(),
|
||||
/*StorePreamblesInMemory=*/true, captureDiags(),
|
||||
/*UpdateDebounce=*/std::chrono::milliseconds(50),
|
||||
ASTRetentionPolicy());
|
||||
|
@ -437,10 +435,11 @@ TEST_F(TUSchedulerTests, EvictedAST) {
|
|||
std::atomic<int> BuiltASTCounter(0);
|
||||
ASTRetentionPolicy Policy;
|
||||
Policy.MaxRetainedASTs = 2;
|
||||
TUScheduler S(
|
||||
/*AsyncThreadsCount=*/1, /*StorePreambleInMemory=*/true,
|
||||
/*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), Policy);
|
||||
TUScheduler S(CDB,
|
||||
/*AsyncThreadsCount=*/1, /*StorePreambleInMemory=*/true,
|
||||
/*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
Policy);
|
||||
|
||||
llvm::StringLiteral SourceContents = R"cpp(
|
||||
int* a;
|
||||
|
@ -487,11 +486,11 @@ TEST_F(TUSchedulerTests, EvictedAST) {
|
|||
}
|
||||
|
||||
TEST_F(TUSchedulerTests, EmptyPreamble) {
|
||||
TUScheduler S(
|
||||
/*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true,
|
||||
/*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
TUScheduler S(CDB,
|
||||
/*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true,
|
||||
/*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
|
||||
auto Foo = testPath("foo.cpp");
|
||||
auto Header = testPath("foo.h");
|
||||
|
@ -532,11 +531,11 @@ TEST_F(TUSchedulerTests, EmptyPreamble) {
|
|||
TEST_F(TUSchedulerTests, RunWaitsForPreamble) {
|
||||
// Testing strategy: we update the file and schedule a few preamble reads at
|
||||
// the same time. All reads should get the same non-null preamble.
|
||||
TUScheduler S(
|
||||
/*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true,
|
||||
/*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
TUScheduler S(CDB,
|
||||
/*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true,
|
||||
/*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
auto Foo = testPath("foo.cpp");
|
||||
auto NonEmptyPreamble = R"cpp(
|
||||
#define FOO 1
|
||||
|
@ -564,11 +563,11 @@ TEST_F(TUSchedulerTests, RunWaitsForPreamble) {
|
|||
}
|
||||
|
||||
TEST_F(TUSchedulerTests, NoopOnEmptyChanges) {
|
||||
TUScheduler S(
|
||||
/*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(),
|
||||
/*StorePreambleInMemory=*/true, captureDiags(),
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
TUScheduler S(CDB,
|
||||
/*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(),
|
||||
/*StorePreambleInMemory=*/true, captureDiags(),
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
|
||||
auto Source = testPath("foo.cpp");
|
||||
auto Header = testPath("foo.h");
|
||||
|
@ -617,11 +616,11 @@ TEST_F(TUSchedulerTests, NoopOnEmptyChanges) {
|
|||
}
|
||||
|
||||
TEST_F(TUSchedulerTests, NoChangeDiags) {
|
||||
TUScheduler S(
|
||||
/*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(),
|
||||
/*StorePreambleInMemory=*/true, captureDiags(),
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
TUScheduler S(CDB,
|
||||
/*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(),
|
||||
/*StorePreambleInMemory=*/true, captureDiags(),
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
|
||||
auto FooCpp = testPath("foo.cpp");
|
||||
auto Contents = "int a; int b;";
|
||||
|
@ -652,7 +651,7 @@ TEST_F(TUSchedulerTests, NoChangeDiags) {
|
|||
}
|
||||
|
||||
TEST_F(TUSchedulerTests, Run) {
|
||||
TUScheduler S(/*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(),
|
||||
TUScheduler S(CDB, /*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(),
|
||||
/*StorePreambleInMemory=*/true, /*ASTCallbacks=*/nullptr,
|
||||
/*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(),
|
||||
ASTRetentionPolicy());
|
||||
|
|
Loading…
Reference in New Issue