Skip to content

Commit 4db81c7

Browse files
[clangd] Support .clangd command line modifications in ScanningAllProjectModules
1 parent 657fb44 commit 4db81c7

File tree

5 files changed

+81
-17
lines changed

5 files changed

+81
-17
lines changed

clang-tools-extra/clangd/GlobalCompilationDatabase.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,16 @@ bool OverlayCDB::setCompileCommand(PathRef File,
830830
return true;
831831
}
832832

833+
std::unique_ptr<ProjectModules>
834+
OverlayCDB::getProjectModules(PathRef File) const {
835+
auto MDB = DelegatingCDB::getProjectModules(File);
836+
MDB->setCommandMangler([&Mangler = Mangler](tooling::CompileCommand &Command,
837+
PathRef CommandPath) {
838+
Mangler(Command, CommandPath);
839+
});
840+
return std::move(MDB);
841+
}
842+
833843
DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base)
834844
: Base(Base) {
835845
if (Base)

clang-tools-extra/clangd/GlobalCompilationDatabase.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ class OverlayCDB : public DelegatingCDB {
209209
setCompileCommand(PathRef File,
210210
std::optional<tooling::CompileCommand> CompilationCommand);
211211

212+
std::unique_ptr<ProjectModules>
213+
getProjectModules(PathRef File) const override;
214+
212215
private:
213216
mutable std::mutex Mutex;
214217
llvm::StringMap<tooling::CompileCommand> Commands; /* GUARDED_BY(Mut) */

clang-tools-extra/clangd/ProjectModules.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROJECTMODULES_H
1010
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROJECTMODULES_H
1111

12+
#include "support/Function.h"
1213
#include "support/Path.h"
1314
#include "support/ThreadsafeFS.h"
15+
#include "clang/Tooling/CompilationDatabase.h"
1416

1517
#include <memory>
1618

@@ -36,11 +38,16 @@ namespace clangd {
3638
/// `<primary-module-name>[:partition-name]`. So module names covers partitions.
3739
class ProjectModules {
3840
public:
41+
using CommandMangler =
42+
llvm::unique_function<void(tooling::CompileCommand &, PathRef) const>;
43+
3944
virtual std::vector<std::string> getRequiredModules(PathRef File) = 0;
4045
virtual PathRef
4146
getSourceForModuleName(llvm::StringRef ModuleName,
4247
PathRef RequiredSrcFile = PathRef()) = 0;
4348

49+
virtual void setCommandMangler(CommandMangler Mangler) {}
50+
4451
virtual ~ProjectModules() = default;
4552
};
4653

clang-tools-extra/clangd/ScanningProjectModules.cpp

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ class ModuleDependencyScanner {
4848
};
4949

5050
/// Scanning the single file specified by \param FilePath.
51-
std::optional<ModuleDependencyInfo> scan(PathRef FilePath);
51+
std::optional<ModuleDependencyInfo>
52+
scan(PathRef FilePath, const ProjectModules::CommandMangler &Mangler);
5253

5354
/// Scanning every source file in the current project to get the
5455
/// <module-name> to <module-unit-source> map.
@@ -57,7 +58,7 @@ class ModuleDependencyScanner {
5758
/// a global module dependency scanner to monitor every file. Or we
5859
/// can simply require the build systems (or even the end users)
5960
/// to provide the map.
60-
void globalScan();
61+
void globalScan(const ProjectModules::CommandMangler &Mangler);
6162

6263
/// Get the source file from the module name. Note that the language
6364
/// guarantees all the module names are unique in a valid program.
@@ -69,7 +70,9 @@ class ModuleDependencyScanner {
6970

7071
/// Return the direct required modules. Indirect required modules are not
7172
/// included.
72-
std::vector<std::string> getRequiredModules(PathRef File);
73+
std::vector<std::string>
74+
getRequiredModules(PathRef File,
75+
const ProjectModules::CommandMangler &Mangler);
7376

7477
private:
7578
std::shared_ptr<const clang::tooling::CompilationDatabase> CDB;
@@ -87,7 +90,8 @@ class ModuleDependencyScanner {
8790
};
8891

8992
std::optional<ModuleDependencyScanner::ModuleDependencyInfo>
90-
ModuleDependencyScanner::scan(PathRef FilePath) {
93+
ModuleDependencyScanner::scan(PathRef FilePath,
94+
const ProjectModules::CommandMangler &Mangler) {
9195
auto Candidates = CDB->getCompileCommands(FilePath);
9296
if (Candidates.empty())
9397
return std::nullopt;
@@ -97,10 +101,8 @@ ModuleDependencyScanner::scan(PathRef FilePath) {
97101
// DirectoryBasedGlobalCompilationDatabase::getCompileCommand.
98102
tooling::CompileCommand Cmd = std::move(Candidates.front());
99103

100-
static int StaticForMainAddr; // Just an address in this process.
101-
Cmd.CommandLine.push_back("-resource-dir=" +
102-
CompilerInvocation::GetResourcesPath(
103-
"clangd", (void *)&StaticForMainAddr));
104+
if (Mangler)
105+
Mangler(Cmd, FilePath);
104106

105107
using namespace clang::tooling::dependencies;
106108

@@ -130,9 +132,10 @@ ModuleDependencyScanner::scan(PathRef FilePath) {
130132
return Result;
131133
}
132134

133-
void ModuleDependencyScanner::globalScan() {
135+
void ModuleDependencyScanner::globalScan(
136+
const ProjectModules::CommandMangler &Mangler) {
134137
for (auto &File : CDB->getAllFiles())
135-
scan(File);
138+
scan(File, Mangler);
136139

137140
GlobalScanned = true;
138141
}
@@ -150,9 +153,9 @@ PathRef ModuleDependencyScanner::getSourceForModuleName(
150153
return {};
151154
}
152155

153-
std::vector<std::string>
154-
ModuleDependencyScanner::getRequiredModules(PathRef File) {
155-
auto ScanningResult = scan(File);
156+
std::vector<std::string> ModuleDependencyScanner::getRequiredModules(
157+
PathRef File, const ProjectModules::CommandMangler &Mangler) {
158+
auto ScanningResult = scan(File, Mangler);
156159
if (!ScanningResult)
157160
return {};
158161

@@ -177,20 +180,25 @@ class ScanningAllProjectModules : public ProjectModules {
177180
~ScanningAllProjectModules() override = default;
178181

179182
std::vector<std::string> getRequiredModules(PathRef File) override {
180-
return Scanner.getRequiredModules(File);
183+
return Scanner.getRequiredModules(File, Mangler);
184+
}
185+
186+
void setCommandMangler(CommandMangler Mangler) override {
187+
this->Mangler = std::move(Mangler);
181188
}
182189

183190
/// RequiredSourceFile is not used intentionally. See the comments of
184191
/// ModuleDependencyScanner for detail.
185192
PathRef
186193
getSourceForModuleName(llvm::StringRef ModuleName,
187194
PathRef RequiredSourceFile = PathRef()) override {
188-
Scanner.globalScan();
195+
Scanner.globalScan(Mangler);
189196
return Scanner.getSourceForModuleName(ModuleName);
190197
}
191198

192199
private:
193200
ModuleDependencyScanner Scanner;
201+
CommandMangler Mangler;
194202
};
195203

196204
std::unique_ptr<ProjectModules> scanningProjectModules(

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

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "Compiler.h"
1919
#include "TestTU.h"
2020
#include "support/ThreadsafeFS.h"
21+
#include "llvm/ADT/StringRef.h"
2122
#include "llvm/Support/FileSystem.h"
2223
#include "llvm/Support/raw_ostream.h"
2324
#include "gmock/gmock.h"
@@ -191,6 +192,41 @@ export module M;
191192
EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.view(TestDir)));
192193
}
193194

195+
TEST_F(PrerequisiteModulesTests, ModuleWithArgumentPatch) {
196+
MockDirectoryCompilationDatabase CDB(TestDir, FS);
197+
198+
CDB.ExtraClangFlags.push_back("-invalid-unknown-flag");
199+
200+
CDB.addFile("Dep.cppm", R"cpp(
201+
export module Dep;
202+
)cpp");
203+
204+
CDB.addFile("M.cppm", R"cpp(
205+
export module M;
206+
import Dep;
207+
)cpp");
208+
209+
// An invalid flag will break the module compilation and the
210+
// getRequiredModules would return an empty array
211+
auto ProjectModules = CDB.getProjectModules(getFullPath("M.cppm"));
212+
EXPECT_TRUE(
213+
ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());
214+
215+
// Set the mangler to filter out the invalid flag
216+
ProjectModules->setCommandMangler(
217+
[](tooling::CompileCommand &Command, PathRef) {
218+
auto const It =
219+
std::find(Command.CommandLine.begin(), Command.CommandLine.end(),
220+
"-invalid-unknown-flag");
221+
Command.CommandLine.erase(It);
222+
});
223+
224+
// And now it returns a non-empty list of required modules since the
225+
// compilation succeeded
226+
EXPECT_FALSE(
227+
ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());
228+
}
229+
194230
TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {
195231
MockDirectoryCompilationDatabase CDB(TestDir, FS);
196232

@@ -435,7 +471,7 @@ void func() {
435471
/*Callback=*/nullptr);
436472
EXPECT_TRUE(Preamble);
437473
EXPECT_TRUE(Preamble->RequiredModules);
438-
474+
439475
auto Result = codeComplete(getFullPath("Use.cpp"), Test.point(),
440476
Preamble.get(), Use, {});
441477
EXPECT_FALSE(Result.Completions.empty());
@@ -474,7 +510,7 @@ void func() {
474510
/*Callback=*/nullptr);
475511
EXPECT_TRUE(Preamble);
476512
EXPECT_TRUE(Preamble->RequiredModules);
477-
513+
478514
auto Result = signatureHelp(getFullPath("Use.cpp"), Test.point(),
479515
*Preamble.get(), Use, MarkupKind::PlainText);
480516
EXPECT_FALSE(Result.signatures.empty());

0 commit comments

Comments
 (0)