xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp (revision c8e7f78a3d28ff6e6223ed136ada8e1e2f34965e)
1 //===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
10 
11 #include "clang/Basic/MakeSupport.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Lex/Preprocessor.h"
14 #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
15 #include "llvm/Support/BLAKE3.h"
16 #include "llvm/Support/StringSaver.h"
17 #include <optional>
18 
19 using namespace clang;
20 using namespace tooling;
21 using namespace dependencies;
22 
23 static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
24                                      ASTReader &Reader,
25                                      const serialization::ModuleFile &MF) {
26   // Only preserve search paths that were used during the dependency scan.
27   std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries;
28   Opts.UserEntries.clear();
29 
30   llvm::BitVector SearchPathUsage(Entries.size());
31   llvm::DenseSet<const serialization::ModuleFile *> Visited;
32   std::function<void(const serialization::ModuleFile *)> VisitMF =
33       [&](const serialization::ModuleFile *MF) {
34         SearchPathUsage |= MF->SearchPathUsage;
35         Visited.insert(MF);
36         for (const serialization::ModuleFile *Import : MF->Imports)
37           if (!Visited.contains(Import))
38             VisitMF(Import);
39       };
40   VisitMF(&MF);
41 
42   for (auto Idx : SearchPathUsage.set_bits())
43     Opts.UserEntries.push_back(Entries[Idx]);
44 }
45 
46 static std::vector<std::string> splitString(std::string S, char Separator) {
47   SmallVector<StringRef> Segments;
48   StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
49   std::vector<std::string> Result;
50   Result.reserve(Segments.size());
51   for (StringRef Segment : Segments)
52     Result.push_back(Segment.str());
53   return Result;
54 }
55 
56 void ModuleDepCollector::addOutputPaths(CompilerInvocation &CI,
57                                         ModuleDeps &Deps) {
58   CI.getFrontendOpts().OutputFile =
59       Controller.lookupModuleOutput(Deps.ID, ModuleOutputKind::ModuleFile);
60   if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
61     CI.getDiagnosticOpts().DiagnosticSerializationFile =
62         Controller.lookupModuleOutput(
63             Deps.ID, ModuleOutputKind::DiagnosticSerializationFile);
64   if (!CI.getDependencyOutputOpts().OutputFile.empty()) {
65     CI.getDependencyOutputOpts().OutputFile = Controller.lookupModuleOutput(
66         Deps.ID, ModuleOutputKind::DependencyFile);
67     CI.getDependencyOutputOpts().Targets =
68         splitString(Controller.lookupModuleOutput(
69                         Deps.ID, ModuleOutputKind::DependencyTargets),
70                     '\0');
71     if (!CI.getDependencyOutputOpts().OutputFile.empty() &&
72         CI.getDependencyOutputOpts().Targets.empty()) {
73       // Fallback to -o as dependency target, as in the driver.
74       SmallString<128> Target;
75       quoteMakeTarget(CI.getFrontendOpts().OutputFile, Target);
76       CI.getDependencyOutputOpts().Targets.push_back(std::string(Target));
77     }
78   }
79 }
80 
81 CompilerInvocation
82 ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs(
83     const ModuleDeps &Deps,
84     llvm::function_ref<void(CompilerInvocation &)> Optimize) const {
85   // Make a deep copy of the original Clang invocation.
86   CompilerInvocation CI(OriginalInvocation);
87 
88   CI.resetNonModularOptions();
89   CI.clearImplicitModuleBuildOptions();
90 
91   // Remove options incompatible with explicit module build or are likely to
92   // differ between identical modules discovered from different translation
93   // units.
94   CI.getFrontendOpts().Inputs.clear();
95   CI.getFrontendOpts().OutputFile.clear();
96 
97   // TODO: Figure out better way to set options to their default value.
98   CI.getCodeGenOpts().MainFileName.clear();
99   CI.getCodeGenOpts().DwarfDebugFlags.clear();
100   if (!CI.getLangOpts()->ModulesCodegen) {
101     CI.getCodeGenOpts().DebugCompilationDir.clear();
102     CI.getCodeGenOpts().CoverageCompilationDir.clear();
103     CI.getCodeGenOpts().CoverageDataFile.clear();
104     CI.getCodeGenOpts().CoverageNotesFile.clear();
105   }
106 
107   // Map output paths that affect behaviour to "-" so their existence is in the
108   // context hash. The final path will be computed in addOutputPaths.
109   if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
110     CI.getDiagnosticOpts().DiagnosticSerializationFile = "-";
111   if (!CI.getDependencyOutputOpts().OutputFile.empty())
112     CI.getDependencyOutputOpts().OutputFile = "-";
113   CI.getDependencyOutputOpts().Targets.clear();
114 
115   CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
116   CI.getFrontendOpts().ARCMTAction = FrontendOptions::ARCMT_None;
117   CI.getFrontendOpts().ObjCMTAction = FrontendOptions::ObjCMT_None;
118   CI.getFrontendOpts().MTMigrateDir.clear();
119   CI.getLangOpts()->ModuleName = Deps.ID.ModuleName;
120   CI.getFrontendOpts().IsSystemModule = Deps.IsSystem;
121 
122   // Inputs
123   InputKind ModuleMapInputKind(CI.getFrontendOpts().DashX.getLanguage(),
124                                InputKind::Format::ModuleMap);
125   CI.getFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile,
126                                            ModuleMapInputKind);
127 
128   auto CurrentModuleMapEntry =
129       ScanInstance.getFileManager().getFile(Deps.ClangModuleMapFile);
130   assert(CurrentModuleMapEntry && "module map file entry not found");
131 
132   auto DepModuleMapFiles = collectModuleMapFiles(Deps.ClangModuleDeps);
133   for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) {
134     // TODO: Track these as `FileEntryRef` to simplify the equality check below.
135     auto ModuleMapEntry = ScanInstance.getFileManager().getFile(ModuleMapFile);
136     assert(ModuleMapEntry && "module map file entry not found");
137 
138     // Don't report module maps describing eagerly-loaded dependency. This
139     // information will be deserialized from the PCM.
140     // TODO: Verify this works fine when modulemap for module A is eagerly
141     // loaded from A.pcm, and module map passed on the command line contains
142     // definition of a submodule: "explicit module A.Private { ... }".
143     if (EagerLoadModules && DepModuleMapFiles.contains(*ModuleMapEntry))
144       continue;
145 
146     // Don't report module map file of the current module unless it also
147     // describes a dependency (for symmetry).
148     if (*ModuleMapEntry == *CurrentModuleMapEntry &&
149         !DepModuleMapFiles.contains(*ModuleMapEntry))
150       continue;
151 
152     CI.getFrontendOpts().ModuleMapFiles.emplace_back(ModuleMapFile);
153   }
154 
155   // Report the prebuilt modules this module uses.
156   for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
157     CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
158 
159   // Add module file inputs from dependencies.
160   addModuleFiles(CI, Deps.ClangModuleDeps);
161 
162   // Remove any macro definitions that are explicitly ignored.
163   if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) {
164     llvm::erase_if(
165         CI.getPreprocessorOpts().Macros,
166         [&CI](const std::pair<std::string, bool> &Def) {
167           StringRef MacroDef = Def.first;
168           return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains(
169               llvm::CachedHashString(MacroDef.split('=').first));
170         });
171     // Remove the now unused option.
172     CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear();
173   }
174 
175   Optimize(CI);
176 
177   return CI;
178 }
179 
180 llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles(
181     ArrayRef<ModuleID> ClangModuleDeps) const {
182   llvm::DenseSet<const FileEntry *> ModuleMapFiles;
183   for (const ModuleID &MID : ClangModuleDeps) {
184     ModuleDeps *MD = ModuleDepsByID.lookup(MID);
185     assert(MD && "Inconsistent dependency info");
186     // TODO: Track ClangModuleMapFile as `FileEntryRef`.
187     auto FE = ScanInstance.getFileManager().getFile(MD->ClangModuleMapFile);
188     assert(FE && "Missing module map file that was previously found");
189     ModuleMapFiles.insert(*FE);
190   }
191   return ModuleMapFiles;
192 }
193 
194 void ModuleDepCollector::addModuleMapFiles(
195     CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
196   if (EagerLoadModules)
197     return; // Only pcm is needed for eager load.
198 
199   for (const ModuleID &MID : ClangModuleDeps) {
200     ModuleDeps *MD = ModuleDepsByID.lookup(MID);
201     assert(MD && "Inconsistent dependency info");
202     CI.getFrontendOpts().ModuleMapFiles.push_back(MD->ClangModuleMapFile);
203   }
204 }
205 
206 void ModuleDepCollector::addModuleFiles(
207     CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
208   for (const ModuleID &MID : ClangModuleDeps) {
209     std::string PCMPath =
210         Controller.lookupModuleOutput(MID, ModuleOutputKind::ModuleFile);
211     if (EagerLoadModules)
212       CI.getFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));
213     else
214       CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
215           {MID.ModuleName, std::move(PCMPath)});
216   }
217 }
218 
219 static bool needsModules(FrontendInputFile FIF) {
220   switch (FIF.getKind().getLanguage()) {
221   case Language::Unknown:
222   case Language::Asm:
223   case Language::LLVM_IR:
224     return false;
225   default:
226     return true;
227   }
228 }
229 
230 void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) {
231   CI.clearImplicitModuleBuildOptions();
232 
233   if (llvm::any_of(CI.getFrontendOpts().Inputs, needsModules)) {
234     Preprocessor &PP = ScanInstance.getPreprocessor();
235     if (Module *CurrentModule = PP.getCurrentModuleImplementation())
236       if (OptionalFileEntryRef CurrentModuleMap =
237               PP.getHeaderSearchInfo()
238                   .getModuleMap()
239                   .getModuleMapFileForUniquing(CurrentModule))
240         CI.getFrontendOpts().ModuleMapFiles.emplace_back(
241             CurrentModuleMap->getNameAsRequested());
242 
243     SmallVector<ModuleID> DirectDeps;
244     for (const auto &KV : ModularDeps)
245       if (KV.second->ImportedByMainFile)
246         DirectDeps.push_back(KV.second->ID);
247 
248     // TODO: Report module maps the same way it's done for modular dependencies.
249     addModuleMapFiles(CI, DirectDeps);
250 
251     addModuleFiles(CI, DirectDeps);
252 
253     for (const auto &KV : DirectPrebuiltModularDeps)
254       CI.getFrontendOpts().ModuleFiles.push_back(KV.second.PCMFile);
255   }
256 }
257 
258 static std::string getModuleContextHash(const ModuleDeps &MD,
259                                         const CompilerInvocation &CI,
260                                         bool EagerLoadModules) {
261   llvm::HashBuilder<llvm::TruncatedBLAKE3<16>,
262                     llvm::support::endianness::native>
263       HashBuilder;
264   SmallString<32> Scratch;
265 
266   // Hash the compiler version and serialization version to ensure the module
267   // will be readable.
268   HashBuilder.add(getClangFullRepositoryVersion());
269   HashBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR);
270 
271   // Hash the BuildInvocation without any input files.
272   SmallVector<const char *, 32> Args;
273   llvm::BumpPtrAllocator Alloc;
274   llvm::StringSaver Saver(Alloc);
275   CI.generateCC1CommandLine(
276       Args, [&](const Twine &Arg) { return Saver.save(Arg).data(); });
277   HashBuilder.addRange(Args);
278 
279   // Hash the module dependencies. These paths may differ even if the invocation
280   // is identical if they depend on the contents of the files in the TU -- for
281   // example, case-insensitive paths to modulemap files. Usually such a case
282   // would indicate a missed optimization to canonicalize, but it may be
283   // difficult to canonicalize all cases when there is a VFS.
284   for (const auto &ID : MD.ClangModuleDeps) {
285     HashBuilder.add(ID.ModuleName);
286     HashBuilder.add(ID.ContextHash);
287   }
288 
289   HashBuilder.add(EagerLoadModules);
290 
291   llvm::BLAKE3Result<16> Hash = HashBuilder.final();
292   std::array<uint64_t, 2> Words;
293   static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words");
294   std::memcpy(Words.data(), Hash.data(), sizeof(Hash));
295   return toString(llvm::APInt(sizeof(Words) * 8, Words), 36, /*Signed=*/false);
296 }
297 
298 void ModuleDepCollector::associateWithContextHash(const CompilerInvocation &CI,
299                                                   ModuleDeps &Deps) {
300   Deps.ID.ContextHash = getModuleContextHash(Deps, CI, EagerLoadModules);
301   bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second;
302   (void)Inserted;
303   assert(Inserted && "duplicate module mapping");
304 }
305 
306 void ModuleDepCollectorPP::LexedFileChanged(FileID FID,
307                                             LexedFileChangeReason Reason,
308                                             SrcMgr::CharacteristicKind FileType,
309                                             FileID PrevFID,
310                                             SourceLocation Loc) {
311   if (Reason != LexedFileChangeReason::EnterFile)
312     return;
313 
314   // This has to be delayed as the context hash can change at the start of
315   // `CompilerInstance::ExecuteAction`.
316   if (MDC.ContextHash.empty()) {
317     MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash();
318     MDC.Consumer.handleContextHash(MDC.ContextHash);
319   }
320 
321   SourceManager &SM = MDC.ScanInstance.getSourceManager();
322 
323   // Dependency generation really does want to go all the way to the
324   // file entry for a source location to find out what is depended on.
325   // We do not want #line markers to affect dependency generation!
326   if (std::optional<StringRef> Filename = SM.getNonBuiltinFilenameForID(FID))
327     MDC.addFileDep(llvm::sys::path::remove_leading_dotslash(*Filename));
328 }
329 
330 void ModuleDepCollectorPP::InclusionDirective(
331     SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
332     bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
333     StringRef SearchPath, StringRef RelativePath, const Module *Imported,
334     SrcMgr::CharacteristicKind FileType) {
335   if (!File && !Imported) {
336     // This is a non-modular include that HeaderSearch failed to find. Add it
337     // here as `FileChanged` will never see it.
338     MDC.addFileDep(FileName);
339   }
340   handleImport(Imported);
341 }
342 
343 void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc,
344                                         ModuleIdPath Path,
345                                         const Module *Imported) {
346   if (MDC.ScanInstance.getPreprocessor().isInImportingCXXNamedModules()) {
347     P1689ModuleInfo RequiredModule;
348     RequiredModule.ModuleName = Path[0].first->getName().str();
349     RequiredModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
350     MDC.RequiredStdCXXModules.push_back(RequiredModule);
351     return;
352   }
353 
354   handleImport(Imported);
355 }
356 
357 void ModuleDepCollectorPP::handleImport(const Module *Imported) {
358   if (!Imported)
359     return;
360 
361   const Module *TopLevelModule = Imported->getTopLevelModule();
362 
363   if (MDC.isPrebuiltModule(TopLevelModule))
364     MDC.DirectPrebuiltModularDeps.insert(
365         {TopLevelModule, PrebuiltModuleDep{TopLevelModule}});
366   else
367     DirectModularDeps.insert(TopLevelModule);
368 }
369 
370 void ModuleDepCollectorPP::EndOfMainFile() {
371   FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID();
372   MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager()
373                                  .getFileEntryForID(MainFileID)
374                                  ->getName());
375 
376   auto &PP = MDC.ScanInstance.getPreprocessor();
377   if (PP.isInNamedModule()) {
378     P1689ModuleInfo ProvidedModule;
379     ProvidedModule.ModuleName = PP.getNamedModuleName();
380     ProvidedModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
381     ProvidedModule.IsStdCXXModuleInterface = PP.isInNamedInterfaceUnit();
382     // Don't put implementation (non partition) unit as Provide.
383     // Put the module as required instead. Since the implementation
384     // unit will import the primary module implicitly.
385     if (PP.isInImplementationUnit())
386       MDC.RequiredStdCXXModules.push_back(ProvidedModule);
387     else
388       MDC.ProvidedStdCXXModule = ProvidedModule;
389   }
390 
391   if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
392     MDC.addFileDep(MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
393 
394   for (const Module *M :
395        MDC.ScanInstance.getPreprocessor().getAffectingClangModules())
396     if (!MDC.isPrebuiltModule(M))
397       DirectModularDeps.insert(M);
398 
399   for (const Module *M : DirectModularDeps)
400     handleTopLevelModule(M);
401 
402   MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts);
403 
404   if (MDC.IsStdModuleP1689Format)
405     MDC.Consumer.handleProvidedAndRequiredStdCXXModules(
406         MDC.ProvidedStdCXXModule, MDC.RequiredStdCXXModules);
407 
408   for (auto &&I : MDC.ModularDeps)
409     MDC.Consumer.handleModuleDependency(*I.second);
410 
411   for (auto &&I : MDC.FileDeps)
412     MDC.Consumer.handleFileDependency(I);
413 
414   for (auto &&I : MDC.DirectPrebuiltModularDeps)
415     MDC.Consumer.handlePrebuiltModuleDependency(I.second);
416 }
417 
418 std::optional<ModuleID>
419 ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
420   assert(M == M->getTopLevelModule() && "Expected top level module!");
421 
422   // A top-level module might not be actually imported as a module when
423   // -fmodule-name is used to compile a translation unit that imports this
424   // module. In that case it can be skipped. The appropriate header
425   // dependencies will still be reported as expected.
426   if (!M->getASTFile())
427     return {};
428 
429   // If this module has been handled already, just return its ID.
430   auto ModI = MDC.ModularDeps.insert({M, nullptr});
431   if (!ModI.second)
432     return ModI.first->second->ID;
433 
434   ModI.first->second = std::make_unique<ModuleDeps>();
435   ModuleDeps &MD = *ModI.first->second;
436 
437   MD.ID.ModuleName = M->getFullModuleName();
438   MD.ImportedByMainFile = DirectModularDeps.contains(M);
439   MD.IsSystem = M->IsSystem;
440 
441   ModuleMap &ModMapInfo =
442       MDC.ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
443 
444   OptionalFileEntryRef ModuleMap = ModMapInfo.getModuleMapFileForUniquing(M);
445 
446   if (ModuleMap) {
447     SmallString<128> Path = ModuleMap->getNameAsRequested();
448     ModMapInfo.canonicalizeModuleMapPath(Path);
449     MD.ClangModuleMapFile = std::string(Path);
450   }
451 
452   serialization::ModuleFile *MF =
453       MDC.ScanInstance.getASTReader()->getModuleManager().lookup(
454           M->getASTFile());
455   MDC.ScanInstance.getASTReader()->visitInputFiles(
456       *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
457         // __inferred_module.map is the result of the way in which an implicit
458         // module build handles inferred modules. It adds an overlay VFS with
459         // this file in the proper directory and relies on the rest of Clang to
460         // handle it like normal. With explicitly built modules we don't need
461         // to play VFS tricks, so replace it with the correct module map.
462         if (IF.getFile()->getName().endswith("__inferred_module.map")) {
463           MDC.addFileDep(MD, ModuleMap->getName());
464           return;
465         }
466         MDC.addFileDep(MD, IF.getFile()->getName());
467       });
468 
469   llvm::DenseSet<const Module *> SeenDeps;
470   addAllSubmodulePrebuiltDeps(M, MD, SeenDeps);
471   addAllSubmoduleDeps(M, MD, SeenDeps);
472   addAllAffectingClangModules(M, MD, SeenDeps);
473 
474   MDC.ScanInstance.getASTReader()->visitTopLevelModuleMaps(
475       *MF, [&](FileEntryRef FE) {
476         if (FE.getNameAsRequested().endswith("__inferred_module.map"))
477           return;
478         MD.ModuleMapFileDeps.emplace_back(FE.getNameAsRequested());
479       });
480 
481   CompilerInvocation CI = MDC.makeInvocationForModuleBuildWithoutOutputs(
482       MD, [&](CompilerInvocation &BuildInvocation) {
483         if (MDC.OptimizeArgs)
484           optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(),
485                                    *MDC.ScanInstance.getASTReader(), *MF);
486       });
487 
488   MDC.associateWithContextHash(CI, MD);
489 
490   // Finish the compiler invocation. Requires dependencies and the context hash.
491   MDC.addOutputPaths(CI, MD);
492 
493   MD.BuildArguments = CI.getCC1CommandLine();
494 
495   return MD.ID;
496 }
497 
498 static void forEachSubmoduleSorted(const Module *M,
499                                    llvm::function_ref<void(const Module *)> F) {
500   // Submodule order depends on order of header includes for inferred submodules
501   // we don't care about the exact order, so sort so that it's consistent across
502   // TUs to improve sharing.
503   SmallVector<const Module *> Submodules(M->submodules());
504   llvm::stable_sort(Submodules, [](const Module *A, const Module *B) {
505     return A->Name < B->Name;
506   });
507   for (const Module *SubM : Submodules)
508     F(SubM);
509 }
510 
511 void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps(
512     const Module *M, ModuleDeps &MD,
513     llvm::DenseSet<const Module *> &SeenSubmodules) {
514   addModulePrebuiltDeps(M, MD, SeenSubmodules);
515 
516   forEachSubmoduleSorted(M, [&](const Module *SubM) {
517     addAllSubmodulePrebuiltDeps(SubM, MD, SeenSubmodules);
518   });
519 }
520 
521 void ModuleDepCollectorPP::addModulePrebuiltDeps(
522     const Module *M, ModuleDeps &MD,
523     llvm::DenseSet<const Module *> &SeenSubmodules) {
524   for (const Module *Import : M->Imports)
525     if (Import->getTopLevelModule() != M->getTopLevelModule())
526       if (MDC.isPrebuiltModule(Import->getTopLevelModule()))
527         if (SeenSubmodules.insert(Import->getTopLevelModule()).second)
528           MD.PrebuiltModuleDeps.emplace_back(Import->getTopLevelModule());
529 }
530 
531 void ModuleDepCollectorPP::addAllSubmoduleDeps(
532     const Module *M, ModuleDeps &MD,
533     llvm::DenseSet<const Module *> &AddedModules) {
534   addModuleDep(M, MD, AddedModules);
535 
536   forEachSubmoduleSorted(M, [&](const Module *SubM) {
537     addAllSubmoduleDeps(SubM, MD, AddedModules);
538   });
539 }
540 
541 void ModuleDepCollectorPP::addModuleDep(
542     const Module *M, ModuleDeps &MD,
543     llvm::DenseSet<const Module *> &AddedModules) {
544   for (const Module *Import : M->Imports) {
545     if (Import->getTopLevelModule() != M->getTopLevelModule() &&
546         !MDC.isPrebuiltModule(Import)) {
547       if (auto ImportID = handleTopLevelModule(Import->getTopLevelModule()))
548         if (AddedModules.insert(Import->getTopLevelModule()).second)
549           MD.ClangModuleDeps.push_back(*ImportID);
550     }
551   }
552 }
553 
554 void ModuleDepCollectorPP::addAllAffectingClangModules(
555     const Module *M, ModuleDeps &MD,
556     llvm::DenseSet<const Module *> &AddedModules) {
557   addAffectingClangModule(M, MD, AddedModules);
558 
559   for (const Module *SubM : M->submodules())
560     addAllAffectingClangModules(SubM, MD, AddedModules);
561 }
562 
563 void ModuleDepCollectorPP::addAffectingClangModule(
564     const Module *M, ModuleDeps &MD,
565     llvm::DenseSet<const Module *> &AddedModules) {
566   for (const Module *Affecting : M->AffectingClangModules) {
567     assert(Affecting == Affecting->getTopLevelModule() &&
568            "Not quite import not top-level module");
569     if (Affecting != M->getTopLevelModule() &&
570         !MDC.isPrebuiltModule(Affecting)) {
571       if (auto ImportID = handleTopLevelModule(Affecting))
572         if (AddedModules.insert(Affecting).second)
573           MD.ClangModuleDeps.push_back(*ImportID);
574     }
575   }
576 }
577 
578 ModuleDepCollector::ModuleDepCollector(
579     std::unique_ptr<DependencyOutputOptions> Opts,
580     CompilerInstance &ScanInstance, DependencyConsumer &C,
581     DependencyActionController &Controller, CompilerInvocation OriginalCI,
582     bool OptimizeArgs, bool EagerLoadModules, bool IsStdModuleP1689Format)
583     : ScanInstance(ScanInstance), Consumer(C), Controller(Controller),
584       Opts(std::move(Opts)), OriginalInvocation(std::move(OriginalCI)),
585       OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),
586       IsStdModuleP1689Format(IsStdModuleP1689Format) {}
587 
588 void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
589   PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(*this));
590 }
591 
592 void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
593 
594 bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
595   std::string Name(M->getTopLevelModuleName());
596   const auto &PrebuiltModuleFiles =
597       ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;
598   auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name);
599   if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
600     return false;
601   assert("Prebuilt module came from the expected AST file" &&
602          PrebuiltModuleFileIt->second == M->getASTFile()->getName());
603   return true;
604 }
605 
606 static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
607                                           SmallVectorImpl<char> &Storage) {
608   if (llvm::sys::path::is_absolute(Path) &&
609       !llvm::sys::path::is_style_windows(llvm::sys::path::Style::native))
610     return Path;
611   Storage.assign(Path.begin(), Path.end());
612   CI.getFileManager().makeAbsolutePath(Storage);
613   llvm::sys::path::make_preferred(Storage);
614   return StringRef(Storage.data(), Storage.size());
615 }
616 
617 void ModuleDepCollector::addFileDep(StringRef Path) {
618   llvm::SmallString<256> Storage;
619   Path = makeAbsoluteAndPreferred(ScanInstance, Path, Storage);
620   FileDeps.push_back(std::string(Path));
621 }
622 
623 void ModuleDepCollector::addFileDep(ModuleDeps &MD, StringRef Path) {
624   llvm::SmallString<256> Storage;
625   Path = makeAbsoluteAndPreferred(ScanInstance, Path, Storage);
626   MD.FileDeps.insert(Path);
627 }
628