diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -1335,21 +1335,18 @@ void ClangdLSPServer::applyConfiguration( const ConfigurationSettings &Settings) { - // Per-file update to the compilation database. - llvm::StringSet<> ModifiedFiles; + llvm::StringMap> Commands; for (auto &Entry : Settings.compilationDatabaseChanges) { PathRef File = Entry.first; - auto Old = CDB->getCompileCommand(File); - auto New = - tooling::CompileCommand(std::move(Entry.second.workingDirectory), File, - std::move(Entry.second.compilationCommand), - /*Output=*/""); - if (Old != New) { - CDB->setCompileCommand(File, std::move(New)); - ModifiedFiles.insert(File); - } + if (Entry.second.compilationCommand.empty()) + Commands.insert({File, std::nullopt}); + else + Commands.insert({File, tooling::CompileCommand( + std::move(Entry.second.workingDirectory), File, + std::move(Entry.second.compilationCommand), + /*Output=*/"")}); } - + llvm::StringSet<> ModifiedFiles{CDB->setCompileCommands(std::move(Commands))}; Server->reparseOpenFilesIfNeeded( [&](llvm::StringRef File) { return ModifiedFiles.count(File) != 0; }); } 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 @@ -17,6 +17,7 @@ #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" #include #include #include @@ -189,14 +190,19 @@ getCompileCommand(PathRef File) const override; tooling::CompileCommand getFallbackCommand(PathRef File) const override; - /// Sets or clears the compilation command for a particular file. - void - setCompileCommand(PathRef File, - std::optional CompilationCommand); + /// Sets compilation commands and return updated files. + llvm::StringSet<> setCompileCommands( + llvm::StringMap> Commands); + + /// Legacy inefficient implementation that inserts one file at a time + /// that is implemented as a wrapper on top of setCompileCommands above. + void setCompileCommand(PathRef File, + std::optional Cmd); private: mutable std::mutex Mutex; llvm::StringMap Commands; /* GUARDED_BY(Mut) */ + std::unique_ptr CDB; /* GUARDED_BY(Mut) */ CommandMangler Mangler; std::vector FallbackFlags; }; 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 @@ -729,6 +729,35 @@ return Res->PI; } +// Helper class that exposes CDB pushed via LSP protocol as +// tooling::CompilationDatabase for interpolation. +class InMemoryCompilationDatabase : public tooling::CompilationDatabase { +public: + InMemoryCompilationDatabase( + llvm::StringMap &Commands) + : Commands(Commands) {} + + std::vector + getCompileCommands(StringRef FilePath) const override { + auto It = Commands.find(removeDots(FilePath)); + if (It != Commands.end()) + return {It->second}; + return {}; + } + + std::vector getAllFiles() const override { + std::vector Res; + Res.reserve(Commands.size()); + for (const auto &S : Commands.keys()) + Res.push_back(S.str()); + return Res; + } + +private: + // Use reference to OverlayCDB::Commands to avoid copies. + llvm::StringMap &Commands; +}; + OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base, std::vector FallbackFlags, CommandMangler Mangler) @@ -740,9 +769,11 @@ std::optional Cmd; { std::lock_guard Lock(Mutex); - auto It = Commands.find(removeDots(File)); - if (It != Commands.end()) - Cmd = It->second; + if (CDB) { + auto Candidates = CDB->getCompileCommands(File); + if (!Candidates.empty()) + Cmd = std::move(Candidates.front()); + } } if (!Cmd) Cmd = DelegatingCDB::getCompileCommand(File); @@ -763,20 +794,36 @@ return Cmd; } -void OverlayCDB::setCompileCommand(PathRef File, - std::optional Cmd) { - // We store a canonical version internally to prevent mismatches between set - // and get compile commands. Also it assures clients listening to broadcasts - // doesn't receive different names for the same file. - std::string CanonPath = removeDots(File); +llvm::StringSet<> OverlayCDB::setCompileCommands( + llvm::StringMap> NewCommands) { + llvm::StringSet<> ModifiedFiles; { std::unique_lock Lock(Mutex); - if (Cmd) - Commands[CanonPath] = std::move(*Cmd); - else - Commands.erase(CanonPath); + for (auto &E : NewCommands) { + // We store a canonical version internally to prevent mismatches between + // set and get compile commands. Also it assures clients listening to + // broadcasts doesn't receive different names for the same file. + std::string CanonPath = removeDots(E.getKey()); + if (E.getValue()) + Commands[CanonPath] = std::move(*E.getValue()); + else + Commands.erase(CanonPath); + ModifiedFiles.insert(CanonPath); + } + CDB = tooling::inferMissingCompileCommands( + std::unique_ptr( + new InMemoryCompilationDatabase(Commands))); } - OnCommandChanged.broadcast({CanonPath}); + for (const auto &S : ModifiedFiles.keys()) + OnCommandChanged.broadcast({S.str()}); + return ModifiedFiles; +} + +void OverlayCDB::setCompileCommand(PathRef File, + std::optional Cmd) { + llvm::StringMap> NewCommands; + NewCommands[File] = std::move(Cmd); + setCompileCommands(NewCommands); } DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base) 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 @@ -95,10 +95,14 @@ CDB.setCompileCommand(testPath("foo.cc"), Override); EXPECT_THAT(CDB.getCompileCommand(testPath("foo.cc"))->CommandLine, Contains("-DA=3")); - EXPECT_EQ(CDB.getCompileCommand(testPath("missing.cc")), std::nullopt); - CDB.setCompileCommand(testPath("missing.cc"), Override); + // Expect interpolation from foo.cc EXPECT_THAT(CDB.getCompileCommand(testPath("missing.cc"))->CommandLine, Contains("-DA=3")); + // Check that explicit override replaces interpolation + Override = cmd(testPath("missing.cc"), "-DA=4"); + CDB.setCompileCommand(testPath("missing.cc"), Override); + EXPECT_THAT(CDB.getCompileCommand(testPath("missing.cc"))->CommandLine, + Contains("-DA=4")); } TEST_F(OverlayCDBTest, GetFallbackCommand) {