From ba3ceb89e36acf492d7c798879de0a4b331aee04 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Tue, 21 Apr 2026 11:23:19 +0800 Subject: [PATCH] [NFC] [clangd] [C++20] [Modules] Introduce ProjectModules::getModuleNameState interface (#193133) A hole in the current design is that, we assumed there is no duplicated module name in different module interface in the same project. This is not true techniquelly. ISO disallows duplicated module names in a linked program. But we can have multiple program in a project. It will be fine if they are not linked together. And in practice, it will be fine if the symbols are masked and if these module interface units are not showing in the same context of a single translation unit. I am trying to improve this. This patch tries to add some NFC things to reduce further patch size. AI assisted. --- clang-tools-extra/clangd/ProjectModules.cpp | 46 +++++++++++++------ clang-tools-extra/clangd/ProjectModules.h | 9 ++++ .../unittests/PrerequisiteModulesTest.cpp | 4 ++ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/clang-tools-extra/clangd/ProjectModules.cpp b/clang-tools-extra/clangd/ProjectModules.cpp index 67b2a3bd9096..9473f01eab8a 100644 --- a/clang-tools-extra/clangd/ProjectModules.cpp +++ b/clang-tools-extra/clangd/ProjectModules.cpp @@ -1,5 +1,4 @@ -//===------------------ ProjectModules.cpp -------------------------*- -//C++-*-===// +//===------------------ ProjectModules.cpp --------- ------------*- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,6 +13,26 @@ namespace clang::clangd { namespace { + +std::optional +getCompileCommandForFile(const clang::tooling::CompilationDatabase &CDB, + PathRef FilePath, + const ProjectModules::CommandMangler &Mangler) { + auto Candidates = CDB.getCompileCommands(FilePath); + if (Candidates.empty()) + return std::nullopt; + + // Choose the first candidates as the compile commands as the file. + // Following the same logic with + // DirectoryBasedGlobalCompilationDatabase::getCompileCommand. + tooling::CompileCommand Cmd = std::move(Candidates.front()); + + if (Mangler) + Mangler(Cmd, FilePath); + + return Cmd; +} + /// A scanner to query the dependency information for C++20 Modules. /// /// The scanner can scan a single file with `scan(PathRef)` member function @@ -96,18 +115,10 @@ private: std::optional ModuleDependencyScanner::scan(PathRef FilePath, const ProjectModules::CommandMangler &Mangler) { - auto Candidates = CDB->getCompileCommands(FilePath); - if (Candidates.empty()) + auto Cmd = getCompileCommandForFile(*CDB, FilePath, Mangler); + if (!Cmd) return std::nullopt; - // Choose the first candidates as the compile commands as the file. - // Following the same logic with - // DirectoryBasedGlobalCompilationDatabase::getCompileCommand. - tooling::CompileCommand Cmd = std::move(Candidates.front()); - - if (Mangler) - Mangler(Cmd, FilePath); - using namespace clang::tooling; DependencyScanningTool ScanningTool(Service); @@ -119,13 +130,13 @@ ModuleDependencyScanner::scan(PathRef FilePath, TextDiagnosticPrinter DiagConsumer(OS, DiagOpts); std::optional ScanningResult = - ScanningTool.getP1689ModuleDependencyFile(Cmd, Cmd.Directory, + ScanningTool.getP1689ModuleDependencyFile(*Cmd, Cmd->Directory, DiagConsumer); if (!ScanningResult) { elog("Scanning modules dependencies for {0} failed: {1}", FilePath, S); std::string Cmdline; - for (auto &Arg : Cmd.CommandLine) + for (auto &Arg : Cmd->CommandLine) Cmdline += Arg + " "; elog("The command line the scanning tool use is: {0}", Cmdline); return std::nullopt; @@ -227,6 +238,13 @@ public: return *ScanningResult->ModuleName; } + // Determining Unique/Multiple needs a global scan; return Unknown for cost + // reasons. We will have other ProjectModules implementations can determine + // this more efficiently. + ModuleNameState getModuleNameState(llvm::StringRef /*ModuleName*/) override { + return ModuleNameState::Unknown; + } + private: ModuleDependencyScanner Scanner; CommandMangler Mangler; diff --git a/clang-tools-extra/clangd/ProjectModules.h b/clang-tools-extra/clangd/ProjectModules.h index 057e1b83764b..fbf658bafe15 100644 --- a/clang-tools-extra/clangd/ProjectModules.h +++ b/clang-tools-extra/clangd/ProjectModules.h @@ -38,6 +38,12 @@ namespace clangd { /// `[:partition-name]`. So module names covers partitions. class ProjectModules { public: + enum class ModuleNameState { + Unknown, + Unique, + Multiple, + }; + using CommandMangler = llvm::unique_function; @@ -45,6 +51,9 @@ public: virtual std::string getModuleNameForSource(PathRef File) = 0; virtual std::string getSourceForModuleName(llvm::StringRef ModuleName, PathRef RequiredSrcFile) = 0; + virtual ModuleNameState getModuleNameState(llvm::StringRef ModuleName) { + return ModuleNameState::Unknown; + } virtual void setCommandMangler(CommandMangler Mangler) {} diff --git a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp index 6a4250cb5389..3622f9cf694b 100644 --- a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp +++ b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp @@ -54,6 +54,10 @@ public: return Underlying->getSourceForModuleName(ModuleName, RequiredSrcFile); } + ModuleNameState getModuleNameState(llvm::StringRef ModuleName) override { + return Underlying->getModuleNameState(ModuleName); + } + private: std::unique_ptr Underlying; std::atomic &Count;