Skip to content

Commit 02aec86

Browse files
fleeting-xxtstellar
authored andcommitted
[clangd] [Modules] Fix to correctly handle module dependencies (llvm#142828)
This is a re-application of llvm#142090 without the unit test changes. A subsequent PR will follow that adds a unit test for module dependencies. - Fix dangling string references in the return value of getAllRequiredModules() - Change a couple of calls in getOrBuildModuleFile() to use the loop variable instead of the ModuleName parameter.
1 parent c4f257c commit 02aec86

File tree

4 files changed

+110
-12
lines changed

4 files changed

+110
-12
lines changed

clang-tools-extra/clangd/ModulesBuilder.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,9 @@ void ModuleFileCache::remove(StringRef ModuleName) {
360360
/// Collect the directly and indirectly required module names for \param
361361
/// ModuleName in topological order. The \param ModuleName is guaranteed to
362362
/// be the last element in \param ModuleNames.
363-
llvm::SmallVector<StringRef> getAllRequiredModules(ProjectModules &MDB,
363+
llvm::SmallVector<std::string> getAllRequiredModules(ProjectModules &MDB,
364364
StringRef ModuleName) {
365-
llvm::SmallVector<llvm::StringRef> ModuleNames;
365+
llvm::SmallVector<std::string> ModuleNames;
366366
llvm::StringSet<> ModuleNamesSet;
367367

368368
auto VisitDeps = [&](StringRef ModuleName, auto Visitor) -> void {
@@ -373,7 +373,7 @@ llvm::SmallVector<StringRef> getAllRequiredModules(ProjectModules &MDB,
373373
if (ModuleNamesSet.insert(RequiredModuleName).second)
374374
Visitor(RequiredModuleName, Visitor);
375375

376-
ModuleNames.push_back(ModuleName);
376+
ModuleNames.push_back(ModuleName.str());
377377
};
378378
VisitDeps(ModuleName, VisitDeps);
379379

@@ -418,28 +418,30 @@ llvm::Error ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile(
418418
// Get Required modules in topological order.
419419
auto ReqModuleNames = getAllRequiredModules(MDB, ModuleName);
420420
for (llvm::StringRef ReqModuleName : ReqModuleNames) {
421-
if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
421+
if (BuiltModuleFiles.isModuleUnitBuilt(ReqModuleName))
422422
continue;
423423

424424
if (auto Cached = Cache.getModule(ReqModuleName)) {
425425
if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles,
426426
TFS.view(std::nullopt))) {
427-
log("Reusing module {0} from {1}", ModuleName,
427+
log("Reusing module {0} from {1}", ReqModuleName,
428428
Cached->getModuleFilePath());
429429
BuiltModuleFiles.addModuleFile(std::move(Cached));
430430
continue;
431431
}
432432
Cache.remove(ReqModuleName);
433433
}
434434

435+
std::string ReqFileName =
436+
MDB.getSourceForModuleName(ReqModuleName);
435437
llvm::Expected<ModuleFile> MF = buildModuleFile(
436-
ModuleName, ModuleUnitFileName, getCDB(), TFS, BuiltModuleFiles);
438+
ReqModuleName, ReqFileName, getCDB(), TFS, BuiltModuleFiles);
437439
if (llvm::Error Err = MF.takeError())
438440
return Err;
439441

440-
log("Built module {0} to {1}", ModuleName, MF->getModuleFilePath());
442+
log("Built module {0} to {1}", ReqModuleName, MF->getModuleFilePath());
441443
auto BuiltModuleFile = std::make_shared<const ModuleFile>(std::move(*MF));
442-
Cache.add(ModuleName, BuiltModuleFile);
444+
Cache.add(ReqModuleName, BuiltModuleFile);
443445
BuiltModuleFiles.addModuleFile(std::move(BuiltModuleFile));
444446
}
445447

clang-tools-extra/clangd/ProjectModules.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class ProjectModules {
4242
llvm::unique_function<void(tooling::CompileCommand &, PathRef) const>;
4343

4444
virtual std::vector<std::string> getRequiredModules(PathRef File) = 0;
45-
virtual PathRef
45+
virtual std::string
4646
getSourceForModuleName(llvm::StringRef ModuleName,
4747
PathRef RequiredSrcFile = PathRef()) = 0;
4848

clang-tools-extra/clangd/ScanningProjectModules.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class ModuleDependencyScanner {
6666
///
6767
/// TODO: We should handle the case that there are multiple source files
6868
/// declaring the same module.
69-
PathRef getSourceForModuleName(llvm::StringRef ModuleName) const;
69+
std::string getSourceForModuleName(llvm::StringRef ModuleName) const;
7070

7171
/// Return the direct required modules. Indirect required modules are not
7272
/// included.
@@ -140,7 +140,7 @@ void ModuleDependencyScanner::globalScan(
140140
GlobalScanned = true;
141141
}
142142

143-
PathRef ModuleDependencyScanner::getSourceForModuleName(
143+
std::string ModuleDependencyScanner::getSourceForModuleName(
144144
llvm::StringRef ModuleName) const {
145145
assert(
146146
GlobalScanned &&
@@ -189,7 +189,7 @@ class ScanningAllProjectModules : public ProjectModules {
189189

190190
/// RequiredSourceFile is not used intentionally. See the comments of
191191
/// ModuleDependencyScanner for detail.
192-
PathRef
192+
std::string
193193
getSourceForModuleName(llvm::StringRef ModuleName,
194194
PathRef RequiredSourceFile = PathRef()) override {
195195
Scanner.globalScan(Mangler);
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# A smoke test to check that a simple dependency chain for modules can work.
2+
#
3+
# FIXME: This fails on the Windows ARM64 build server. Not entirely sure why as it has been tested on
4+
# an ARM64 Windows VM and appears to work there.
5+
# UNSUPPORTED: host=aarch64-pc-windows-msvc
6+
#
7+
# RUN: rm -fr %t
8+
# RUN: mkdir -p %t
9+
# RUN: split-file %s %t
10+
#
11+
# RUN: sed -e "s|DIR|%/t|g" %t/compile_commands.json.tmpl > %t/compile_commands.json.tmp
12+
# RUN: sed -e "s|CLANG_CC|%clang|g" %t/compile_commands.json.tmp > %t/compile_commands.json
13+
# RUN: sed -e "s|DIR|%/t|g" %t/definition.jsonrpc.tmpl > %t/definition.jsonrpc.tmp
14+
#
15+
# On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..."
16+
# (with the extra slash in the front), so we add it here.
17+
# RUN: sed -E -e 's|"file://([A-Z]):/|"file:///\1:/|g' %/t/definition.jsonrpc.tmp > %/t/definition.jsonrpc
18+
#
19+
# RUN: clangd -experimental-modules-support -lit-test < %t/definition.jsonrpc \
20+
# RUN: | FileCheck -strict-whitespace %t/definition.jsonrpc
21+
22+
#--- A-frag.cppm
23+
export module A:frag;
24+
export void printA() {}
25+
26+
#--- A.cppm
27+
export module A;
28+
export import :frag;
29+
30+
#--- Use.cpp
31+
import A;
32+
void foo() {
33+
print
34+
}
35+
36+
#--- compile_commands.json.tmpl
37+
[
38+
{
39+
"directory": "DIR",
40+
"command": "CLANG_CC -fprebuilt-module-path=DIR -std=c++20 -o DIR/main.cpp.o -c DIR/Use.cpp",
41+
"file": "DIR/Use.cpp"
42+
},
43+
{
44+
"directory": "DIR",
45+
"command": "CLANG_CC -std=c++20 DIR/A.cppm --precompile -o DIR/A.pcm",
46+
"file": "DIR/A.cppm"
47+
},
48+
{
49+
"directory": "DIR",
50+
"command": "CLANG_CC -std=c++20 DIR/A-frag.cppm --precompile -o DIR/A-frag.pcm",
51+
"file": "DIR/A-frag.cppm"
52+
}
53+
]
54+
55+
#--- definition.jsonrpc.tmpl
56+
{
57+
"jsonrpc": "2.0",
58+
"id": 0,
59+
"method": "initialize",
60+
"params": {
61+
"processId": 123,
62+
"rootPath": "clangd",
63+
"capabilities": {
64+
"textDocument": {
65+
"completion": {
66+
"completionItem": {
67+
"snippetSupport": true
68+
}
69+
}
70+
}
71+
},
72+
"trace": "off"
73+
}
74+
}
75+
---
76+
{
77+
"jsonrpc": "2.0",
78+
"method": "textDocument/didOpen",
79+
"params": {
80+
"textDocument": {
81+
"uri": "file://DIR/Use.cpp",
82+
"languageId": "cpp",
83+
"version": 1,
84+
"text": "import A;\nvoid foo() {\n print\n}\n"
85+
}
86+
}
87+
}
88+
89+
# CHECK: "message"{{.*}}printA{{.*}}(fix available)
90+
91+
---
92+
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file://DIR/Use.cpp"},"context":{"triggerKind":1},"position":{"line":2,"character":6}}}
93+
---
94+
{"jsonrpc":"2.0","id":2,"method":"shutdown"}
95+
---
96+
{"jsonrpc":"2.0","method":"exit"}

0 commit comments

Comments
 (0)