diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -104,6 +104,10 @@ class ModuleDepCollector; +/// Callback that records textual includes and direct modular includes/imports +/// during preprocessing. At the end of the main file, it also collects +/// transitive modular dependencies and passes everything to the +/// \c DependencyConsumer of the parent \c ModuleDepCollector. class ModuleDepCollectorPP final : public PPCallbacks { public: ModuleDepCollectorPP(CompilerInstance &I, ModuleDepCollector &MDC) @@ -124,11 +128,18 @@ void EndOfMainFile() override; private: + /// The compiler instance for the current translation unit. CompilerInstance &Instance; + /// The parent dependency collector. ModuleDepCollector &MDC; - llvm::DenseSet DirectDeps; + /// Working set of direct modular dependencies. + llvm::DenseSet DirectModularDeps; void handleImport(const Module *Imported); + + /// Traverses the previously collected direct modular dependencies to discover + /// transitive modular dependencies and fills the parent \c ModuleDepCollector + /// with both. void handleTopLevelModule(const Module *M); void addAllSubmoduleDeps(const Module *M, ModuleDeps &MD, llvm::DenseSet &AddedModules); @@ -136,6 +147,8 @@ llvm::DenseSet &AddedModules); }; +/// Collects modular and non-modular dependencies of the main file by attaching +/// \c ModuleDepCollectorPP to the preprocessor. class ModuleDepCollector final : public DependencyCollector { public: ModuleDepCollector(std::unique_ptr Opts, @@ -147,12 +160,20 @@ private: friend ModuleDepCollectorPP; + /// The compiler instance for the current translation unit. CompilerInstance &Instance; + /// The consumer of collected dependency information. DependencyConsumer &Consumer; + /// Path to the main source file. std::string MainFile; + /// The module hash identifying the compilation conditions. std::string ContextHash; - std::vector MainDeps; - std::unordered_map Deps; + /// Non-modular file dependencies. This includes the main source file and + /// textually included header files. + std::vector FileDeps; + /// Direct and transitive modular dependencies of the main source file. + std::unordered_map ModularDeps; + /// Options that control the dependency output generation. std::unique_ptr Opts; }; diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -79,7 +79,7 @@ // We do not want #line markers to affect dependency generation! if (Optional Filename = SM.getNonBuiltinFilenameForID(SM.getFileID(SM.getExpansionLoc(Loc)))) - MDC.MainDeps.push_back( + MDC.FileDeps.push_back( std::string(llvm::sys::path::remove_leading_dotslash(*Filename))); } @@ -91,7 +91,7 @@ if (!File && !Imported) { // This is a non-modular include that HeaderSearch failed to find. Add it // here as `FileChanged` will never see it. - MDC.MainDeps.push_back(std::string(FileName)); + MDC.FileDeps.push_back(std::string(FileName)); } handleImport(Imported); } @@ -106,9 +106,10 @@ if (!Imported) return; - MDC.Deps[MDC.ContextHash + Imported->getTopLevelModule()->getFullModuleName()] + const Module *TopLevelModule = Imported->getTopLevelModule(); + MDC.ModularDeps[MDC.ContextHash + TopLevelModule->getFullModuleName()] .ImportedByMainFile = true; - DirectDeps.insert(Imported->getTopLevelModule()); + DirectModularDeps.insert(TopLevelModule); } void ModuleDepCollectorPP::EndOfMainFile() { @@ -116,21 +117,20 @@ MDC.MainFile = std::string( Instance.getSourceManager().getFileEntryForID(MainFileID)->getName()); - for (const Module *M : DirectDeps) { + for (const Module *M : DirectModularDeps) handleTopLevelModule(M); - } - for (auto &&I : MDC.Deps) + for (auto &&I : MDC.ModularDeps) MDC.Consumer.handleModuleDependency(I.second); - for (auto &&I : MDC.MainDeps) + for (auto &&I : MDC.FileDeps) MDC.Consumer.handleFileDependency(*MDC.Opts, I); } void ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { assert(M == M->getTopLevelModule() && "Expected top level module!"); - auto ModI = MDC.Deps.insert( + auto ModI = MDC.ModularDeps.insert( std::make_pair(MDC.ContextHash + M->getFullModuleName(), ModuleDeps{})); if (!ModI.first->second.ID.ModuleName.empty())