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

Reply via email to