diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -357,6 +357,7 @@ Context createProcessingContext(PathRef) const; config::Provider *ConfigProvider = nullptr; + const GlobalCompilationDatabase &CDB; const ThreadsafeFS &TFS; Path ResourceDir; diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -172,7 +172,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, const ThreadsafeFS &TFS, const Options &Opts, Callbacks *Callbacks) - : ConfigProvider(Opts.ConfigProvider), TFS(TFS), + : ConfigProvider(Opts.ConfigProvider), CDB(CDB), TFS(TFS), DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.HeavyweightDynamicSymbolIndex, Opts.CollectMainFileRefs) @@ -257,7 +257,7 @@ Inputs.Contents = std::string(Contents); Inputs.Version = Version.str(); Inputs.ForceRebuild = ForceRebuild; - Inputs.Opts = std::move(Opts); + Inputs.Opts = Opts; Inputs.Index = Index; Inputs.Opts.BuildRecoveryAST = BuildRecoveryAST; Inputs.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType; @@ -265,6 +265,23 @@ // If we loaded Foo.h, we want to make sure Foo.cpp is indexed. if (NewFile && BackgroundIdx) BackgroundIdx->boostRelated(File); + + if (NewFile) { + if (auto *InternalCDB = CDB.lookupCDB(File)) { + for (const auto &CDBFile : InternalCDB->getAllFiles()) { + if (CDBFile != File && !WorkScheduler.hasFile(CDBFile)) { + WithContext WithContext(createProcessingContext(CDBFile)); + if (Config::current().AST.Build == Config::ASTPolicy::PreBuild) { + auto Buffer = llvm::MemoryBuffer::getFile(CDBFile); + Inputs.Contents = std::string(Buffer->get()->getBuffer()); + WorkScheduler.update(CDBFile, Inputs, WantDiagnostics::No); + if (BackgroundIdx) + BackgroundIdx->boostRelated(CDBFile); + } + } + } + } + } } void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); } diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -70,6 +70,13 @@ // ::). All nested namespaces are affected as well. std::vector FullyQualifiedNamespaces; } Style; + + enum class ASTPolicy { PreBuild, OnDemand }; + /// Controls AST prebuild behavior. + struct { + /// Whether this AST should be prebuild. + ASTPolicy Build = ASTPolicy::OnDemand; + } AST; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -157,6 +157,7 @@ compile(std::move(F.If)); compile(std::move(F.CompileFlags)); compile(std::move(F.Index)); + compile(std::move(F.AST)); } void compile(Fragment::IfBlock &&F) { @@ -264,6 +265,18 @@ } } + void compile(Fragment::ASTBlock &&F) { + if (F.Build) { + if (auto Val = compileEnum("Build", **F.Build) + .map("OnDemand", Config::ASTPolicy::OnDemand) + .map("PreBuild", Config::ASTPolicy::PreBuild) + .value()) { + Out.Apply.push_back( + [Val](const Params &, Config &C) { C.AST.Build = *Val; }); + } + } + } + constexpr static llvm::SourceMgr::DiagKind Error = llvm::SourceMgr::DK_Error; constexpr static llvm::SourceMgr::DiagKind Warning = llvm::SourceMgr::DK_Warning; diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -174,6 +174,16 @@ std::vector> FullyQualifiedNamespaces; }; StyleBlock Style; + + struct ASTBlock { + /// Controls whether clangd prebuilds the AST for the current file. When + /// opening a file for the first time. clangd iterates over all the files in + /// its compilation database and prebuilds the AST for those that have this + /// option set to "PreBuild". Legal values are "PreBuild" or "OnDemand". + /// Default is "OnDemand". + llvm::Optional> Build; + }; + ASTBlock AST; }; } // namespace config diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -40,6 +40,7 @@ Dict.handle("CompileFlags", [&](Node &N) { parse(F.CompileFlags, N); }); Dict.handle("Index", [&](Node &N) { parse(F.Index, N); }); Dict.handle("Style", [&](Node &N) { parse(F.Style, N); }); + Dict.handle("AST", [&](Node &N) { parse(F.AST, N); }); Dict.parse(N); return !(N.failed() || HadError); } @@ -89,6 +90,12 @@ Dict.parse(N); } + void parse(Fragment::ASTBlock &F, Node &N) { + DictParser Dict("AST", this); + Dict.handle("Build", [&](Node &N) { F.Build = scalarValue(N, "Build"); }); + Dict.parse(N); + } + // Helper for parsing mapping nodes (dictionaries). // We don't use YamlIO as we want to control over unknown keys. class DictParser { diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.h b/clang-tools-extra/clangd/GlobalCompilationDatabase.h --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.h +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.h @@ -40,6 +40,8 @@ virtual llvm::Optional getCompileCommand(PathRef File) const = 0; + virtual tooling::CompilationDatabase *lookupCDB(PathRef File) const = 0; + /// Finds the closest project to \p File. virtual llvm::Optional getProjectInfo(PathRef File) const { return llvm::None; @@ -76,6 +78,8 @@ llvm::Optional getCompileCommand(PathRef File) const override; + virtual tooling::CompilationDatabase *lookupCDB(PathRef File) const override; + /// Returns the path to first directory containing a compilation database in /// \p File's parents. llvm::Optional getProjectInfo(PathRef File) const override; @@ -132,6 +136,9 @@ llvm::Optional getCompileCommand(PathRef File) const override; + + tooling::CompilationDatabase *lookupCDB(PathRef File) const override; + tooling::CompileCommand getFallbackCommand(PathRef File) const override; /// Project info is gathered purely from the inner compilation database to /// ensure consistency. diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @@ -68,6 +68,20 @@ llvm::Optional DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const { + auto *CDB = lookupCDB(File); + if (!CDB) { + return llvm::None; + } + + auto Candidates = CDB->getCompileCommands(File); + if (!Candidates.empty()) + return std::move(Candidates.front()); + + return None; +} + +tooling::CompilationDatabase * +DirectoryBasedGlobalCompilationDatabase::lookupCDB(PathRef File) const { CDBLookupRequest Req; Req.FileName = File; Req.ShouldBroadcast = true; @@ -75,14 +89,10 @@ auto Res = lookupCDB(Req); if (!Res) { log("Failed to find compilation database for {0}", File); - return llvm::None; + return nullptr; } - auto Candidates = Res->CDB->getCompileCommands(File); - if (!Candidates.empty()) - return std::move(Candidates.front()); - - return None; + return Res->CDB; } // For platforms where paths are case-insensitive (but case-preserving), @@ -270,6 +280,10 @@ return Cmd; } +tooling::CompilationDatabase *OverlayCDB::lookupCDB(PathRef File) const { + return Base ? Base->lookupCDB(File) : nullptr; +} + tooling::CompileCommand OverlayCDB::getFallbackCommand(PathRef File) const { auto Cmd = Base ? Base->getFallbackCommand(File) : GlobalCompilationDatabase::getFallbackCommand(File); diff --git a/clang-tools-extra/clangd/QueryDriverDatabase.cpp b/clang-tools-extra/clangd/QueryDriverDatabase.cpp --- a/clang-tools-extra/clangd/QueryDriverDatabase.cpp +++ b/clang-tools-extra/clangd/QueryDriverDatabase.cpp @@ -277,6 +277,10 @@ return addSystemIncludes(*Cmd, SystemIncludes); } + tooling::CompilationDatabase *lookupCDB(PathRef File) const override { + return Base->lookupCDB(File); + } + llvm::Optional getProjectInfo(PathRef File) const override { return Base->getProjectInfo(File); } diff --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-extra/clangd/TUScheduler.h --- a/clang-tools-extra/clangd/TUScheduler.h +++ b/clang-tools-extra/clangd/TUScheduler.h @@ -231,6 +231,8 @@ /// Returns true if the file was not previously tracked. bool update(PathRef File, ParseInputs Inputs, WantDiagnostics WD); + bool hasFile(PathRef File); + /// Remove \p File from the list of tracked files and schedule removal of its /// resources. Pending diagnostics for closed files may not be delivered, even /// if requested with WantDiags::Auto or WantDiags::Yes. diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -627,11 +627,15 @@ FileInputs = Inputs; } - log("ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}", + log("ASTWorker building file {0} version {1}", FileName, Inputs.Version); + vlog("ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}", FileName, Inputs.Version, Inputs.CompileCommand.Heuristic, Inputs.CompileCommand.Directory, llvm::join(Inputs.CompileCommand.CommandLine, " ")); + if (!InputsAreTheSame) + log("ASTWorker inputs are not the same, cached AST is invalidated"); + StoreDiags CompilerInvocationDiagConsumer; std::vector CC1Args; std::unique_ptr Invocation = buildCompilerInvocation( @@ -1289,6 +1293,8 @@ return NewFile; } +bool TUScheduler::hasFile(PathRef File) { return Files[File] != nullptr; } + void TUScheduler::remove(PathRef File) { bool Removed = Files.erase(File); if (!Removed) diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -1123,6 +1123,10 @@ FileName, std::move(CommandLine), "")}; } + tooling::CompilationDatabase *lookupCDB(PathRef File) const override { + return nullptr; + } + std::vector ExtraClangFlags; private: diff --git a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp --- a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp +++ b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp @@ -70,6 +70,10 @@ return None; } + tooling::CompilationDatabase *lookupCDB(PathRef File) const override { + return nullptr; + } + tooling::CompileCommand getFallbackCommand(llvm::StringRef File) const override { return cmd(File, "-DA=2"); diff --git a/clang-tools-extra/clangd/unittests/TestFS.h b/clang-tools-extra/clangd/unittests/TestFS.h --- a/clang-tools-extra/clangd/unittests/TestFS.h +++ b/clang-tools-extra/clangd/unittests/TestFS.h @@ -65,6 +65,8 @@ llvm::Optional getCompileCommand(PathRef File) const override; + tooling::CompilationDatabase *lookupCDB(PathRef File) const override; + llvm::Optional getProjectInfo(PathRef File) const override; std::vector ExtraClangFlags; diff --git a/clang-tools-extra/clangd/unittests/TestFS.cpp b/clang-tools-extra/clangd/unittests/TestFS.cpp --- a/clang-tools-extra/clangd/unittests/TestFS.cpp +++ b/clang-tools-extra/clangd/unittests/TestFS.cpp @@ -71,6 +71,11 @@ FileName, std::move(CommandLine), "")}; } +tooling::CompilationDatabase * +MockCompilationDatabase::lookupCDB(PathRef File) const { + return nullptr; +} + const char *testRoot() { #ifdef _WIN32 return "C:\\clangd-test";