-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[clangd] [Modules] Fixes to correctly handle module dependencies #142090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,8 +84,7 @@ class FailedPrerequisiteModules : public PrerequisiteModules { | |
|
|
||
| // We shouldn't adjust the compilation commands based on | ||
| // FailedPrerequisiteModules. | ||
| void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override { | ||
| } | ||
| void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override {} | ||
|
|
||
| // FailedPrerequisiteModules can never be reused. | ||
| bool | ||
|
|
@@ -430,21 +429,21 @@ class CachingProjectModules : public ProjectModules { | |
| /// Collect the directly and indirectly required module names for \param | ||
| /// ModuleName in topological order. The \param ModuleName is guaranteed to | ||
| /// be the last element in \param ModuleNames. | ||
| llvm::SmallVector<StringRef> getAllRequiredModules(PathRef RequiredSource, | ||
| CachingProjectModules &MDB, | ||
| StringRef ModuleName) { | ||
| llvm::SmallVector<llvm::StringRef> ModuleNames; | ||
| llvm::SmallVector<std::string> getAllRequiredModules(PathRef RequiredSource, | ||
| CachingProjectModules &MDB, | ||
| StringRef ModuleName) { | ||
| llvm::SmallVector<std::string> ModuleNames; | ||
| llvm::StringSet<> ModuleNamesSet; | ||
|
|
||
| auto VisitDeps = [&](StringRef ModuleName, auto Visitor) -> void { | ||
| ModuleNamesSet.insert(ModuleName); | ||
|
|
||
| for (StringRef RequiredModuleName : MDB.getRequiredModules( | ||
| for (const std::string &RequiredModuleName : MDB.getRequiredModules( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be better to use StringRef here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll change this back to a |
||
| MDB.getSourceForModuleName(ModuleName, RequiredSource))) | ||
| if (ModuleNamesSet.insert(RequiredModuleName).second) | ||
| Visitor(RequiredModuleName, Visitor); | ||
|
|
||
| ModuleNames.push_back(ModuleName); | ||
| ModuleNames.push_back(ModuleName.str()); | ||
| }; | ||
| VisitDeps(ModuleName, VisitDeps); | ||
|
|
||
|
|
@@ -494,28 +493,30 @@ llvm::Error ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile( | |
| // Get Required modules in topological order. | ||
| auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName); | ||
| for (llvm::StringRef ReqModuleName : ReqModuleNames) { | ||
| if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName)) | ||
| if (BuiltModuleFiles.isModuleUnitBuilt(ReqModuleName)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, what a shame. I didn't notice this, my bad. |
||
| continue; | ||
|
|
||
| if (auto Cached = Cache.getModule(ReqModuleName)) { | ||
| if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles, | ||
| TFS.view(std::nullopt))) { | ||
| log("Reusing module {0} from {1}", ModuleName, | ||
| log("Reusing module {0} from {1}", ReqModuleName, | ||
| Cached->getModuleFilePath()); | ||
| BuiltModuleFiles.addModuleFile(std::move(Cached)); | ||
| continue; | ||
| } | ||
| Cache.remove(ReqModuleName); | ||
| } | ||
|
|
||
| std::string ReqFileName = | ||
| MDB.getSourceForModuleName(ReqModuleName, RequiredSource); | ||
| llvm::Expected<ModuleFile> MF = buildModuleFile( | ||
| ModuleName, ModuleUnitFileName, getCDB(), TFS, BuiltModuleFiles); | ||
| ReqModuleName, ReqFileName, getCDB(), TFS, BuiltModuleFiles); | ||
| if (llvm::Error Err = MF.takeError()) | ||
| return Err; | ||
|
|
||
| log("Built module {0} to {1}", ModuleName, MF->getModuleFilePath()); | ||
| log("Built module {0} to {1}", ReqModuleName, MF->getModuleFilePath()); | ||
| auto BuiltModuleFile = std::make_shared<const ModuleFile>(std::move(*MF)); | ||
| Cache.add(ModuleName, BuiltModuleFile); | ||
| Cache.add(ReqModuleName, BuiltModuleFile); | ||
| BuiltModuleFiles.addModuleFile(std::move(BuiltModuleFile)); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # A smoke test to check that a simple dependency chain for modules can work. | ||
| # | ||
| # RUN: rm -fr %t | ||
| # RUN: mkdir -p %t | ||
| # RUN: split-file %s %t | ||
| # | ||
| # RUN: sed -e "s|DIR|%/t|g" %t/compile_commands.json.tmpl > %t/compile_commands.json.tmp | ||
| # RUN: sed -e "s|CLANG_CC|%clang|g" %t/compile_commands.json.tmp > %t/compile_commands.json | ||
| # RUN: sed -e "s|DIR|%/t|g" %t/definition.jsonrpc.tmpl > %t/definition.jsonrpc.tmp | ||
| # | ||
| # On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..." | ||
| # (with the extra slash in the front), so we add it here. | ||
| # RUN: sed -E -e 's|"file://([A-Z]):/|"file:///\1:/|g' %/t/definition.jsonrpc.tmp > %/t/definition.jsonrpc | ||
| # | ||
| # RUN: clangd -experimental-modules-support -lit-test < %t/definition.jsonrpc \ | ||
| # RUN: | FileCheck -strict-whitespace %t/definition.jsonrpc | ||
|
|
||
| #--- A-frag.cppm | ||
| export module A:frag; | ||
| export void printA() {} | ||
|
|
||
| #--- A.cppm | ||
| export module A; | ||
| export import :frag; | ||
|
|
||
| #--- Use.cpp | ||
| import A; | ||
| void foo() { | ||
| } | ||
|
|
||
| #--- compile_commands.json.tmpl | ||
| [ | ||
| { | ||
| "directory": "DIR", | ||
| "command": "CLANG_CC -fprebuilt-module-path=DIR -std=c++20 -o DIR/main.cpp.o -c DIR/Use.cpp", | ||
| "file": "DIR/Use.cpp" | ||
| }, | ||
| { | ||
| "directory": "DIR", | ||
| "command": "CLANG_CC -std=c++20 DIR/A.cppm --precompile -o DIR/A.pcm", | ||
| "file": "DIR/A.cppm" | ||
| }, | ||
| { | ||
| "directory": "DIR", | ||
| "command": "CLANG_CC -std=c++20 DIR/A-frag.cppm --precompile -o DIR/A-frag.pcm", | ||
| "file": "DIR/A-frag.cppm" | ||
| } | ||
| ] | ||
|
|
||
| #--- definition.jsonrpc.tmpl | ||
| { | ||
| "jsonrpc": "2.0", | ||
| "id": 0, | ||
| "method": "initialize", | ||
| "params": { | ||
| "processId": 123, | ||
| "rootPath": "clangd", | ||
| "capabilities": { | ||
| "textDocument": { | ||
| "completion": { | ||
| "completionItem": { | ||
| "snippetSupport": true | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "trace": "off" | ||
| } | ||
| } | ||
| --- | ||
| { | ||
| "jsonrpc": "2.0", | ||
| "method": "textDocument/didOpen", | ||
| "params": { | ||
| "textDocument": { | ||
| "uri": "file://DIR/Use.cpp", | ||
| "languageId": "cpp", | ||
| "version": 1, | ||
| "text": "import A;\nvoid foo() {\n print\n}\n" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| # CHECK: "message"{{.*}}printA{{.*}}(fix available) | ||
|
|
||
| --- | ||
| {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file://DIR/Use.cpp"},"context":{"triggerKind":1},"position":{"line":2,"character":6}}} | ||
| --- | ||
| {"jsonrpc":"2.0","id":2,"method":"shutdown"} | ||
| --- | ||
| {"jsonrpc":"2.0","method":"exit"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally we don't like unnecessary changes, if you want, you can make such changes in seperate pacthes.