================ @@ -0,0 +1,206 @@ +//===------------------ ProjectModules.h -------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ProjectModules.h" + +#include "support/Logger.h" + +#include "clang/Tooling/DependencyScanning/DependencyScanningService.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" + +using namespace clang; +using namespace clang::clangd; + +namespace { +/// A scanner to query the dependency information for C++20 Modules. +/// +/// The scanner can scan a single file with `scan(PathRef)` member function +/// or scan the whole project with `globalScan(vector<PathRef>)` member +/// function. See the comments of `globalScan` to see the details. +/// +/// The ModuleDependencyScanner can get the directly required module names for a +/// specific source file. Also the ModuleDependencyScanner can get the source +/// file declaring the primary module interface for a specific module name. +/// +/// IMPORTANT NOTE: we assume that every module unit is only declared once in a +/// source file in the project. But the assumption is not strictly true even +/// besides the invalid projects. The language specification requires that every +/// module unit should be unique in a valid program. But a project can contain +/// multiple programs. Then it is valid that we can have multiple source files +/// declaring the same module in a project as long as these source files don't +/// interfere with each other. +class ModuleDependencyScanner { +public: + ModuleDependencyScanner(const GlobalCompilationDatabase &CDB, + const ThreadsafeFS &TFS) + : CDB(CDB), TFS(TFS), + Service(tooling::dependencies::ScanningMode::CanonicalPreprocessing, + tooling::dependencies::ScanningOutputFormat::P1689) {} + + /// The scanned modules dependency information for a specific source file. + struct ModuleDependencyInfo { + /// The name of the module if the file is a module unit. + std::optional<std::string> ModuleName; + /// A list of names for the modules that the file directly depends. + std::vector<std::string> RequiredModules; + }; + + /// Scanning the single file specified by \param FilePath. + std::optional<ModuleDependencyInfo> scan(PathRef FilePath); + + /// Scanning every source file in the current project to get the + /// <module-name> to <module-unit-source> map. + /// TODO: We should find an efficient method to get the <module-name> + /// to <module-unit-source> map. We can make it either by providing + /// a global module dependency scanner to monitor every file. Or we + /// can simply require the build systems (or even the end users) + /// to provide the map. + void globalScan(llvm::ArrayRef<std::string> AllFiles); + + /// Get the source file from the module name. Note that the language + /// guarantees all the module names are unique in a valid program. + /// This function should only be called after globalScan. + /// + /// TODO: We should handle the case that there are multiple source files + /// declaring the same module. + PathRef getSourceForModuleName(llvm::StringRef ModuleName) const; + + /// Return the direct required modules. Indirect required modules are not + /// included. + std::vector<std::string> getRequiredModules(PathRef File); + +private: + const GlobalCompilationDatabase &CDB; + const ThreadsafeFS &TFS; + + // Whether the scanner has scanned the project globally. + bool GlobalScanned = false; + + clang::tooling::dependencies::DependencyScanningService Service; + + // TODO: Add a scanning cache. + + // Map module name to source file path. + llvm::StringMap<std::string> ModuleNameToSource; +}; + +std::optional<ModuleDependencyScanner::ModuleDependencyInfo> +ModuleDependencyScanner::scan(PathRef FilePath) { + std::optional<tooling::CompileCommand> Cmd = CDB.getCompileCommand(FilePath); + + if (!Cmd) + return std::nullopt; + + using namespace clang::tooling::dependencies; + + llvm::SmallString<128> FilePathDir(FilePath); + llvm::sys::path::remove_filename(FilePathDir); + DependencyScanningTool ScanningTool(Service, TFS.view(FilePathDir)); + + llvm::Expected<P1689Rule> ScanningResult = + ScanningTool.getP1689ModuleDependencyFile(*Cmd, Cmd->Directory); + + if (auto E = ScanningResult.takeError()) { + elog("Scanning modules dependencies for {0} failed: {1}", FilePath, + llvm::toString(std::move(E))); + return std::nullopt; + } + + ModuleDependencyInfo Result; + + if (ScanningResult->Provides) { + ModuleNameToSource[ScanningResult->Provides->ModuleName] = FilePath; + Result.ModuleName = ScanningResult->Provides->ModuleName; + } + + for (auto &Required : ScanningResult->Requires) + Result.RequiredModules.push_back(Required.ModuleName); + + return Result; +} + +void ModuleDependencyScanner::globalScan(llvm::ArrayRef<std::string> AllFiles) { + for (auto &File : AllFiles) + scan(File); + + GlobalScanned = true; +} + +PathRef ModuleDependencyScanner::getSourceForModuleName( + llvm::StringRef ModuleName) const { + assert( + GlobalScanned && + "We should only call getSourceForModuleName after calling globalScan()"); + + if (auto It = ModuleNameToSource.find(ModuleName); + It != ModuleNameToSource.end()) + return It->second; + + return {}; +} + +std::vector<std::string> +ModuleDependencyScanner::getRequiredModules(PathRef File) { + auto ScanningResult = scan(File); + if (!ScanningResult) + return {}; + + return ScanningResult->RequiredModules; +} +} // namespace + +namespace clang { +namespace clangd { + +/// TODO: The existing `ScanningAllProjectModules` is not efficient. See the +/// comments in ModuleDependencyScanner for detail. +/// +/// In the future, we wish the build system can provide a well design +/// compilation database for modules then we can query that new compilation +/// database directly. Or we need to have a global long-live scanner to detect +/// the state of each file. +class ScanningAllProjectModules : public ProjectModules { ---------------- kadircet wrote:
this also belongs to anon-namepsace https://github.com/llvm/llvm-project/pull/66462 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits