Author: Dominicentek Date: 2025-12-09T07:05:51Z New Revision: 2fa492726e774c050d6f21d57990c8bfbd7f1400
URL: https://github.com/llvm/llvm-project/commit/2fa492726e774c050d6f21d57990c8bfbd7f1400 DIFF: https://github.com/llvm/llvm-project/commit/2fa492726e774c050d6f21d57990c8bfbd7f1400.diff LOG: [clangd] Add a (currently hidden) --strong-workspace-mode flag (#155905) Its current effect is to use the `rootUri` provided by the client as the working directory for fallback commands. Future effects will include other behaviors appropriate for cases where clangd is being run in the context of editing a single workspace, such as using the `compile_commands.json` file in the workspace root for all opened files. The flag is hidden until other behaviors are implemented and they constitute a cohesive "mode" for users. --------- Co-authored-by: Nathan Ridge <[email protected]> Added: Modified: clang-tools-extra/clangd/ClangdLSPServer.cpp clang-tools-extra/clangd/ClangdServer.h clang-tools-extra/clangd/GlobalCompilationDatabase.cpp clang-tools-extra/clangd/GlobalCompilationDatabase.h clang-tools-extra/clangd/tool/Check.cpp clang-tools-extra/clangd/tool/ClangdMain.cpp clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index f8e6da73bbb1f..1518f177b06a0 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -554,6 +554,8 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params, if (const auto &Dir = Params.initializationOptions.compilationDatabasePath) CDBOpts.CompileCommandsDir = Dir; CDBOpts.ContextProvider = Opts.ContextProvider; + if (Opts.StrongWorkspaceMode) + CDBOpts.applyFallbackWorkingDirectory(Opts.WorkspaceRoot); BaseCDB = std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts); } diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index 4a1eae188f7eb..d45d13fa034b5 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -152,6 +152,11 @@ class ClangdServer { /// FIXME: If not set, should use the current working directory. std::optional<std::string> WorkspaceRoot; + /// Sets an alternate mode of operation. Current effects are: + /// - Using the current working directory as the working directory for + /// fallback commands + bool StrongWorkspaceMode; + /// The resource directory is used to find internal headers, overriding /// defaults and -resource-dir compiler flag). /// If std::nullopt, ClangdServer calls diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp index c6afd0bc07cbd..d229a71867558 100644 --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @@ -64,7 +64,9 @@ GlobalCompilationDatabase::getFallbackCommand(PathRef File) const { if (FileExtension.empty() || FileExtension == ".h") Argv.push_back("-xobjective-c++-header"); Argv.push_back(std::string(File)); - tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File), + tooling::CompileCommand Cmd(FallbackWorkingDirectory + ? *FallbackWorkingDirectory + : llvm::sys::path::parent_path(File), llvm::sys::path::filename(File), std::move(Argv), /*Output=*/""); Cmd.Heuristic = "clangd fallback"; @@ -349,7 +351,8 @@ bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load( DirectoryBasedGlobalCompilationDatabase:: DirectoryBasedGlobalCompilationDatabase(const Options &Opts) - : Opts(Opts), Broadcaster(std::make_unique<BroadcastThread>(*this)) { + : GlobalCompilationDatabase(Opts.FallbackWorkingDirectory), Opts(Opts), + Broadcaster(std::make_unique<BroadcastThread>(*this)) { if (!this->Opts.ContextProvider) this->Opts.ContextProvider = [](llvm::StringRef) { return Context::current().clone(); @@ -460,6 +463,21 @@ DirectoryBasedGlobalCompilationDatabase::lookupCDB( return Result; } +void DirectoryBasedGlobalCompilationDatabase::Options:: + applyFallbackWorkingDirectory( + std::optional<std::string> FallbackWorkingDirectory) { + if (FallbackWorkingDirectory) + this->FallbackWorkingDirectory = *FallbackWorkingDirectory; + else { + // Clangd is running in strong workspace mode but the client didn't + // specify a workspace path in the `initialize` request. + // Fallback to current working directory. + SmallString<256> CWD; + llvm::sys::fs::current_path(CWD); + this->FallbackWorkingDirectory = std::string(CWD); + } +} + // The broadcast thread announces files with new compile commands to the world. // Primarily this is used to enqueue them for background indexing. // @@ -759,9 +777,10 @@ DirectoryBasedGlobalCompilationDatabase::getProjectModules(PathRef File) const { OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base, std::vector<std::string> FallbackFlags, - CommandMangler Mangler) - : DelegatingCDB(Base), Mangler(std::move(Mangler)), - FallbackFlags(std::move(FallbackFlags)) {} + CommandMangler Mangler, + std::optional<std::string> FallbackWorkingDirectory) + : DelegatingCDB(Base, FallbackWorkingDirectory), + Mangler(std::move(Mangler)), FallbackFlags(std::move(FallbackFlags)) {} std::optional<tooling::CompileCommand> OverlayCDB::getCompileCommand(PathRef File) const { @@ -844,16 +863,20 @@ OverlayCDB::getProjectModules(PathRef File) const { return MDB; } -DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base) - : Base(Base) { +DelegatingCDB::DelegatingCDB( + const GlobalCompilationDatabase *Base, + std::optional<std::string> FallbackWorkingDirectory) + : GlobalCompilationDatabase(FallbackWorkingDirectory), Base(Base) { if (Base) BaseChanged = Base->watch([this](const std::vector<std::string> Changes) { OnCommandChanged.broadcast(Changes); }); } -DelegatingCDB::DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base) - : DelegatingCDB(Base.get()) { +DelegatingCDB::DelegatingCDB( + std::unique_ptr<GlobalCompilationDatabase> Base, + std::optional<std::string> FallbackWorkingDirectory) + : DelegatingCDB(Base.get(), FallbackWorkingDirectory) { BaseOwner = std::move(Base); } diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.h b/clang-tools-extra/clangd/GlobalCompilationDatabase.h index 1d636d73664be..415c7f50f8606 100644 --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.h +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.h @@ -35,6 +35,9 @@ struct ProjectInfo { /// Provides compilation arguments used for parsing C and C++ files. class GlobalCompilationDatabase { public: + GlobalCompilationDatabase( + std::optional<std::string> FallbackWorkingDirectory = std::nullopt) + : FallbackWorkingDirectory(FallbackWorkingDirectory) {} virtual ~GlobalCompilationDatabase() = default; /// If there are any known-good commands for building this file, returns one. @@ -69,14 +72,19 @@ class GlobalCompilationDatabase { } protected: + std::optional<std::string> FallbackWorkingDirectory; mutable CommandChanged OnCommandChanged; }; // Helper class for implementing GlobalCompilationDatabases that wrap others. class DelegatingCDB : public GlobalCompilationDatabase { public: - DelegatingCDB(const GlobalCompilationDatabase *Base); - DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base); + DelegatingCDB( + const GlobalCompilationDatabase *Base, + std::optional<std::string> FallbackWorkingDirectory = std::nullopt); + DelegatingCDB( + std::unique_ptr<GlobalCompilationDatabase> Base, + std::optional<std::string> FallbackWorkingDirectory = std::nullopt); std::optional<tooling::CompileCommand> getCompileCommand(PathRef File) const override; @@ -117,6 +125,12 @@ class DirectoryBasedGlobalCompilationDatabase // Only look for a compilation database in this one fixed directory. // FIXME: fold this into config/context mechanism. std::optional<Path> CompileCommandsDir; + // Working directory for fallback commands + // If unset, parent directory of file should be used + std::optional<std::string> FallbackWorkingDirectory; + + void applyFallbackWorkingDirectory( + std::optional<std::string> FallbackWorkingDirectory); }; DirectoryBasedGlobalCompilationDatabase(const Options &Opts); @@ -194,9 +208,11 @@ class OverlayCDB : public DelegatingCDB { // Base may be null, in which case no entries are inherited. // FallbackFlags are added to the fallback compile command. // Adjuster is applied to all commands, fallback or not. - OverlayCDB(const GlobalCompilationDatabase *Base, - std::vector<std::string> FallbackFlags = {}, - CommandMangler Mangler = nullptr); + OverlayCDB( + const GlobalCompilationDatabase *Base, + std::vector<std::string> FallbackFlags = {}, + CommandMangler Mangler = nullptr, + std::optional<std::string> FallbackWorkingDirectory = std::nullopt); std::optional<tooling::CompileCommand> getCompileCommand(PathRef File) const override; diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp index df8d075e80596..9c6de40ebde0f 100644 --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -169,6 +169,8 @@ class Checker { bool buildCommand(const ThreadsafeFS &TFS) { log("Loading compilation database..."); DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS); + if (Opts.StrongWorkspaceMode) + CDBOpts.applyFallbackWorkingDirectory(Opts.WorkspaceRoot); CDBOpts.CompileCommandsDir = Config::current().CompileFlags.CDBSearch.FixedCDBPath; BaseCDB = @@ -178,8 +180,10 @@ class Checker { getSystemIncludeExtractor(llvm::ArrayRef(Opts.QueryDriverGlobs)); if (Opts.ResourceDir) Mangler.ResourceDir = *Opts.ResourceDir; + CDB = std::make_unique<OverlayCDB>( - BaseCDB.get(), std::vector<std::string>{}, std::move(Mangler)); + BaseCDB.get(), std::vector<std::string>{}, std::move(Mangler), + CDBOpts.FallbackWorkingDirectory); if (auto TrueCmd = CDB->getCompileCommand(File)) { Cmd = std::move(*TrueCmd); @@ -502,7 +506,7 @@ bool check(llvm::StringRef File, const ThreadsafeFS &TFS, config::DiagnosticCallback Diag) const override { config::Fragment F; // If we're timing clang-tidy checks, implicitly disabling the slow ones - // is counterproductive! + // is counterproductive! if (CheckTidyTime.getNumOccurrences()) F.Diagnostics.ClangTidy.FastCheckFilter.emplace("None"); return {std::move(F).compile(Diag)}; diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index 4a990f8f716ca..54af3662470db 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -500,6 +500,17 @@ opt<bool> EnableConfig{ init(true), }; +opt<bool> StrongWorkspaceMode{ + "strong-workspace-mode", + cat(Features), + desc("An alternate mode of operation for clangd, where the clangd instance " + "is used to edit a single workspace.\n" + "When enabled, fallback commands use the workspace directory as their " + "working directory instead of the parent folder."), + init(false), + Hidden, +}; + opt<bool> UseDirtyHeaders{"use-dirty-headers", cat(Misc), desc("Use files open in the editor when parsing " "headers instead of reading from the disk"), @@ -907,6 +918,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var } if (!ResourceDir.empty()) Opts.ResourceDir = ResourceDir; + Opts.StrongWorkspaceMode = StrongWorkspaceMode; Opts.BuildDynamicSymbolIndex = true; #if CLANGD_ENABLE_REMOTE if (RemoteIndexAddress.empty() != ProjectRoot.empty()) { diff --git a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp index c9e01e52dac1f..39ab1446e980b 100644 --- a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp +++ b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp @@ -55,6 +55,20 @@ TEST(GlobalCompilationDatabaseTest, FallbackCommand) { testPath("foo/bar"))); } +TEST(GlobalCompilationDatabaseTest, FallbackWorkingDirectory) { + MockFS TFS; + DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS); + CDBOpts.applyFallbackWorkingDirectory(testPath("foo")); + EXPECT_EQ(CDBOpts.FallbackWorkingDirectory, testPath("foo")); + + DirectoryBasedGlobalCompilationDatabase DB(CDBOpts); + auto Cmd = DB.getFallbackCommand(testPath("foo/src/bar.cc")); + EXPECT_EQ(Cmd.Directory, testPath("foo")); + EXPECT_THAT(Cmd.CommandLine, + ElementsAre("clang", testPath("foo/src/bar.cc"))); + EXPECT_EQ(Cmd.Output, ""); +} + static tooling::CompileCommand cmd(llvm::StringRef File, llvm::StringRef Arg) { return tooling::CompileCommand( testRoot(), File, {"clang", std::string(Arg), std::string(File)}, ""); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
