Skip to content

Commit 2fa4927

Browse files
[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]>
1 parent d46d99c commit 2fa4927

File tree

7 files changed

+92
-16
lines changed

7 files changed

+92
-16
lines changed

clang-tools-extra/clangd/ClangdLSPServer.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,8 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
554554
if (const auto &Dir = Params.initializationOptions.compilationDatabasePath)
555555
CDBOpts.CompileCommandsDir = Dir;
556556
CDBOpts.ContextProvider = Opts.ContextProvider;
557+
if (Opts.StrongWorkspaceMode)
558+
CDBOpts.applyFallbackWorkingDirectory(Opts.WorkspaceRoot);
557559
BaseCDB =
558560
std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
559561
}

clang-tools-extra/clangd/ClangdServer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ class ClangdServer {
152152
/// FIXME: If not set, should use the current working directory.
153153
std::optional<std::string> WorkspaceRoot;
154154

155+
/// Sets an alternate mode of operation. Current effects are:
156+
/// - Using the current working directory as the working directory for
157+
/// fallback commands
158+
bool StrongWorkspaceMode;
159+
155160
/// The resource directory is used to find internal headers, overriding
156161
/// defaults and -resource-dir compiler flag).
157162
/// If std::nullopt, ClangdServer calls

clang-tools-extra/clangd/GlobalCompilationDatabase.cpp

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ GlobalCompilationDatabase::getFallbackCommand(PathRef File) const {
6464
if (FileExtension.empty() || FileExtension == ".h")
6565
Argv.push_back("-xobjective-c++-header");
6666
Argv.push_back(std::string(File));
67-
tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File),
67+
tooling::CompileCommand Cmd(FallbackWorkingDirectory
68+
? *FallbackWorkingDirectory
69+
: llvm::sys::path::parent_path(File),
6870
llvm::sys::path::filename(File), std::move(Argv),
6971
/*Output=*/"");
7072
Cmd.Heuristic = "clangd fallback";
@@ -349,7 +351,8 @@ bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load(
349351

350352
DirectoryBasedGlobalCompilationDatabase::
351353
DirectoryBasedGlobalCompilationDatabase(const Options &Opts)
352-
: Opts(Opts), Broadcaster(std::make_unique<BroadcastThread>(*this)) {
354+
: GlobalCompilationDatabase(Opts.FallbackWorkingDirectory), Opts(Opts),
355+
Broadcaster(std::make_unique<BroadcastThread>(*this)) {
353356
if (!this->Opts.ContextProvider)
354357
this->Opts.ContextProvider = [](llvm::StringRef) {
355358
return Context::current().clone();
@@ -460,6 +463,21 @@ DirectoryBasedGlobalCompilationDatabase::lookupCDB(
460463
return Result;
461464
}
462465

466+
void DirectoryBasedGlobalCompilationDatabase::Options::
467+
applyFallbackWorkingDirectory(
468+
std::optional<std::string> FallbackWorkingDirectory) {
469+
if (FallbackWorkingDirectory)
470+
this->FallbackWorkingDirectory = *FallbackWorkingDirectory;
471+
else {
472+
// Clangd is running in strong workspace mode but the client didn't
473+
// specify a workspace path in the `initialize` request.
474+
// Fallback to current working directory.
475+
SmallString<256> CWD;
476+
llvm::sys::fs::current_path(CWD);
477+
this->FallbackWorkingDirectory = std::string(CWD);
478+
}
479+
}
480+
463481
// The broadcast thread announces files with new compile commands to the world.
464482
// Primarily this is used to enqueue them for background indexing.
465483
//
@@ -759,9 +777,10 @@ DirectoryBasedGlobalCompilationDatabase::getProjectModules(PathRef File) const {
759777

760778
OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base,
761779
std::vector<std::string> FallbackFlags,
762-
CommandMangler Mangler)
763-
: DelegatingCDB(Base), Mangler(std::move(Mangler)),
764-
FallbackFlags(std::move(FallbackFlags)) {}
780+
CommandMangler Mangler,
781+
std::optional<std::string> FallbackWorkingDirectory)
782+
: DelegatingCDB(Base, FallbackWorkingDirectory),
783+
Mangler(std::move(Mangler)), FallbackFlags(std::move(FallbackFlags)) {}
765784

766785
std::optional<tooling::CompileCommand>
767786
OverlayCDB::getCompileCommand(PathRef File) const {
@@ -844,16 +863,20 @@ OverlayCDB::getProjectModules(PathRef File) const {
844863
return MDB;
845864
}
846865

847-
DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base)
848-
: Base(Base) {
866+
DelegatingCDB::DelegatingCDB(
867+
const GlobalCompilationDatabase *Base,
868+
std::optional<std::string> FallbackWorkingDirectory)
869+
: GlobalCompilationDatabase(FallbackWorkingDirectory), Base(Base) {
849870
if (Base)
850871
BaseChanged = Base->watch([this](const std::vector<std::string> Changes) {
851872
OnCommandChanged.broadcast(Changes);
852873
});
853874
}
854875

855-
DelegatingCDB::DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base)
856-
: DelegatingCDB(Base.get()) {
876+
DelegatingCDB::DelegatingCDB(
877+
std::unique_ptr<GlobalCompilationDatabase> Base,
878+
std::optional<std::string> FallbackWorkingDirectory)
879+
: DelegatingCDB(Base.get(), FallbackWorkingDirectory) {
857880
BaseOwner = std::move(Base);
858881
}
859882

clang-tools-extra/clangd/GlobalCompilationDatabase.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ struct ProjectInfo {
3535
/// Provides compilation arguments used for parsing C and C++ files.
3636
class GlobalCompilationDatabase {
3737
public:
38+
GlobalCompilationDatabase(
39+
std::optional<std::string> FallbackWorkingDirectory = std::nullopt)
40+
: FallbackWorkingDirectory(FallbackWorkingDirectory) {}
3841
virtual ~GlobalCompilationDatabase() = default;
3942

4043
/// If there are any known-good commands for building this file, returns one.
@@ -69,14 +72,19 @@ class GlobalCompilationDatabase {
6972
}
7073

7174
protected:
75+
std::optional<std::string> FallbackWorkingDirectory;
7276
mutable CommandChanged OnCommandChanged;
7377
};
7478

7579
// Helper class for implementing GlobalCompilationDatabases that wrap others.
7680
class DelegatingCDB : public GlobalCompilationDatabase {
7781
public:
78-
DelegatingCDB(const GlobalCompilationDatabase *Base);
79-
DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base);
82+
DelegatingCDB(
83+
const GlobalCompilationDatabase *Base,
84+
std::optional<std::string> FallbackWorkingDirectory = std::nullopt);
85+
DelegatingCDB(
86+
std::unique_ptr<GlobalCompilationDatabase> Base,
87+
std::optional<std::string> FallbackWorkingDirectory = std::nullopt);
8088

8189
std::optional<tooling::CompileCommand>
8290
getCompileCommand(PathRef File) const override;
@@ -117,6 +125,12 @@ class DirectoryBasedGlobalCompilationDatabase
117125
// Only look for a compilation database in this one fixed directory.
118126
// FIXME: fold this into config/context mechanism.
119127
std::optional<Path> CompileCommandsDir;
128+
// Working directory for fallback commands
129+
// If unset, parent directory of file should be used
130+
std::optional<std::string> FallbackWorkingDirectory;
131+
132+
void applyFallbackWorkingDirectory(
133+
std::optional<std::string> FallbackWorkingDirectory);
120134
};
121135

122136
DirectoryBasedGlobalCompilationDatabase(const Options &Opts);
@@ -194,9 +208,11 @@ class OverlayCDB : public DelegatingCDB {
194208
// Base may be null, in which case no entries are inherited.
195209
// FallbackFlags are added to the fallback compile command.
196210
// Adjuster is applied to all commands, fallback or not.
197-
OverlayCDB(const GlobalCompilationDatabase *Base,
198-
std::vector<std::string> FallbackFlags = {},
199-
CommandMangler Mangler = nullptr);
211+
OverlayCDB(
212+
const GlobalCompilationDatabase *Base,
213+
std::vector<std::string> FallbackFlags = {},
214+
CommandMangler Mangler = nullptr,
215+
std::optional<std::string> FallbackWorkingDirectory = std::nullopt);
200216

201217
std::optional<tooling::CompileCommand>
202218
getCompileCommand(PathRef File) const override;

clang-tools-extra/clangd/tool/Check.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ class Checker {
169169
bool buildCommand(const ThreadsafeFS &TFS) {
170170
log("Loading compilation database...");
171171
DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
172+
if (Opts.StrongWorkspaceMode)
173+
CDBOpts.applyFallbackWorkingDirectory(Opts.WorkspaceRoot);
172174
CDBOpts.CompileCommandsDir =
173175
Config::current().CompileFlags.CDBSearch.FixedCDBPath;
174176
BaseCDB =
@@ -178,8 +180,10 @@ class Checker {
178180
getSystemIncludeExtractor(llvm::ArrayRef(Opts.QueryDriverGlobs));
179181
if (Opts.ResourceDir)
180182
Mangler.ResourceDir = *Opts.ResourceDir;
183+
181184
CDB = std::make_unique<OverlayCDB>(
182-
BaseCDB.get(), std::vector<std::string>{}, std::move(Mangler));
185+
BaseCDB.get(), std::vector<std::string>{}, std::move(Mangler),
186+
CDBOpts.FallbackWorkingDirectory);
183187

184188
if (auto TrueCmd = CDB->getCompileCommand(File)) {
185189
Cmd = std::move(*TrueCmd);
@@ -502,7 +506,7 @@ bool check(llvm::StringRef File, const ThreadsafeFS &TFS,
502506
config::DiagnosticCallback Diag) const override {
503507
config::Fragment F;
504508
// If we're timing clang-tidy checks, implicitly disabling the slow ones
505-
// is counterproductive!
509+
// is counterproductive!
506510
if (CheckTidyTime.getNumOccurrences())
507511
F.Diagnostics.ClangTidy.FastCheckFilter.emplace("None");
508512
return {std::move(F).compile(Diag)};

clang-tools-extra/clangd/tool/ClangdMain.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,17 @@ opt<bool> EnableConfig{
500500
init(true),
501501
};
502502

503+
opt<bool> StrongWorkspaceMode{
504+
"strong-workspace-mode",
505+
cat(Features),
506+
desc("An alternate mode of operation for clangd, where the clangd instance "
507+
"is used to edit a single workspace.\n"
508+
"When enabled, fallback commands use the workspace directory as their "
509+
"working directory instead of the parent folder."),
510+
init(false),
511+
Hidden,
512+
};
513+
503514
opt<bool> UseDirtyHeaders{"use-dirty-headers", cat(Misc),
504515
desc("Use files open in the editor when parsing "
505516
"headers instead of reading from the disk"),
@@ -907,6 +918,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
907918
}
908919
if (!ResourceDir.empty())
909920
Opts.ResourceDir = ResourceDir;
921+
Opts.StrongWorkspaceMode = StrongWorkspaceMode;
910922
Opts.BuildDynamicSymbolIndex = true;
911923
#if CLANGD_ENABLE_REMOTE
912924
if (RemoteIndexAddress.empty() != ProjectRoot.empty()) {

clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ TEST(GlobalCompilationDatabaseTest, FallbackCommand) {
5555
testPath("foo/bar")));
5656
}
5757

58+
TEST(GlobalCompilationDatabaseTest, FallbackWorkingDirectory) {
59+
MockFS TFS;
60+
DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
61+
CDBOpts.applyFallbackWorkingDirectory(testPath("foo"));
62+
EXPECT_EQ(CDBOpts.FallbackWorkingDirectory, testPath("foo"));
63+
64+
DirectoryBasedGlobalCompilationDatabase DB(CDBOpts);
65+
auto Cmd = DB.getFallbackCommand(testPath("foo/src/bar.cc"));
66+
EXPECT_EQ(Cmd.Directory, testPath("foo"));
67+
EXPECT_THAT(Cmd.CommandLine,
68+
ElementsAre("clang", testPath("foo/src/bar.cc")));
69+
EXPECT_EQ(Cmd.Output, "");
70+
}
71+
5872
static tooling::CompileCommand cmd(llvm::StringRef File, llvm::StringRef Arg) {
5973
return tooling::CompileCommand(
6074
testRoot(), File, {"clang", std::string(Arg), std::string(File)}, "");

0 commit comments

Comments
 (0)