Skip to content

Commit

Permalink
[clangd] Indexing of standard library
Browse files Browse the repository at this point in the history
This provides a nice "warm start" with all headers indexed, not just
those included so far.

The standard library is indexed after a preamble is parsed, using that
file's configuration. The result is pushed into the dynamic index.
If we later see a higher language version, we reindex it.

It's configurable as Index.StandardLibrary, off by default for now.

Based on D105177 by @kuhnel

Fixes clangd/clangd#618

Differential Revision: https://reviews.llvm.org/D115232
  • Loading branch information
sam-mccall committed May 17, 2022
1 parent 8430b82 commit ecaa4d9
Show file tree
Hide file tree
Showing 18 changed files with 713 additions and 21 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/CMakeLists.txt
Expand Up @@ -119,6 +119,7 @@ add_clang_library(clangDaemon
index/Ref.cpp
index/Relation.cpp
index/Serialization.cpp
index/StdLib.cpp
index/Symbol.cpp
index/SymbolCollector.cpp
index/SymbolID.cpp
Expand Down
45 changes: 39 additions & 6 deletions clang-tools-extra/clangd/ClangdServer.cpp
Expand Up @@ -26,6 +26,7 @@
#include "index/CanonicalIncludes.h"
#include "index/FileIndex.h"
#include "index/Merge.h"
#include "index/StdLib.h"
#include "refactor/Rename.h"
#include "refactor/Tweak.h"
#include "support/Cancellation.h"
Expand Down Expand Up @@ -59,16 +60,39 @@ namespace {
// Update the FileIndex with new ASTs and plumb the diagnostics responses.
struct UpdateIndexCallbacks : public ParsingCallbacks {
UpdateIndexCallbacks(FileIndex *FIndex,
ClangdServer::Callbacks *ServerCallbacks)
: FIndex(FIndex), ServerCallbacks(ServerCallbacks) {}
ClangdServer::Callbacks *ServerCallbacks,
const ThreadsafeFS &TFS, AsyncTaskRunner *Tasks)
: FIndex(FIndex), ServerCallbacks(ServerCallbacks), TFS(TFS),
Tasks(Tasks) {}

void onPreambleAST(PathRef Path, llvm::StringRef Version, ASTContext &Ctx,
void onPreambleAST(PathRef Path, llvm::StringRef Version,
const CompilerInvocation &CI, ASTContext &Ctx,
Preprocessor &PP,
const CanonicalIncludes &CanonIncludes) override {
// If this preamble uses a standard library we haven't seen yet, index it.
if (FIndex)
if (auto Loc = Stdlib.add(*CI.getLangOpts(), PP.getHeaderSearchInfo()))
indexStdlib(CI, std::move(*Loc));

if (FIndex)
FIndex->updatePreamble(Path, Version, Ctx, PP, CanonIncludes);
}

void indexStdlib(const CompilerInvocation &CI, StdLibLocation Loc) {
auto Task = [this, LO(*CI.getLangOpts()), Loc(std::move(Loc)),
CI(std::make_unique<CompilerInvocation>(CI))]() mutable {
IndexFileIn IF;
IF.Symbols = indexStandardLibrary(std::move(CI), Loc, TFS);
if (Stdlib.isBest(LO))
FIndex->updatePreamble(std::move(IF));
};
if (Tasks)
// This doesn't have a semaphore to enforce -j, but it's rare.
Tasks->runAsync("IndexStdlib", std::move(Task));
else
Task();
}

void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override {
if (FIndex)
FIndex->updateMain(Path, AST);
Expand Down Expand Up @@ -103,6 +127,9 @@ struct UpdateIndexCallbacks : public ParsingCallbacks {
private:
FileIndex *FIndex;
ClangdServer::Callbacks *ServerCallbacks;
const ThreadsafeFS &TFS;
StdLibSet Stdlib;
AsyncTaskRunner *Tasks;
};

class DraftStoreFS : public ThreadsafeFS {
Expand Down Expand Up @@ -154,12 +181,15 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
Transient(Opts.ImplicitCancellation ? TUScheduler::InvalidateOnUpdate
: TUScheduler::NoInvalidation),
DirtyFS(std::make_unique<DraftStoreFS>(TFS, DraftMgr)) {
if (Opts.AsyncThreadsCount != 0)
IndexTasks.emplace();
// Pass a callback into `WorkScheduler` to extract symbols from a newly
// parsed file and rebuild the file index synchronously each time an AST
// is parsed.
WorkScheduler.emplace(
CDB, TUScheduler::Options(Opts),
std::make_unique<UpdateIndexCallbacks>(DynamicIdx.get(), Callbacks));
WorkScheduler.emplace(CDB, TUScheduler::Options(Opts),
std::make_unique<UpdateIndexCallbacks>(
DynamicIdx.get(), Callbacks, TFS,
IndexTasks ? IndexTasks.getPointer() : nullptr));
// Adds an index to the stack, at higher priority than existing indexes.
auto AddIndex = [&](SymbolIndex *Idx) {
if (this->Index != nullptr) {
Expand Down Expand Up @@ -975,6 +1005,9 @@ ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) {
// and we're blocking the main thread.
if (!WorkScheduler->blockUntilIdle(timeoutSeconds(TimeoutSeconds)))
return false;
// TUScheduler is the only thing that starts background indexing work.
if (IndexTasks && !IndexTasks->wait(timeoutSeconds(TimeoutSeconds)))
return false;

// Unfortunately we don't have strict topological order between the rest of
// the components. E.g. CDB broadcast triggers backrgound indexing.
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/ClangdServer.h
Expand Up @@ -427,6 +427,7 @@ class ClangdServer {
mutable std::mutex CachedCompletionFuzzyFindRequestMutex;

llvm::Optional<std::string> WorkspaceRoot;
llvm::Optional<AsyncTaskRunner> IndexTasks; // for stdlib indexing.
llvm::Optional<TUScheduler> WorkScheduler;
// Invalidation policy used for actions that we assume are "transient".
TUScheduler::ASTActionInvalidation Transient;
Expand Down
5 changes: 3 additions & 2 deletions clang-tools-extra/clangd/Config.h
Expand Up @@ -81,11 +81,12 @@ struct Config {
/// forward-slashes.
std::string MountPoint;
};
/// Controls background-index behavior.
/// Controls index behavior.
struct {
/// Whether this TU should be indexed.
/// Whether this TU should be background-indexed.
BackgroundPolicy Background = BackgroundPolicy::Build;
ExternalIndexSpec External;
bool StandardLibrary = false;
} Index;

enum UnusedIncludesPolicy { Strict, None };
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/ConfigCompile.cpp
Expand Up @@ -332,6 +332,11 @@ struct FragmentCompiler {
}
if (F.External)
compile(std::move(**F.External), F.External->Range);
if (F.StandardLibrary)
Out.Apply.push_back(
[Val(**F.StandardLibrary)](const Params &, Config &C) {
C.Index.StandardLibrary = Val;
});
}

void compile(Fragment::IndexBlock::ExternalBlock &&External,
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/ConfigFragment.h
Expand Up @@ -199,6 +199,9 @@ struct Fragment {
llvm::Optional<Located<std::string>> MountPoint;
};
llvm::Optional<Located<ExternalBlock>> External;
// Whether the standard library visible from this file should be indexed.
// This makes all standard library symbols available, included or not.
llvm::Optional<Located<bool>> StandardLibrary;
};
IndexBlock Index;

Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/ConfigYAML.cpp
Expand Up @@ -184,6 +184,10 @@ class Parser {
F.External.emplace(std::move(External));
F.External->Range = N.getSourceRange();
});
Dict.handle("StandardLibrary", [&](Node &N) {
if (auto StandardLibrary = boolValue(N, "StandardLibrary"))
F.StandardLibrary = *StandardLibrary;
});
Dict.parse(N);
}

Expand Down
7 changes: 4 additions & 3 deletions clang-tools-extra/clangd/TUScheduler.cpp
Expand Up @@ -1013,9 +1013,10 @@ void PreambleThread::build(Request Req) {
bool IsFirstPreamble = !LatestBuild;
LatestBuild = clang::clangd::buildPreamble(
FileName, *Req.CI, Inputs, StoreInMemory,
[this, Version(Inputs.Version)](ASTContext &Ctx, Preprocessor &PP,
const CanonicalIncludes &CanonIncludes) {
Callbacks.onPreambleAST(FileName, Version, Ctx, PP, CanonIncludes);
[&](ASTContext &Ctx, Preprocessor &PP,
const CanonicalIncludes &CanonIncludes) {
Callbacks.onPreambleAST(FileName, Inputs.Version, *Req.CI, Ctx, PP,
CanonIncludes);
},
&Stats);
if (!LatestBuild)
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/TUScheduler.h
Expand Up @@ -133,8 +133,8 @@ class ParsingCallbacks {
/// contains only AST nodes from the #include directives at the start of the
/// file. AST node in the current file should be observed on onMainAST call.
virtual void onPreambleAST(PathRef Path, llvm::StringRef Version,
ASTContext &Ctx, Preprocessor &PP,
const CanonicalIncludes &) {}
const CompilerInvocation &CI, ASTContext &Ctx,
Preprocessor &PP, const CanonicalIncludes &) {}

/// The argument function is run under the critical section guarding against
/// races when closing the files.
Expand Down
16 changes: 10 additions & 6 deletions clang-tools-extra/clangd/index/FileIndex.cpp
Expand Up @@ -425,12 +425,7 @@ FileIndex::FileIndex()
MainFileSymbols(IndexContents::All),
MainFileIndex(std::make_unique<MemIndex>()) {}

void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version,
ASTContext &AST, Preprocessor &PP,
const CanonicalIncludes &Includes) {
IndexFileIn IF;
std::tie(IF.Symbols, std::ignore, IF.Relations) =
indexHeaderSymbols(Version, AST, PP, Includes);
void FileIndex::updatePreamble(IndexFileIn IF) {
FileShardedIndex ShardedIndex(std::move(IF));
for (auto Uri : ShardedIndex.getAllSources()) {
auto IF = ShardedIndex.getShard(Uri);
Expand Down Expand Up @@ -461,6 +456,15 @@ void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version,
}
}

void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version,
ASTContext &AST, Preprocessor &PP,
const CanonicalIncludes &Includes) {
IndexFileIn IF;
std::tie(IF.Symbols, std::ignore, IF.Relations) =
indexHeaderSymbols(Version, AST, PP, Includes);
updatePreamble(std::move(IF));
}

void FileIndex::updateMain(PathRef Path, ParsedAST &AST) {
auto Contents = indexMainDecls(AST);
MainFileSymbols.update(
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/index/FileIndex.h
Expand Up @@ -114,6 +114,7 @@ class FileIndex : public MergedIndex {
/// and macros in \p PP.
void updatePreamble(PathRef Path, llvm::StringRef Version, ASTContext &AST,
Preprocessor &PP, const CanonicalIncludes &Includes);
void updatePreamble(IndexFileIn);

/// Update symbols and references from main file \p Path with
/// `indexMainDecls`.
Expand Down

0 comments on commit ecaa4d9

Please sign in to comment.