ilya-biryukov updated this revision to Diff 148822.
ilya-biryukov added a comment.
- Remove ASTBuilder, make everything a helper function
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D47063
Files:
clangd/ClangdUnit.cpp
clangd/ClangdUnit.h
clangd/TUScheduler.cpp
clangd/TUScheduler.h
test/clangd/trace.test
unittests/clangd/FileIndexTests.cpp
Index: unittests/clangd/FileIndexTests.cpp
===================================================================
--- unittests/clangd/FileIndexTests.cpp
+++ unittests/clangd/FileIndexTests.cpp
@@ -11,6 +11,7 @@
#include "TestFS.h"
#include "TestTU.h"
#include "index/FileIndex.h"
+#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CompilationDatabase.h"
@@ -208,18 +209,6 @@
TEST(FileIndexTest, RebuildWithPreamble) {
auto FooCpp = testPath("foo.cpp");
auto FooH = testPath("foo.h");
- FileIndex Index;
- bool IndexUpdated = false;
- CppFile File("foo.cpp", /*StorePreambleInMemory=*/true,
- std::make_shared<PCHContainerOperations>(),
- [&Index, &IndexUpdated](PathRef FilePath, ASTContext &Ctx,
- std::shared_ptr<Preprocessor> PP) {
- EXPECT_FALSE(IndexUpdated)
- << "Expected only a single index update";
- IndexUpdated = true;
- Index.update(FilePath, &Ctx, std::move(PP));
- });
-
// Preparse ParseInputs.
ParseInputs PI;
PI.CompileCommand.Directory = testRoot();
@@ -243,7 +232,19 @@
)cpp";
// Rebuild the file.
- File.rebuild(std::move(PI));
+ auto CI = buildCompilerInvocation(PI);
+
+ FileIndex Index;
+ bool IndexUpdated = false;
+ buildPreamble(
+ FooCpp, *CI, /*OldPreamble=*/nullptr, tooling::CompileCommand(), PI,
+ std::make_shared<PCHContainerOperations>(), /*StoreInMemory=*/true,
+ [&Index, &IndexUpdated](PathRef FilePath, ASTContext &Ctx,
+ std::shared_ptr<Preprocessor> PP) {
+ EXPECT_FALSE(IndexUpdated) << "Expected only a single index update";
+ IndexUpdated = true;
+ Index.update(FilePath, &Ctx, std::move(PP));
+ });
ASSERT_TRUE(IndexUpdated);
// Check the index contains symbols from the preamble, but not from the main
Index: test/clangd/trace.test
===================================================================
--- test/clangd/trace.test
+++ test/clangd/trace.test
@@ -8,14 +8,14 @@
# CHECK: "args": {
# CHECK: "File": "{{.*(/|\\)}}foo.c"
# CHECK: },
-# CHECK: "name": "Preamble",
+# CHECK: "name": "BuildPreamble",
# CHECK: "ph": "X",
# CHECK: }
# CHECK: {
# CHECK: "args": {
# CHECK: "File": "{{.*(/|\\)}}foo.c"
# CHECK: },
-# CHECK: "name": "Build",
+# CHECK: "name": "BuildAST",
# CHECK: "ph": "X",
# CHECK: }
# CHECK: },
Index: clangd/TUScheduler.h
===================================================================
--- clangd/TUScheduler.h
+++ clangd/TUScheduler.h
@@ -42,6 +42,15 @@
/// within a bounded amount of time.
};
+/// Configuration of the AST retention policy. This only covers retention of
+/// *idle* ASTs. If queue has operations requiring the AST, they might be
+/// kept in memory.
+struct ASTRetentionParams {
+ /// Maximum number of ASTs to be retained in memory when there are no pending
+ /// requests for them.
+ unsigned MaxRetainedASTs = 3;
+};
+
/// Handles running tasks for ClangdServer and managing the resources (e.g.,
/// preambles and ASTs) for opened files.
/// TUScheduler is not thread-safe, only one thread should be providing updates
@@ -53,7 +62,8 @@
public:
TUScheduler(unsigned AsyncThreadsCount, bool StorePreamblesInMemory,
PreambleParsedCallback PreambleCallback,
- std::chrono::steady_clock::duration UpdateDebounce);
+ std::chrono::steady_clock::duration UpdateDebounce,
+ ASTRetentionParams RetentionConfig = {});
~TUScheduler();
/// Returns estimated memory usage for each of the currently open files.
@@ -99,11 +109,18 @@
/// This class stores per-file data in the Files map.
struct FileData;
+public:
+ /// Responsible for retaining and rebuilding idle ASTs. An implementation is
+ /// an LRU cache.
+ class ASTCache;
+
+private:
const bool StorePreamblesInMemory;
const std::shared_ptr<PCHContainerOperations> PCHOps;
const PreambleParsedCallback PreambleCallback;
Semaphore Barrier;
llvm::StringMap<std::unique_ptr<FileData>> Files;
+ std::unique_ptr<ASTCache> IdleASTs;
// None when running tasks synchronously and non-None when running tasks
// asynchronously.
llvm::Optional<AsyncTaskRunner> PreambleTasks;
Index: clangd/TUScheduler.cpp
===================================================================
--- clangd/TUScheduler.cpp
+++ clangd/TUScheduler.cpp
@@ -45,16 +45,88 @@
#include "TUScheduler.h"
#include "Logger.h"
#include "Trace.h"
+#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PCHContainerOperations.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Path.h"
+#include <algorithm>
#include <memory>
#include <queue>
#include <thread>
namespace clang {
namespace clangd {
using std::chrono::steady_clock;
+
+namespace {
+class ASTWorker;
+}
+
+/// An LRU cache of idle ASTs.
+/// Because we want to limit the overall number of these we retain, the cache
+/// owns ASTs (and may evict them) while their workers are idle.
+/// Workers borrow ASTs when active, and return them when done.
+class TUScheduler::ASTCache {
+public:
+ using Key = const ASTWorker *;
+
+ ASTCache(unsigned MaxRetainedASTs) : MaxRetainedASTs(MaxRetainedASTs) {}
+
+ /// Returns result of getUsedBytes() for the AST cached by \p K.
+ /// If no AST is cached, 0 is returned.
+ bool getUsedBytes(Key K) {
+ std::lock_guard<std::mutex> Lock(Mut);
+ auto It = findByKey(K);
+ if (It == LRU.end() || !It->second)
+ return 0;
+ return It->second->getUsedBytes();
+ }
+
+ /// Store the value in the pool, possibly removing the last used AST.
+ /// The value should not be in the pool when this function is called.
+ void put(Key K, std::unique_ptr<ParsedAST> V) {
+ std::unique_lock<std::mutex> Lock(Mut);
+ assert(findByKey(K) == LRU.end());
+
+ LRU.insert(LRU.begin(), {K, std::move(V)});
+ if (LRU.size() <= MaxRetainedASTs)
+ return;
+ // We're past the limit, remove the last element.
+ std::unique_ptr<ParsedAST> ForCleanup = std::move(LRU.back().second);
+ LRU.pop_back();
+ // Run the expensive destructor outside the lock.
+ Lock.unlock();
+ ForCleanup.reset();
+ }
+
+ /// Returns the cached value for \p K, or llvm::None if the value is not in
+ /// the cache anymore.
+ llvm::Optional<std::unique_ptr<ParsedAST>> take(Key K) {
+ std::unique_lock<std::mutex> Lock(Mut);
+ auto Existing = findByKey(K);
+ if (Existing == LRU.end())
+ return llvm::None;
+ std::unique_ptr<ParsedAST> V = std::move(Existing->second);
+ LRU.erase(Existing);
+ return V;
+ }
+
+private:
+ using KVPair = std::pair<Key, std::unique_ptr<ParsedAST>>;
+
+ std::vector<KVPair>::iterator findByKey(Key K) {
+ return std::find_if(LRU.begin(), LRU.end(),
+ [K](const KVPair &P) { return P.first == K; });
+ }
+
+ std::mutex Mut;
+ unsigned MaxRetainedASTs;
+ /// Items sorted in LRU order, i.e. first item is the most recently accessed
+ /// one.
+ std::vector<KVPair> LRU; /* GUARDED_BY(Mut) */
+};
+
namespace {
class ASTWorkerHandle;
@@ -70,18 +142,26 @@
/// worker.
class ASTWorker {
friend class ASTWorkerHandle;
- ASTWorker(llvm::StringRef File, Semaphore &Barrier, CppFile AST, bool RunSync,
- steady_clock::duration UpdateDebounce);
+ ASTWorker(PathRef FileName, TUScheduler::ASTCache &LRUCache,
+ Semaphore &Barrier, bool RunSync,
+ steady_clock::duration UpdateDebounce,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ bool StorePreamblesInMemory,
+ PreambleParsedCallback PreambleCallback);
public:
/// Create a new ASTWorker and return a handle to it.
/// The processing thread is spawned using \p Tasks. However, when \p Tasks
/// is null, all requests will be processed on the calling thread
/// synchronously instead. \p Barrier is acquired when processing each
/// request, it is be used to limit the number of actively running threads.
- static ASTWorkerHandle Create(llvm::StringRef File, AsyncTaskRunner *Tasks,
- Semaphore &Barrier, CppFile AST,
- steady_clock::duration UpdateDebounce);
+ static ASTWorkerHandle Create(PathRef FileName,
+ TUScheduler::ASTCache &IdleASTs,
+ AsyncTaskRunner *Tasks, Semaphore &Barrier,
+ steady_clock::duration UpdateDebounce,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ bool StorePreamblesInMemory,
+ PreambleParsedCallback PreambleCallback);
~ASTWorker();
void update(ParseInputs Inputs, WantDiagnostics,
@@ -119,22 +199,30 @@
llvm::Optional<WantDiagnostics> UpdateType;
};
- const std::string File;
+ /// Handles retention of ASTs.
+ TUScheduler::ASTCache &IdleASTs;
const bool RunSync;
- // Time to wait after an update to see whether another update obsoletes it.
+ /// Time to wait after an update to see whether another update obsoletes it.
const steady_clock::duration UpdateDebounce;
+ /// File that ASTWorker is reponsible for.
+ const Path FileName;
+ /// Whether to keep the built preambles in memory or on disk.
+ const bool StorePreambleInMemory;
+ /// Callback, passed to the preamble builder.
+ const PreambleParsedCallback PreambleCallback;
+ /// Helper class required to build the ASTs.
+ const std::shared_ptr<PCHContainerOperations> PCHs;
Semaphore &Barrier;
- // AST and FileInputs are only accessed on the processing thread from run().
- CppFile AST;
- // Inputs, corresponding to the current state of AST.
+ /// Inputs, corresponding to the current state of AST.
ParseInputs FileInputs;
- // Guards members used by both TUScheduler and the worker thread.
+ /// CompilerInvocation used for FileInputs.
+ std::unique_ptr<CompilerInvocation> Invocation;
+ /// Size of the last AST
+ /// Guards members used by both TUScheduler and the worker thread.
mutable std::mutex Mutex;
std::shared_ptr<const PreambleData> LastBuiltPreamble; /* GUARDED_BY(Mutex) */
- // Result of getUsedBytes() after the last rebuild or read of AST.
- std::size_t LastASTSize; /* GUARDED_BY(Mutex) */
- // Set to true to signal run() to finish processing.
+ /// Set to true to signal run() to finish processing.
bool Done; /* GUARDED_BY(Mutex) */
std::deque<Request> Requests; /* GUARDED_BY(Mutex) */
mutable std::condition_variable RequestsCV;
@@ -182,27 +270,37 @@
std::shared_ptr<ASTWorker> Worker;
};
-ASTWorkerHandle ASTWorker::Create(llvm::StringRef File, AsyncTaskRunner *Tasks,
- Semaphore &Barrier, CppFile AST,
- steady_clock::duration UpdateDebounce) {
+ASTWorkerHandle ASTWorker::Create(PathRef FileName,
+ TUScheduler::ASTCache &IdleASTs,
+ AsyncTaskRunner *Tasks, Semaphore &Barrier,
+ steady_clock::duration UpdateDebounce,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ bool StorePreamblesInMemory,
+ PreambleParsedCallback PreambleCallback) {
std::shared_ptr<ASTWorker> Worker(new ASTWorker(
- File, Barrier, std::move(AST), /*RunSync=*/!Tasks, UpdateDebounce));
+ FileName, IdleASTs, Barrier, /*RunSync=*/!Tasks, UpdateDebounce,
+ std::move(PCHs), StorePreamblesInMemory, std::move(PreambleCallback)));
if (Tasks)
- Tasks->runAsync("worker:" + llvm::sys::path::filename(File),
+ Tasks->runAsync("worker:" + llvm::sys::path::filename(FileName),
[Worker]() { Worker->run(); });
return ASTWorkerHandle(std::move(Worker));
}
-ASTWorker::ASTWorker(llvm::StringRef File, Semaphore &Barrier, CppFile AST,
- bool RunSync, steady_clock::duration UpdateDebounce)
- : File(File), RunSync(RunSync), UpdateDebounce(UpdateDebounce),
- Barrier(Barrier), AST(std::move(AST)), Done(false) {
- if (RunSync)
- return;
-}
+ASTWorker::ASTWorker(PathRef FileName, TUScheduler::ASTCache &LRUCache,
+ Semaphore &Barrier, bool RunSync,
+ steady_clock::duration UpdateDebounce,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ bool StorePreamblesInMemory,
+ PreambleParsedCallback PreambleCallback)
+ : IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce),
+ FileName(FileName), StorePreambleInMemory(StorePreamblesInMemory),
+ PreambleCallback(std::move(PreambleCallback)), PCHs(std::move(PCHs)),
+ Barrier(Barrier), Done(false) {}
ASTWorker::~ASTWorker() {
+ // Make sure we remove the cached AST, if any.
+ IdleASTs.take(this);
#ifndef NDEBUG
std::lock_guard<std::mutex> Lock(Mutex);
assert(Done && "handle was not destroyed");
@@ -213,20 +311,41 @@
void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags,
UniqueFunction<void(std::vector<Diag>)> OnUpdated) {
auto Task = [=](decltype(OnUpdated) OnUpdated) mutable {
+ tooling::CompileCommand OldCommand = std::move(FileInputs.CompileCommand);
FileInputs = Inputs;
- auto Diags = AST.rebuild(std::move(Inputs));
+ // Remove the old AST if it's still in cache.
+ IdleASTs.take(this);
+
+ log("Updating file " + FileName + " with command [" +
+ Inputs.CompileCommand.Directory + "] " +
+ llvm::join(Inputs.CompileCommand.CommandLine, " "));
+ // Rebuild the preamble and the AST.
+ Invocation = buildCompilerInvocation(Inputs);
+ if (!Invocation) {
+ log("Could not build CompilerInvocation for file " + FileName);
+ return;
+ }
+ std::shared_ptr<const PreambleData> NewPreamble = buildPreamble(
+ FileName, *Invocation, getPossiblyStalePreamble(), OldCommand, Inputs,
+ PCHs, StorePreambleInMemory, PreambleCallback);
{
std::lock_guard<std::mutex> Lock(Mutex);
- if (AST.getPreamble())
- LastBuiltPreamble = AST.getPreamble();
- LastASTSize = AST.getUsedBytes();
+ if (NewPreamble)
+ LastBuiltPreamble = NewPreamble;
}
+ // Build the AST for diagnostics.
+ llvm::Optional<ParsedAST> AST =
+ buildAST(FileName, llvm::make_unique<CompilerInvocation>(*Invocation),
+ Inputs, NewPreamble, PCHs);
// We want to report the diagnostics even if this update was cancelled.
// It seems more useful than making the clients wait indefinitely if they
// spam us with updates.
- if (Diags && WantDiags != WantDiagnostics::No)
- OnUpdated(std::move(*Diags));
+ if (WantDiags != WantDiagnostics::No && AST)
+ OnUpdated(AST->getDiagnostics());
+ // Stash the AST in the cache for further use.
+ IdleASTs.put(this,
+ AST ? llvm::make_unique<ParsedAST>(std::move(*AST)) : nullptr);
};
startTask("Update", Bind(Task, std::move(OnUpdated)), WantDiags);
@@ -236,20 +355,26 @@
llvm::StringRef Name,
UniqueFunction<void(llvm::Expected<InputsAndAST>)> Action) {
auto Task = [=](decltype(Action) Action) {
- ParsedAST *ActualAST = AST.getAST();
- if (!ActualAST) {
- Action(llvm::make_error<llvm::StringError>("invalid AST",
- llvm::errc::invalid_argument));
- return;
+ llvm::Optional<std::unique_ptr<ParsedAST>> AST = IdleASTs.take(this);
+ if (!AST) {
+ // Try rebuilding the AST.
+ llvm::Optional<ParsedAST> NewAST =
+ Invocation
+ ? buildAST(FileName,
+ llvm::make_unique<CompilerInvocation>(*Invocation),
+ FileInputs, getPossiblyStalePreamble(), PCHs)
+ : llvm::None;
+ AST = NewAST ? llvm::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
}
- Action(InputsAndAST{FileInputs, *ActualAST});
-
- // Size of the AST might have changed after reads too, e.g. if some decls
- // were deserialized from preamble.
- std::lock_guard<std::mutex> Lock(Mutex);
- LastASTSize = ActualAST->getUsedBytes();
+ // Make sure we put the AST back into the LRU cache.
+ auto _ = llvm::make_scope_exit(
+ [&AST, this]() { IdleASTs.put(this, std::move(*AST)); });
+ // Run the user-provided action.
+ if (!*AST)
+ return Action(llvm::make_error<llvm::StringError>(
+ "invalid AST", llvm::errc::invalid_argument));
+ Action(InputsAndAST{FileInputs, **AST});
};
-
startTask(Name, Bind(Task, std::move(Action)),
/*UpdateType=*/llvm::None);
}
@@ -261,8 +386,13 @@
}
std::size_t ASTWorker::getUsedBytes() const {
- std::lock_guard<std::mutex> Lock(Mutex);
- return LastASTSize;
+ // 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
+ // only, so this should be fine.
+ std::size_t Result = IdleASTs.getUsedBytes(this);
+ if (auto Preamble = getPossiblyStalePreamble())
+ Result += Preamble->Preamble.getSize();
+ return Result;
}
void ASTWorker::stop() {
@@ -278,7 +408,7 @@
llvm::Optional<WantDiagnostics> UpdateType) {
if (RunSync) {
assert(!Done && "running a task after stop()");
- trace::Span Tracer(Name + ":" + llvm::sys::path::filename(File));
+ trace::Span Tracer(Name + ":" + llvm::sys::path::filename(FileName));
Task();
return;
}
@@ -415,10 +545,12 @@
TUScheduler::TUScheduler(unsigned AsyncThreadsCount,
bool StorePreamblesInMemory,
PreambleParsedCallback PreambleCallback,
- steady_clock::duration UpdateDebounce)
+ std::chrono::steady_clock::duration UpdateDebounce,
+ ASTRetentionParams RetentionConfig)
: StorePreamblesInMemory(StorePreamblesInMemory),
PCHOps(std::make_shared<PCHContainerOperations>()),
PreambleCallback(std::move(PreambleCallback)), Barrier(AsyncThreadsCount),
+ IdleASTs(llvm::make_unique<ASTCache>(RetentionConfig.MaxRetainedASTs)),
UpdateDebounce(UpdateDebounce) {
if (0 < AsyncThreadsCount) {
PreambleTasks.emplace();
@@ -454,9 +586,9 @@
if (!FD) {
// Create a new worker to process the AST-related tasks.
ASTWorkerHandle Worker = ASTWorker::Create(
- File, WorkerThreads ? WorkerThreads.getPointer() : nullptr, Barrier,
- CppFile(File, StorePreamblesInMemory, PCHOps, PreambleCallback),
- UpdateDebounce);
+ File, *IdleASTs, WorkerThreads ? WorkerThreads.getPointer() : nullptr,
+ Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory,
+ PreambleCallback);
FD = std::unique_ptr<FileData>(new FileData{
Inputs.Contents, Inputs.CompileCommand, std::move(Worker)});
} else {
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -47,6 +47,7 @@
PreambleData(PrecompiledPreamble Preamble, std::vector<Diag> Diags,
std::vector<Inclusion> Inclusions);
+ tooling::CompileCommand CompileCommand;
PrecompiledPreamble Preamble;
std::vector<Diag> Diags;
// Processes like code completions and go-to-definitions will need #include
@@ -128,50 +129,32 @@
using PreambleParsedCallback = std::function<void(
PathRef Path, ASTContext &, std::shared_ptr<clang::Preprocessor>)>;
-/// Manages resources, required by clangd. Allows to rebuild file with new
-/// contents, and provides AST and Preamble for it.
-class CppFile {
-public:
- CppFile(PathRef FileName, bool StorePreamblesInMemory,
- std::shared_ptr<PCHContainerOperations> PCHs,
- PreambleParsedCallback PreambleCallback);
-
- /// Rebuild the AST and the preamble.
- /// Returns a list of diagnostics or llvm::None, if an error occured.
- llvm::Optional<std::vector<Diag>> rebuild(ParseInputs &&Inputs);
- /// Returns the last built preamble.
- const std::shared_ptr<const PreambleData> &getPreamble() const;
- /// Returns the last built AST.
- ParsedAST *getAST() const;
- /// Returns an estimated size, in bytes, currently occupied by the AST and the
- /// Preamble.
- std::size_t getUsedBytes() const;
-
-private:
- /// Build a new preamble for \p Inputs. If the current preamble can be reused,
- /// it is returned instead.
- /// This method is const to ensure we don't incidentally modify any fields.
- std::shared_ptr<const PreambleData>
- rebuildPreamble(CompilerInvocation &CI,
- const tooling::CompileCommand &Command,
- IntrusiveRefCntPtr<vfs::FileSystem> FS,
- llvm::MemoryBuffer &ContentsBuffer) const;
-
- const Path FileName;
- const bool StorePreamblesInMemory;
-
- /// The last CompileCommand used to build AST and Preamble.
- tooling::CompileCommand Command;
- /// The last parsed AST.
- llvm::Optional<ParsedAST> AST;
- /// The last built Preamble.
- std::shared_ptr<const PreambleData> Preamble;
- /// Utility class required by clang
- std::shared_ptr<PCHContainerOperations> PCHs;
- /// This is called after the file is parsed. This can be nullptr if there is
- /// no callback.
- PreambleParsedCallback PreambleCallback;
-};
+/// Builds compiler invocation that could be used to build AST or preamble.
+std::unique_ptr<CompilerInvocation>
+buildCompilerInvocation(const ParseInputs &Inputs);
+
+/// Rebuild the preamble for the new inputs unless the old one can be reused.
+/// If \p OldPreamble can be reused, it is returned unchanged.
+/// If \p OldPreamble is null, always builds the preamble.
+/// If \p PreambleCallback is set, it will be run on top of the AST while
+/// building the preamble. Note that if the old preamble was reused, no AST is
+/// built and, therefore, the callback will not be executed.
+std::shared_ptr<const PreambleData>
+buildPreamble(PathRef FileName, CompilerInvocation &CI,
+ std::shared_ptr<const PreambleData> OldPreamble,
+ const tooling::CompileCommand &OldCompileCommand,
+ const ParseInputs &Inputs,
+ std::shared_ptr<PCHContainerOperations> PCHs, bool StoreInMemory,
+ PreambleParsedCallback PreambleCallback);
+
+/// Build an AST from provided user inputs. This function does not check if
+/// preamble can be reused, as this function expects that \p Preamble is the
+/// result of calling buildPreamble.
+llvm::Optional<ParsedAST>
+buildAST(PathRef FileName, std::unique_ptr<CompilerInvocation> Invocation,
+ const ParseInputs &Inputs,
+ std::shared_ptr<const PreambleData> Preamble,
+ std::shared_ptr<PCHContainerOperations> PCHs);
/// Get the beginning SourceLocation at a specified \p Pos.
/// May be invalid if Pos is, or if there's no identifier.
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -175,8 +175,12 @@
ASTDiags.EndSourceFile();
std::vector<const Decl *> ParsedDecls = Action->takeTopLevelDecls();
+ std::vector<Diag> Diags = ASTDiags.take();
+ // Add diagnostics from the preamble, if any.
+ if (Preamble)
+ Diags.insert(Diags.begin(), Preamble->Diags.begin(), Preamble->Diags.end());
return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action),
- std::move(ParsedDecls), ASTDiags.take(),
+ std::move(ParsedDecls), std::move(Diags),
std::move(Inclusions));
}
@@ -243,120 +247,57 @@
assert(this->Action);
}
-CppFile::CppFile(PathRef FileName, bool StorePreamblesInMemory,
- std::shared_ptr<PCHContainerOperations> PCHs,
- PreambleParsedCallback PreambleCallback)
- : FileName(FileName), StorePreamblesInMemory(StorePreamblesInMemory),
- PCHs(std::move(PCHs)), PreambleCallback(std::move(PreambleCallback)) {
- log("Created CppFile for " + FileName);
-}
-
-llvm::Optional<std::vector<Diag>> CppFile::rebuild(ParseInputs &&Inputs) {
- log("Rebuilding file " + FileName + " with command [" +
- Inputs.CompileCommand.Directory + "] " +
- llvm::join(Inputs.CompileCommand.CommandLine, " "));
-
+std::unique_ptr<CompilerInvocation>
+clangd::buildCompilerInvocation(const ParseInputs &Inputs) {
std::vector<const char *> ArgStrs;
for (const auto &S : Inputs.CompileCommand.CommandLine)
ArgStrs.push_back(S.c_str());
if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
- log("Couldn't set working directory");
- // We run parsing anyway, our lit-tests rely on results for non-existing
- // working dirs.
- }
-
- // Prepare CompilerInvocation.
- std::unique_ptr<CompilerInvocation> CI;
- {
- // FIXME(ibiryukov): store diagnostics from CommandLine when we start
- // reporting them.
- IgnoreDiagnostics IgnoreDiagnostics;
- IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
- CompilerInstance::createDiagnostics(new DiagnosticOptions,
- &IgnoreDiagnostics, false);
- CI = createInvocationFromCommandLine(ArgStrs, CommandLineDiagsEngine,
- Inputs.FS);
- if (!CI) {
- log("Could not build CompilerInvocation for file " + FileName);
- AST = llvm::None;
- Preamble = nullptr;
- return llvm::None;
- }
- // createInvocationFromCommandLine sets DisableFree.
- CI->getFrontendOpts().DisableFree = false;
- CI->getLangOpts()->CommentOpts.ParseAllComments = true;
+ log("Couldn't set working directory when creating compiler invocation.");
+ // We proceed anyway, our lit-tests rely on results for non-existing working
+ // dirs.
}
- std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer =
- llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, FileName);
-
- // Compute updated Preamble.
- std::shared_ptr<const PreambleData> NewPreamble =
- rebuildPreamble(*CI, Inputs.CompileCommand, Inputs.FS, *ContentsBuffer);
-
- // Remove current AST to avoid wasting memory.
- AST = llvm::None;
- // Compute updated AST.
- llvm::Optional<ParsedAST> NewAST;
- {
- trace::Span Tracer("Build");
- SPAN_ATTACH(Tracer, "File", FileName);
- NewAST = ParsedAST::Build(std::move(CI), NewPreamble,
- std::move(ContentsBuffer), PCHs, Inputs.FS);
- }
-
- std::vector<Diag> Diagnostics;
- if (NewAST) {
- // Collect diagnostics from both the preamble and the AST.
- if (NewPreamble)
- Diagnostics = NewPreamble->Diags;
- Diagnostics.insert(Diagnostics.end(), NewAST->getDiagnostics().begin(),
- NewAST->getDiagnostics().end());
- }
-
- // Write the results of rebuild into class fields.
- Command = std::move(Inputs.CompileCommand);
- Preamble = std::move(NewPreamble);
- AST = std::move(NewAST);
- return Diagnostics;
-}
-
-const std::shared_ptr<const PreambleData> &CppFile::getPreamble() const {
- return Preamble;
-}
-
-ParsedAST *CppFile::getAST() const {
- // We could add mutable to AST instead of const_cast here, but that would also
- // allow writing to AST from const methods.
- return AST ? const_cast<ParsedAST *>(AST.getPointer()) : nullptr;
-}
-
-std::size_t CppFile::getUsedBytes() const {
- std::size_t Total = 0;
- if (AST)
- Total += AST->getUsedBytes();
- if (StorePreamblesInMemory && Preamble)
- Total += Preamble->Preamble.getSize();
- return Total;
+ // FIXME(ibiryukov): store diagnostics from CommandLine when we start
+ // reporting them.
+ IgnoreDiagnostics IgnoreDiagnostics;
+ IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
+ CompilerInstance::createDiagnostics(new DiagnosticOptions,
+ &IgnoreDiagnostics, false);
+ std::unique_ptr<CompilerInvocation> CI = createInvocationFromCommandLine(
+ ArgStrs, CommandLineDiagsEngine, Inputs.FS);
+ if (!CI)
+ return nullptr;
+ // createInvocationFromCommandLine sets DisableFree.
+ CI->getFrontendOpts().DisableFree = false;
+ CI->getLangOpts()->CommentOpts.ParseAllComments = true;
+ return CI;
}
-std::shared_ptr<const PreambleData>
-CppFile::rebuildPreamble(CompilerInvocation &CI,
- const tooling::CompileCommand &Command,
- IntrusiveRefCntPtr<vfs::FileSystem> FS,
- llvm::MemoryBuffer &ContentsBuffer) const {
- const auto &OldPreamble = this->Preamble;
- auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), &ContentsBuffer, 0);
- if (OldPreamble && compileCommandsAreEqual(this->Command, Command) &&
- OldPreamble->Preamble.CanReuse(CI, &ContentsBuffer, Bounds, FS.get())) {
+std::shared_ptr<const PreambleData> clangd::buildPreamble(
+ PathRef FileName, CompilerInvocation &CI,
+ std::shared_ptr<const PreambleData> OldPreamble,
+ const tooling::CompileCommand &OldCompileCommand, const ParseInputs &Inputs,
+ std::shared_ptr<PCHContainerOperations> PCHs, bool StoreInMemory,
+ PreambleParsedCallback PreambleCallback) {
+ // Note that we don't need to copy the input contents, preamble can live
+ // without those.
+ auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Inputs.Contents);
+ auto Bounds =
+ ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
+
+ if (OldPreamble &&
+ compileCommandsAreEqual(Inputs.CompileCommand, OldCompileCommand) &&
+ OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
+ Inputs.FS.get())) {
log("Reusing preamble for file " + Twine(FileName));
return OldPreamble;
}
log("Preamble for file " + Twine(FileName) +
" cannot be reused. Attempting to rebuild it.");
- trace::Span Tracer("Preamble");
+ trace::Span Tracer("BuildPreamble");
SPAN_ATTACH(Tracer, "File", FileName);
StoreDiags PreambleDiagnostics;
IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
@@ -369,18 +310,22 @@
CI.getFrontendOpts().SkipFunctionBodies = true;
CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
+ if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
+ log("Couldn't set working directory when building the preamble.");
+ // We proceed anyway, our lit-tests rely on results for non-existing working
+ // dirs.
+ }
auto BuiltPreamble = PrecompiledPreamble::Build(
- CI, &ContentsBuffer, Bounds, *PreambleDiagsEngine, FS, PCHs,
- /*StoreInMemory=*/StorePreamblesInMemory, SerializedDeclsCollector);
+ CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, Inputs.FS, PCHs,
+ StoreInMemory, SerializedDeclsCollector);
// When building the AST for the main file, we do want the function
// bodies.
CI.getFrontendOpts().SkipFunctionBodies = false;
if (BuiltPreamble) {
log("Built preamble of size " + Twine(BuiltPreamble->getSize()) +
" for file " + Twine(FileName));
-
return std::make_shared<PreambleData>(
std::move(*BuiltPreamble), PreambleDiagnostics.take(),
SerializedDeclsCollector.takeInclusions());
@@ -390,6 +335,24 @@
}
}
+llvm::Optional<ParsedAST> clangd::buildAST(
+ PathRef FileName, std::unique_ptr<CompilerInvocation> Invocation,
+ const ParseInputs &Inputs, std::shared_ptr<const PreambleData> Preamble,
+ std::shared_ptr<PCHContainerOperations> PCHs) {
+ trace::Span Tracer("BuildAST");
+ SPAN_ATTACH(Tracer, "File", FileName);
+
+ if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
+ log("Couldn't set working directory when building the preamble.");
+ // We proceed anyway, our lit-tests rely on results for non-existing working
+ // dirs.
+ }
+
+ return ParsedAST::Build(
+ llvm::make_unique<CompilerInvocation>(*Invocation), Preamble,
+ llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents), PCHs, Inputs.FS);
+}
+
SourceLocation clangd::getBeginningOfIdentifier(ParsedAST &Unit,
const Position &Pos,
const FileID FID) {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits