[llvm-branch-commits] [clang] [clang-tools-extra] [clang][DependencyScanning] Dissolve DependencyScannerImpl.h/cpp (PR #211409)

Qiongsi Wu via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jul 22 15:46:55 PDT 2026


https://github.com/qiongsiwu created https://github.com/llvm/llvm-project/pull/211409

`DependencyScannerImpl.h/cpp` were created to share code between TU scanning and by-name scanning when they were on different code paths. After unifying the implementation through `CompilerInstanceWithContext`, we no longer need a separate header/cpp pair and all the code are moved into `DependencyScanningWorker.cpp`. This PR is mechnical and does not intend to introduce behavivor changes. 

---

<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>

>From 05ba93ad0efbab46bc91250881b8a802590ad195 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qiongsi_wu at apple.com>
Date: Wed, 22 Jul 2026 13:59:13 -0700
Subject: [PATCH] Move implementation details to DependencyScanningWorker.cpp
 and dissolve DependencyScannerImpl.h/cpp.

---
 clang-tools-extra/clangd/ProjectModules.cpp   |   1 +
 .../DependencyScannerImpl.h                   | 122 ----
 .../DependencyScanningUtils.h                 |   1 -
 .../DependencyScanningWorker.h                |  14 +-
 .../clang/Tooling/DependencyScanningTool.h    |   1 -
 clang/lib/DependencyScanning/CMakeLists.txt   |   1 -
 .../DependencyScannerImpl.cpp                 | 678 ------------------
 .../DependencyScanningWorker.cpp              | 672 ++++++++++++++++-
 clang/lib/Tooling/DependencyScanningTool.cpp  |   1 -
 9 files changed, 684 insertions(+), 807 deletions(-)
 delete mode 100644 clang/include/clang/DependencyScanning/DependencyScannerImpl.h
 delete mode 100644 clang/lib/DependencyScanning/DependencyScannerImpl.cpp

diff --git a/clang-tools-extra/clangd/ProjectModules.cpp b/clang-tools-extra/clangd/ProjectModules.cpp
index 33cbc715e9659..4ea38f4177fe9 100644
--- a/clang-tools-extra/clangd/ProjectModules.cpp
+++ b/clang-tools-extra/clangd/ProjectModules.cpp
@@ -10,6 +10,7 @@
 #include "Compiler.h"
 #include "support/Logger.h"
 #include "clang/DependencyScanning/DependencyScanningService.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Tooling/DependencyScanningTool.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/SmallString.h"
diff --git a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
deleted file mode 100644
index 6c77dd936a092..0000000000000
--- a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
+++ /dev/null
@@ -1,122 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_DEPENDENCYSCANNING_DEPENDENCYSCANNERIMPL_H
-#define LLVM_CLANG_DEPENDENCYSCANNING_DEPENDENCYSCANNERIMPL_H
-
-#include "clang/DependencyScanning/DependencyScanningFilesystem.h"
-#include "clang/DependencyScanning/ModuleDepCollector.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/CompilerInvocation.h"
-#include "clang/Frontend/TextDiagnosticPrinter.h"
-#include "llvm/Support/VirtualFileSystem.h"
-#include <mutex>
-#include <thread>
-
-namespace clang {
-class DiagnosticConsumer;
-
-namespace dependencies {
-class DependencyScanningService;
-class DependencyScanningWorker;
-
-class DependencyConsumer;
-class DependencyActionController;
-class DependencyScanningWorkerFilesystem;
-
-// Helper functions and data types.
-std::unique_ptr<DiagnosticOptions>
-createDiagOptions(ArrayRef<std::string> CommandLine);
-
-struct DiagnosticsEngineWithDiagOpts {
-  // We need to bound the lifetime of the DiagOpts used to create the
-  // DiganosticsEngine with the DiagnosticsEngine itself.
-  std::unique_ptr<DiagnosticOptions> DiagOpts;
-  IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine;
-
-  DiagnosticsEngineWithDiagOpts(ArrayRef<std::string> CommandLine,
-                                IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
-                                DiagnosticConsumer &DC);
-};
-
-std::unique_ptr<CompilerInvocation>
-createCompilerInvocation(ArrayRef<std::string> CommandLine,
-                         DiagnosticsEngine &Diags);
-
-/// Canonicalizes command-line macro defines (e.g. removing "-DX -UX").
-void canonicalizeDefines(PreprocessorOptions &PPOpts);
-
-/// Creates a CompilerInvocation suitable for the dependency scanner.
-std::shared_ptr<CompilerInvocation>
-createScanCompilerInvocation(const CompilerInvocation &Invocation,
-                             const DependencyScanningService &Service,
-                             DependencyActionController &Controller);
-
-/// Creates dependency output options to be reported to the dependency consumer,
-/// deducing missing information if necessary.
-std::unique_ptr<DependencyOutputOptions>
-createDependencyOutputOptions(const CompilerInvocation &Invocation);
-
-void initializeScanCompilerInstance(
-    CompilerInstance &ScanInstance,
-    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
-    DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
-    IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS);
-
-SmallVector<StringRef>
-getInitialStableDirs(const CompilerInstance &ScanInstance);
-
-std::optional<PrebuiltModulesAttrsMap>
-computePrebuiltModulesASTMap(CompilerInstance &ScanInstance,
-                             SmallVector<StringRef> &StableDirs);
-
-/// Create the dependency collector that will collect the produced
-/// dependencies. May return the created ModuleDepCollector depending
-/// on the scanning format.
-std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector(
-    CompilerInstance &ScanInstance,
-    std::unique_ptr<DependencyOutputOptions> DepOutputOpts,
-    DependencyScanningService &Service, CompilerInvocation &Inv,
-    DependencyActionController &Controller,
-    PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-    SmallVector<StringRef> &StableDirs);
-
-/// Manages (and terminates) the asynchronous compilation of modules.
-class AsyncModuleCompiles {
-  std::mutex Mutex;
-  bool Stop = false;
-  // FIXME: Have the service own a thread pool and use that instead.
-  std::vector<std::thread> Compiles;
-
-public:
-  /// Registers the module compilation, unless this instance is about to be
-  /// destroyed.
-  void add(llvm::unique_function<void()> Compile) {
-    std::lock_guard<std::mutex> Lock(Mutex);
-    if (!Stop)
-      Compiles.emplace_back(std::move(Compile));
-  }
-
-  ~AsyncModuleCompiles() {
-    {
-      std::lock_guard<std::mutex> Lock(Mutex);
-      Stop = true;
-    }
-    for (std::thread &Compile : Compiles)
-      Compile.join();
-  }
-};
-
-void runTUModulePrescan(CompilerInstance &PrescanCI,
-                        DependencyScanningService &Service,
-                        DependencyActionController &Controller,
-                        AsyncModuleCompiles &Compiles);
-} // namespace dependencies
-} // namespace clang
-
-#endif // LLVM_CLANG_DEPENDENCYSCANNING_DEPENDENCYSCANNERIMPL_H
diff --git a/clang/include/clang/DependencyScanning/DependencyScanningUtils.h b/clang/include/clang/DependencyScanning/DependencyScanningUtils.h
index 33f7216762b03..952342d2a322d 100644
--- a/clang/include/clang/DependencyScanning/DependencyScanningUtils.h
+++ b/clang/include/clang/DependencyScanning/DependencyScanningUtils.h
@@ -11,7 +11,6 @@
 
 #include "clang/DependencyScanning/DependencyActionController.h"
 #include "clang/DependencyScanning/DependencyConsumer.h"
-#include "clang/DependencyScanning/DependencyScannerImpl.h"
 #include "clang/DependencyScanning/DependencyScanningWorker.h"
 #include "clang/DependencyScanning/ModuleDepCollector.h"
 #include "llvm/ADT/DenseSet.h"
diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h
index e30e2280f13af..c71ca63cbd650 100644
--- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h
+++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h
@@ -9,11 +9,10 @@
 #ifndef LLVM_CLANG_DEPENDENCYSCANNING_DEPENDENCYSCANNINGWORKER_H
 #define LLVM_CLANG_DEPENDENCYSCANNING_DEPENDENCYSCANNINGWORKER_H
 
-#include "clang/Basic/AtomicLineLogger.h"
+#include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/LLVM.h"
-#include "clang/DependencyScanning/DependencyScannerImpl.h"
 #include "clang/DependencyScanning/DependencyScanningService.h"
 #include "clang/DependencyScanning/ModuleDepCollector.h"
 #include "clang/Frontend/PCHContainerOperations.h"
@@ -33,6 +32,17 @@ namespace dependencies {
 class DependencyConsumer;
 class DependencyScanningWorkerFilesystem;
 
+struct DiagnosticsEngineWithDiagOpts {
+  // We need to bound the lifetime of the DiagOpts used to create the
+  // DiganosticsEngine with the DiagnosticsEngine itself.
+  std::unique_ptr<DiagnosticOptions> DiagOpts;
+  IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine;
+
+  DiagnosticsEngineWithDiagOpts(ArrayRef<std::string> CommandLine,
+                                IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
+                                DiagnosticConsumer &DC);
+};
+
 /// An individual dependency scanning worker that is able to run on its own
 /// thread.
 ///
diff --git a/clang/include/clang/Tooling/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanningTool.h
index 0f1f6fb3d6d0e..ae0619e51de54 100644
--- a/clang/include/clang/Tooling/DependencyScanningTool.h
+++ b/clang/include/clang/Tooling/DependencyScanningTool.h
@@ -9,7 +9,6 @@
 #ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNINGTOOL_H
 #define LLVM_CLANG_TOOLING_DEPENDENCYSCANNINGTOOL_H
 
-#include "clang/DependencyScanning/DependencyScannerImpl.h"
 #include "clang/DependencyScanning/DependencyScanningService.h"
 #include "clang/DependencyScanning/DependencyScanningUtils.h"
 #include "clang/DependencyScanning/DependencyScanningWorker.h"
diff --git a/clang/lib/DependencyScanning/CMakeLists.txt b/clang/lib/DependencyScanning/CMakeLists.txt
index 015c6bcb12326..88eb6408842f9 100644
--- a/clang/lib/DependencyScanning/CMakeLists.txt
+++ b/clang/lib/DependencyScanning/CMakeLists.txt
@@ -11,7 +11,6 @@ add_clang_library(clangDependencyScanning
   DependencyScanningService.cpp
   DependencyScanningWorker.cpp
   DependencyScanningUtils.cpp
-  DependencyScannerImpl.cpp
   InProcessModuleCache.cpp
   ModuleDepCollector.cpp
 
diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
deleted file mode 100644
index 003d0170df41f..0000000000000
--- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
+++ /dev/null
@@ -1,678 +0,0 @@
-//===- DependencyScannerImpl.cpp - Implements module dependency scanning --===//
-//
-// 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 "clang/DependencyScanning/DependencyScannerImpl.h"
-#include "clang/Basic/DiagnosticFrontend.h"
-#include "clang/Basic/DiagnosticSerialization.h"
-#include "clang/DependencyScanning/DependencyActionController.h"
-#include "clang/DependencyScanning/DependencyConsumer.h"
-#include "clang/DependencyScanning/DependencyScanningFilesystem.h"
-#include "clang/DependencyScanning/DependencyScanningService.h"
-#include "clang/DependencyScanning/DependencyScanningWorker.h"
-#include "clang/Frontend/FrontendActions.h"
-#include "llvm/ADT/IntrusiveRefCntPtr.h"
-#include "llvm/ADT/ScopeExit.h"
-#include "llvm/Option/Option.h"
-#include "llvm/Support/AdvisoryLock.h"
-#include "llvm/Support/CrashRecoveryContext.h"
-#include "llvm/Support/VirtualFileSystem.h"
-#include "llvm/TargetParser/Host.h"
-
-#include <mutex>
-#include <thread>
-
-using namespace clang;
-using namespace dependencies;
-
-static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
-                                   const HeaderSearchOptions &ExistingHSOpts,
-                                   DiagnosticsEngine *Diags,
-                                   const LangOptions &LangOpts) {
-  if (LangOpts.Modules) {
-    if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {
-      if (Diags) {
-        Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
-        auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {
-          if (VFSOverlays.empty()) {
-            Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
-          } else {
-            std::string Files = llvm::join(VFSOverlays, "\n");
-            Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;
-          }
-        };
-        VFSNote(0, HSOpts.VFSOverlayFiles);
-        VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
-      }
-    }
-  }
-  return false;
-}
-
-namespace {
-
-using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
-
-/// A listener that collects the imported modules and the input
-/// files. While visiting, collect vfsoverlays and file inputs that determine
-/// whether prebuilt modules fully resolve in stable directories.
-class PrebuiltModuleListener : public ASTReaderListener {
-public:
-  PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
-                         llvm::SmallVector<std::string> &NewModuleFiles,
-                         PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
-                         const HeaderSearchOptions &HSOpts,
-                         const LangOptions &LangOpts, DiagnosticsEngine &Diags,
-                         const ArrayRef<StringRef> StableDirs)
-      : PrebuiltModuleFiles(PrebuiltModuleFiles),
-        NewModuleFiles(NewModuleFiles),
-        PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
-        ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {}
-
-  bool needsImportVisitation() const override { return true; }
-  bool needsInputFileVisitation() override { return true; }
-  bool needsSystemInputFileVisitation() override { return true; }
-
-  /// Accumulate the modules are transitively depended on by the initial
-  /// prebuilt module.
-  void visitImport(StringRef ModuleName, StringRef Filename) override {
-    if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
-      NewModuleFiles.push_back(Filename.str());
-
-    auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(Filename);
-    PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
-    if (PrebuiltMapEntry.second)
-      PrebuiltModule.setInStableDir(!StableDirs.empty());
-
-    if (auto It = PrebuiltModulesASTMap.find(CurrentFile);
-        It != PrebuiltModulesASTMap.end() && CurrentFile != Filename)
-      PrebuiltModule.addDependent(It->getKey());
-  }
-
-  /// For each input file discovered, check whether it's external path is in a
-  /// stable directory. Traversal is stopped if the current module is not
-  /// considered stable.
-  bool visitInputFileAsRequested(StringRef FilenameAsRequested,
-                                 StringRef Filename, bool isSystem,
-                                 bool isOverridden, time_t StoredTime,
-                                 bool isExplicitModule) override {
-    if (StableDirs.empty())
-      return false;
-    auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
-    if ((PrebuiltEntryIt == PrebuiltModulesASTMap.end()) ||
-        (!PrebuiltEntryIt->second.isInStableDir()))
-      return false;
-
-    PrebuiltEntryIt->second.setInStableDir(
-        isPathInStableDir(StableDirs, Filename));
-    return PrebuiltEntryIt->second.isInStableDir();
-  }
-
-  /// Update which module that is being actively traversed.
-  void visitModuleFile(ModuleFileName Filename, serialization::ModuleKind Kind,
-                       bool DirectlyImported) override {
-    // If the CurrentFile is not
-    // considered stable, update any of it's transitive dependents.
-    auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
-    if ((PrebuiltEntryIt != PrebuiltModulesASTMap.end()) &&
-        !PrebuiltEntryIt->second.isInStableDir())
-      PrebuiltEntryIt->second.updateDependentsNotInStableDirs(
-          PrebuiltModulesASTMap);
-    CurrentFile = Filename.str();
-  }
-
-  /// Check the header search options for a given module when considering
-  /// if the module comes from stable directories.
-  bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
-                               StringRef ModuleFilename, StringRef ContextHash,
-                               bool Complain) override {
-
-    auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
-    PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
-    if (PrebuiltMapEntry.second)
-      PrebuiltModule.setInStableDir(!StableDirs.empty());
-
-    if (PrebuiltModule.isInStableDir())
-      PrebuiltModule.setInStableDir(areOptionsInStableDir(StableDirs, HSOpts));
-
-    return false;
-  }
-
-  /// Accumulate vfsoverlays used to build these prebuilt modules.
-  bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
-                             bool Complain) override {
-
-    auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
-    PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
-    if (PrebuiltMapEntry.second)
-      PrebuiltModule.setInStableDir(!StableDirs.empty());
-
-    PrebuiltModule.setVFS(
-        llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles));
-
-    return checkHeaderSearchPaths(
-        HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
-  }
-
-private:
-  PrebuiltModuleFilesT &PrebuiltModuleFiles;
-  llvm::SmallVector<std::string> &NewModuleFiles;
-  PrebuiltModulesAttrsMap &PrebuiltModulesASTMap;
-  const HeaderSearchOptions &ExistingHSOpts;
-  const LangOptions &ExistingLangOpts;
-  DiagnosticsEngine &Diags;
-  std::string CurrentFile;
-  const ArrayRef<StringRef> StableDirs;
-};
-
-/// Visit the given prebuilt module and collect all of the modules it
-/// transitively imports and contributing input files.
-static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
-                                CompilerInstance &CI,
-                                PrebuiltModuleFilesT &ModuleFiles,
-                                PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
-                                DiagnosticsEngine &Diags,
-                                const ArrayRef<StringRef> StableDirs) {
-  // List of module files to be processed.
-  llvm::SmallVector<std::string> Worklist;
-
-  PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap,
-                                  CI.getHeaderSearchOpts(), CI.getLangOpts(),
-                                  Diags, StableDirs);
-
-  Listener.visitModuleFile(ModuleFileName::makeExplicit(PrebuiltModuleFilename),
-                           serialization::MK_ExplicitModule,
-                           /*DirectlyImported=*/true);
-  if (ASTReader::readASTFileControlBlock(
-          PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
-          CI.getPCHContainerReader(),
-          /*FindModuleFileExtensions=*/false, Listener,
-          /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate))
-    return true;
-
-  while (!Worklist.empty()) {
-    // FIXME: This is assuming the PCH only refers to explicitly-built modules,
-    // which technically is not guaranteed. To remove the assumption, we'd need
-    // to also rework how the module files are handled to the scan, specifically
-    // change the values of HeaderSearchOptions::PrebuiltModuleFiles from plain
-    // paths to ModuleFileName.
-    Listener.visitModuleFile(ModuleFileName::makeExplicit(Worklist.back()),
-                             serialization::MK_ExplicitModule,
-                             /*DirectlyImported=*/false);
-    if (ASTReader::readASTFileControlBlock(
-            Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
-            CI.getPCHContainerReader(),
-            /*FindModuleFileExtensions=*/false, Listener,
-            /*ValidateDiagnosticOptions=*/false))
-      return true;
-  }
-  return false;
-}
-
-/// Transform arbitrary file name into an object-like file name.
-static std::string makeObjFileName(StringRef FileName) {
-  SmallString<128> ObjFileName(FileName);
-  llvm::sys::path::replace_extension(ObjFileName, "o");
-  return std::string(ObjFileName);
-}
-
-/// Deduce the dependency target based on the output file and input files.
-static std::string
-deduceDepTarget(const std::string &OutputFile,
-                const SmallVectorImpl<FrontendInputFile> &InputFiles) {
-  if (OutputFile != "-")
-    return OutputFile;
-
-  if (InputFiles.empty() || !InputFiles.front().isFile())
-    return "clang-scan-deps\\ dependency";
-
-  return makeObjFileName(InputFiles.front().getFile());
-}
-
-// Clang implements -D and -U by splatting text into a predefines buffer. This
-// allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
-// define the same macro, or adding C++ style comments before the macro name.
-//
-// This function checks that the first non-space characters in the macro
-// obviously form an identifier that can be uniqued on without lexing. Failing
-// to do this could lead to changing the final definition of a macro.
-//
-// We could set up a preprocessor and actually lex the name, but that's very
-// heavyweight for a situation that will almost never happen in practice.
-static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
-  StringRef Name = Macro.split("=").first.ltrim(" \t");
-  std::size_t I = 0;
-
-  auto FinishName = [&]() -> std::optional<StringRef> {
-    StringRef SimpleName = Name.slice(0, I);
-    if (SimpleName.empty())
-      return std::nullopt;
-    return SimpleName;
-  };
-
-  for (; I != Name.size(); ++I) {
-    switch (Name[I]) {
-    case '(': // Start of macro parameter list
-    case ' ': // End of macro name
-    case '\t':
-      return FinishName();
-    case '_':
-      continue;
-    default:
-      if (llvm::isAlnum(Name[I]))
-        continue;
-      return std::nullopt;
-    }
-  }
-  return FinishName();
-}
-} // namespace
-
-void dependencies::canonicalizeDefines(PreprocessorOptions &PPOpts) {
-  using MacroOpt = std::pair<StringRef, std::size_t>;
-  std::vector<MacroOpt> SimpleNames;
-  SimpleNames.reserve(PPOpts.Macros.size());
-  std::size_t Index = 0;
-  for (const auto &M : PPOpts.Macros) {
-    auto SName = getSimpleMacroName(M.first);
-    // Skip optimizing if we can't guarantee we can preserve relative order.
-    if (!SName)
-      return;
-    SimpleNames.emplace_back(*SName, Index);
-    ++Index;
-  }
-
-  llvm::stable_sort(SimpleNames, llvm::less_first());
-  // Keep the last instance of each macro name by going in reverse
-  auto NewEnd = std::unique(
-      SimpleNames.rbegin(), SimpleNames.rend(),
-      [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });
-  SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
-
-  // Apply permutation.
-  decltype(PPOpts.Macros) NewMacros;
-  NewMacros.reserve(SimpleNames.size());
-  for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {
-    std::size_t OriginalIndex = SimpleNames[I].second;
-    // We still emit undefines here as they may be undefining a predefined macro
-    NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));
-  }
-  std::swap(PPOpts.Macros, NewMacros);
-}
-
-namespace {
-class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter {
-  DependencyScanningWorkerFilesystem *DepFS;
-
-public:
-  ScanningDependencyDirectivesGetter(FileManager &FileMgr) : DepFS(nullptr) {
-    FileMgr.getVirtualFileSystem().visit([&](llvm::vfs::FileSystem &FS) {
-      auto *DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS);
-      if (DFS) {
-        assert(!DepFS && "Found multiple scanning VFSs");
-        DepFS = DFS;
-      }
-    });
-    assert(DepFS && "Did not find scanning VFS");
-  }
-
-  std::unique_ptr<DependencyDirectivesGetter>
-  cloneFor(FileManager &FileMgr) override {
-    return std::make_unique<ScanningDependencyDirectivesGetter>(FileMgr);
-  }
-
-  std::optional<ArrayRef<dependency_directives_scan::Directive>>
-  operator()(FileEntryRef File) override {
-    return DepFS->getDirectiveTokens(File.getName());
-  }
-};
-
-/// Sanitize diagnostic options for dependency scan.
-void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
-  // Don't print 'X warnings and Y errors generated'.
-  DiagOpts.ShowCarets = false;
-  // Don't write out diagnostic file.
-  DiagOpts.DiagnosticSerializationFile.clear();
-  // Don't emit warnings except for scanning specific warnings.
-  // TODO: It would be useful to add a more principled way to ignore all
-  //       warnings that come from source code. The issue is that we need to
-  //       ignore warnings that could be surpressed by
-  //       `#pragma clang diagnostic`, while still allowing some scanning
-  //       warnings for things we're not ready to turn into errors yet.
-  //       See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.
-  llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {
-    return llvm::StringSwitch<bool>(Warning)
-        .Cases({"pch-vfs-diff", "error=pch-vfs-diff"}, false)
-        .StartsWith("no-error=", false)
-        .Default(true);
-  });
-}
-} // namespace
-
-std::unique_ptr<DiagnosticOptions>
-dependencies::createDiagOptions(ArrayRef<std::string> CommandLine) {
-  std::vector<const char *> CLI;
-  for (const std::string &Arg : CommandLine)
-    CLI.push_back(Arg.c_str());
-  auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
-  sanitizeDiagOpts(*DiagOpts);
-  return DiagOpts;
-}
-
-DiagnosticsEngineWithDiagOpts::DiagnosticsEngineWithDiagOpts(
-    ArrayRef<std::string> CommandLine,
-    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC) {
-  std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
-  llvm::transform(CommandLine, CCommandLine.begin(),
-                  [](const std::string &Str) { return Str.c_str(); });
-  DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
-  sanitizeDiagOpts(*DiagOpts);
-  DiagEngine = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DC,
-                                                   /*ShouldOwnClient=*/false);
-}
-
-std::unique_ptr<CompilerInvocation>
-dependencies::createCompilerInvocation(ArrayRef<std::string> CommandLine,
-                                       DiagnosticsEngine &Diags) {
-  llvm::opt::ArgStringList Argv;
-  for (const std::string &Str : ArrayRef(CommandLine).drop_front())
-    Argv.push_back(Str.c_str());
-
-  auto Invocation = std::make_unique<CompilerInvocation>();
-  if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) {
-    // FIXME: Should we just go on like cc1_main does?
-    return nullptr;
-  }
-  return Invocation;
-}
-
-void dependencies::initializeScanCompilerInstance(
-    CompilerInstance &ScanInstance,
-    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
-    DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
-    IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS) {
-  ScanInstance.setBuildingModule(false);
-  ScanInstance.createVirtualFileSystem(FS, DiagConsumer);
-  ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
-  if (!Service.getOpts().EmitWarnings)
-    ScanInstance.getDiagnostics().setIgnoreAllWarnings(true);
-  ScanInstance.createFileManager();
-  ScanInstance.createSourceManager();
-
-  // Use DepFS for getting the dependency directives if requested to do so.
-  if (Service.getOpts().Mode == ScanningMode::DependencyDirectivesScan)
-    ScanInstance.setDependencyDirectivesGetter(
-        std::make_unique<ScanningDependencyDirectivesGetter>(
-            ScanInstance.getFileManager()));
-}
-
-std::shared_ptr<CompilerInvocation> dependencies::createScanCompilerInvocation(
-    const CompilerInvocation &Invocation,
-    const DependencyScanningService &Service,
-    DependencyActionController &Controller) {
-  auto ScanInvocation = std::make_shared<CompilerInvocation>(Invocation);
-
-  sanitizeDiagOpts(ScanInvocation->getDiagnosticOpts());
-
-  ScanInvocation->getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
-      true;
-
-  if (ScanInvocation->getHeaderSearchOpts().ModulesValidateOncePerBuildSession)
-    ScanInvocation->getHeaderSearchOpts().BuildSessionTimestamp =
-        Service.getOpts().BuildSessionTimestamp;
-
-  ScanInvocation->getFrontendOpts().DisableFree = false;
-  ScanInvocation->getFrontendOpts().GenerateGlobalModuleIndex = false;
-  ScanInvocation->getFrontendOpts().UseGlobalModuleIndex = false;
-  ScanInvocation->getFrontendOpts().GenReducedBMI = false;
-  ScanInvocation->getFrontendOpts().ModuleOutputPath.clear();
-  // This will prevent us compiling individual modules asynchronously since
-  // FileManager is not thread-safe, but it does improve performance for now.
-  ScanInvocation->getFrontendOpts().ModulesShareFileManager = true;
-  ScanInvocation->getHeaderSearchOpts().ModuleFormat = "raw";
-  ScanInvocation->getHeaderSearchOpts().ModulesIncludeVFSUsage =
-      any(Service.getOpts().OptimizeArgs & ScanningOptimizations::VFS);
-
-  // Consider different header search and diagnostic options to create
-  // different modules. This avoids the unsound aliasing of module PCMs.
-  //
-  // TODO: Implement diagnostic bucketing to reduce the impact of strict
-  // context hashing.
-  ScanInvocation->getHeaderSearchOpts().ModulesStrictContextHash = true;
-  ScanInvocation->getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true;
-  ScanInvocation->getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
-  ScanInvocation->getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
-  ScanInvocation->getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings =
-      true;
-  ScanInvocation->getHeaderSearchOpts().ModulesForceValidateUserHeaders = false;
-
-  // FIXME: Do this even with PCHs by marking the option as something like
-  // "preprocessor benign" in LangOptions.def so that it passes the
-  // compatibility checks in ASTReader.
-  if (ScanInvocation->getPreprocessorOpts().ImplicitPCHInclude.empty()) {
-    // Application extension only affects the handling of availability
-    // attributes, which cannot change the dependencies.
-    ScanInvocation->getLangOpts().AppExt = false;
-  }
-
-  // Ensure that the scanner does not create new dependency collectors,
-  // and thus won't write out the extra '.d' files to disk.
-  ScanInvocation->getDependencyOutputOpts() = {};
-
-  Controller.initializeScanInvocation(*ScanInvocation);
-
-  return ScanInvocation;
-}
-
-llvm::SmallVector<StringRef>
-dependencies::getInitialStableDirs(const CompilerInstance &ScanInstance) {
-  // Create a collection of stable directories derived from the ScanInstance
-  // for determining whether module dependencies would fully resolve from
-  // those directories.
-  llvm::SmallVector<StringRef> StableDirs;
-  const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
-  if (!Sysroot.empty() && (llvm::sys::path::root_directory(Sysroot) != Sysroot))
-    StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir};
-  return StableDirs;
-}
-
-std::optional<PrebuiltModulesAttrsMap>
-dependencies::computePrebuiltModulesASTMap(
-    CompilerInstance &ScanInstance, llvm::SmallVector<StringRef> &StableDirs) {
-  // Store a mapping of prebuilt module files and their properties like header
-  // search options. This will prevent the implicit build to create duplicate
-  // modules and will force reuse of the existing prebuilt module files
-  // instead.
-  PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
-
-  if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
-    if (visitPrebuiltModule(
-            ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance,
-            ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
-            PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs))
-      return {};
-
-  return PrebuiltModulesASTMap;
-}
-
-std::unique_ptr<DependencyOutputOptions>
-dependencies::createDependencyOutputOptions(
-    const CompilerInvocation &Invocation) {
-  auto Opts = std::make_unique<DependencyOutputOptions>(
-      Invocation.getDependencyOutputOpts());
-  // We need at least one -MT equivalent for the generator of make dependency
-  // files to work.
-  if (Opts->Targets.empty())
-    Opts->Targets = {deduceDepTarget(Invocation.getFrontendOpts().OutputFile,
-                                     Invocation.getFrontendOpts().Inputs)};
-  Opts->IncludeSystemHeaders = true;
-
-  return Opts;
-}
-
-std::shared_ptr<ModuleDepCollector>
-dependencies::initializeScanInstanceDependencyCollector(
-    CompilerInstance &ScanInstance,
-    std::unique_ptr<DependencyOutputOptions> DepOutputOpts,
-    DependencyScanningService &Service, CompilerInvocation &Inv,
-    DependencyActionController &Controller,
-    PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
-    SmallVector<StringRef> &StableDirs) {
-  auto MDC = std::make_shared<ModuleDepCollector>(
-      Service, std::move(DepOutputOpts), ScanInstance, Controller, Inv,
-      std::move(PrebuiltModulesASTMap), StableDirs);
-  ScanInstance.addDependencyCollector(MDC);
-  return MDC;
-}
-
-struct SingleModuleWithAsyncModuleCompiles : PreprocessOnlyAction {
-  DependencyScanningService &Service;
-  DependencyActionController &Controller;
-  AsyncModuleCompiles &Compiles;
-
-  SingleModuleWithAsyncModuleCompiles(DependencyScanningService &Service,
-                                      DependencyActionController &Controller,
-                                      AsyncModuleCompiles &Compiles)
-      : Service(Service), Controller(Controller), Compiles(Compiles) {}
-
-  bool BeginSourceFileAction(CompilerInstance &CI) override;
-};
-
-/// The preprocessor callback that takes care of initiating an asynchronous
-/// module compilation if needed.
-struct AsyncModuleCompile : PPCallbacks {
-  CompilerInstance &CI;
-  DependencyScanningService &Service;
-  DependencyActionController &Controller;
-  AsyncModuleCompiles &Compiles;
-
-  AsyncModuleCompile(CompilerInstance &CI, DependencyScanningService &Service,
-                     DependencyActionController &Controller,
-                     AsyncModuleCompiles &Compiles)
-      : CI(CI), Service(Service), Controller(Controller), Compiles(Compiles) {}
-
-  void moduleLoadSkipped(Module *M) override {
-    M = M->getTopLevelModule();
-
-    HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
-    ModuleCache &ModCache = CI.getModuleCache();
-    ModuleFileName ModuleFileName = HS.getCachedModuleFileName(M);
-
-    uint64_t Timestamp = ModCache.getModuleTimestamp(ModuleFileName);
-    // Someone else already built/validated the PCM.
-    if (Timestamp > CI.getHeaderSearchOpts().BuildSessionTimestamp)
-      return;
-
-    if (!CI.getASTReader())
-      CI.createASTReader();
-    SmallVector<ASTReader::ImportedModule, 0> Imported;
-    // Only calling ReadASTCore() to avoid the expensive eager deserialization
-    // of the clang::Module objects in ReadAST().
-    // FIXME: Consider doing this in the new thread depending on how expensive
-    // the read turns out to be.
-    switch (CI.getASTReader()->ReadASTCore(
-        ModuleFileName, serialization::MK_ImplicitModule, SourceLocation(),
-        nullptr, Imported, {}, {}, {},
-        ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing |
-            ASTReader::ARR_TreatModuleWithErrorsAsOutOfDate)) {
-    case ASTReader::Success:
-      // We successfully read a valid, up-to-date PCM.
-      // FIXME: This could update the timestamp. Regular calls to
-      // ASTReader::ReadAST() would do so unless they encountered corrupted
-      // AST block, corrupted extension block, or did not read the expected
-      // top-level module.
-      return;
-    case ASTReader::OutOfDate:
-    case ASTReader::Missing:
-      // The most interesting case.
-      break;
-    default:
-      // Let the regular scan diagnose this.
-      return;
-    }
-
-    auto Lock = ModCache.getLock(ModuleFileName);
-    bool Owned;
-    llvm::Error LockErr = Lock->tryLock().moveInto(Owned);
-    // Someone else is building the PCM right now.
-    if (!LockErr && !Owned)
-      return;
-    // We should build the PCM.
-    IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
-        llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
-            Service, Service.getOpts().MakeVFS());
-    VFS = createVFSFromCompilerInvocation(CI.getInvocation(),
-                                          CI.getDiagnostics(), std::move(VFS));
-    auto DC = std::make_unique<DiagnosticConsumer>();
-    auto MC = makeInProcessModuleCache(Service.getModuleCacheEntries(),
-                                       Service.getLogger());
-    CompilerInstance::ThreadSafeCloneConfig CloneConfig(std::move(VFS), *DC,
-                                                        std::move(MC));
-    auto ModCI1 = CI.cloneForModuleCompile(SourceLocation(), M, ModuleFileName,
-                                           CloneConfig);
-    auto ModCI2 = CI.cloneForModuleCompile(SourceLocation(), M, ModuleFileName,
-                                           CloneConfig);
-
-    auto ModController = Controller.clone();
-
-    // Note: This lock belongs to a module cache that might not outlive the
-    // thread. This works, because the in-process lock only refers to an object
-    // managed by the service, which does outlive the thread.
-    Compiles.add([Lock = std::move(Lock), ModCI1 = std::move(ModCI1),
-                  ModCI2 = std::move(ModCI2), DC = std::move(DC),
-                  ModController = std::move(ModController), Service = &Service,
-                  Compiles = &Compiles] {
-      llvm::CrashRecoveryContext CRC;
-      (void)CRC.RunSafely([&] {
-        // Quickly discovers and compiles modules for the real scan below.
-        SingleModuleWithAsyncModuleCompiles Action1(*Service, *ModController,
-                                                    *Compiles);
-        (void)ModCI1->ExecuteAction(Action1);
-        // The real scan below.
-        ModCI2->getPreprocessorOpts().SingleModuleParseMode = false;
-        GenerateModuleFromModuleMapAction Action2;
-        (void)ModCI2->ExecuteAction(Action2);
-      });
-    });
-  }
-};
-
-/// Runs the preprocessor on a TU with single-module-parse-mode and compiles
-/// modules asynchronously without blocking or importing them.
-struct SingleTUWithAsyncModuleCompiles : PreprocessOnlyAction {
-  DependencyScanningService &Service;
-  DependencyActionController &Controller;
-  AsyncModuleCompiles &Compiles;
-
-  SingleTUWithAsyncModuleCompiles(DependencyScanningService &Service,
-                                  DependencyActionController &Controller,
-                                  AsyncModuleCompiles &Compiles)
-      : Service(Service), Controller(Controller), Compiles(Compiles) {}
-
-  bool BeginSourceFileAction(CompilerInstance &CI) override {
-    CI.getInvocation().getPreprocessorOpts().SingleModuleParseMode = true;
-    CI.getPreprocessor().addPPCallbacks(std::make_unique<AsyncModuleCompile>(
-        CI, Service, Controller, Compiles));
-    return true;
-  }
-};
-
-bool SingleModuleWithAsyncModuleCompiles::BeginSourceFileAction(
-    CompilerInstance &CI) {
-  CI.getInvocation().getPreprocessorOpts().SingleModuleParseMode = true;
-  CI.getPreprocessor().addPPCallbacks(
-      std::make_unique<AsyncModuleCompile>(CI, Service, Controller, Compiles));
-  return true;
-}
-
-void dependencies::runTUModulePrescan(CompilerInstance &PrescanCI,
-                                      DependencyScanningService &Service,
-                                      DependencyActionController &Controller,
-                                      AsyncModuleCompiles &Compiles) {
-  SingleTUWithAsyncModuleCompiles Action(Service, Controller, Compiles);
-  (void)PrescanCI.ExecuteAction(Action);
-}
diff --git a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
index 488f338e0d541..481c91f1d37ca 100644
--- a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
@@ -9,21 +9,691 @@
 #include "clang/DependencyScanning/DependencyScanningWorker.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Basic/DiagnosticSerialization.h"
 #include "clang/DependencyScanning/DependencyActionController.h"
 #include "clang/DependencyScanning/DependencyConsumer.h"
-#include "clang/DependencyScanning/DependencyScannerImpl.h"
+#include "clang/DependencyScanning/DependencyScanningFilesystem.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/SemaOpenACC.h"
 #include "clang/Serialization/ObjectFilePCHContainerReader.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/ScopeExit.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/AdvisoryLock.h"
+#include "llvm/Support/CrashRecoveryContext.h"
 #include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/TargetParser/Host.h"
+#include <mutex>
+#include <thread>
 
 using namespace clang;
 using namespace dependencies;
 
+namespace {
+bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
+                            const HeaderSearchOptions &ExistingHSOpts,
+                            DiagnosticsEngine *Diags,
+                            const LangOptions &LangOpts) {
+  if (LangOpts.Modules) {
+    if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {
+      if (Diags) {
+        Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
+        auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {
+          if (VFSOverlays.empty()) {
+            Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
+          } else {
+            std::string Files = llvm::join(VFSOverlays, "\n");
+            Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;
+          }
+        };
+        VFSNote(0, HSOpts.VFSOverlayFiles);
+        VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
+      }
+    }
+  }
+  return false;
+}
+
+using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
+
+/// A listener that collects the imported modules and the input
+/// files. While visiting, collect vfsoverlays and file inputs that determine
+/// whether prebuilt modules fully resolve in stable directories.
+class PrebuiltModuleListener : public ASTReaderListener {
+public:
+  PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
+                         llvm::SmallVector<std::string> &NewModuleFiles,
+                         PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
+                         const HeaderSearchOptions &HSOpts,
+                         const LangOptions &LangOpts, DiagnosticsEngine &Diags,
+                         const ArrayRef<StringRef> StableDirs)
+      : PrebuiltModuleFiles(PrebuiltModuleFiles),
+        NewModuleFiles(NewModuleFiles),
+        PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
+        ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {}
+
+  bool needsImportVisitation() const override { return true; }
+  bool needsInputFileVisitation() override { return true; }
+  bool needsSystemInputFileVisitation() override { return true; }
+
+  /// Accumulate the modules are transitively depended on by the initial
+  /// prebuilt module.
+  void visitImport(StringRef ModuleName, StringRef Filename) override {
+    if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
+      NewModuleFiles.push_back(Filename.str());
+
+    auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(Filename);
+    PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
+    if (PrebuiltMapEntry.second)
+      PrebuiltModule.setInStableDir(!StableDirs.empty());
+
+    if (auto It = PrebuiltModulesASTMap.find(CurrentFile);
+        It != PrebuiltModulesASTMap.end() && CurrentFile != Filename)
+      PrebuiltModule.addDependent(It->getKey());
+  }
+
+  /// For each input file discovered, check whether it's external path is in a
+  /// stable directory. Traversal is stopped if the current module is not
+  /// considered stable.
+  bool visitInputFileAsRequested(StringRef FilenameAsRequested,
+                                 StringRef Filename, bool isSystem,
+                                 bool isOverridden, time_t StoredTime,
+                                 bool isExplicitModule) override {
+    if (StableDirs.empty())
+      return false;
+    auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
+    if ((PrebuiltEntryIt == PrebuiltModulesASTMap.end()) ||
+        (!PrebuiltEntryIt->second.isInStableDir()))
+      return false;
+
+    PrebuiltEntryIt->second.setInStableDir(
+        isPathInStableDir(StableDirs, Filename));
+    return PrebuiltEntryIt->second.isInStableDir();
+  }
+
+  /// Update which module that is being actively traversed.
+  void visitModuleFile(ModuleFileName Filename, serialization::ModuleKind Kind,
+                       bool DirectlyImported) override {
+    // If the CurrentFile is not
+    // considered stable, update any of it's transitive dependents.
+    auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
+    if ((PrebuiltEntryIt != PrebuiltModulesASTMap.end()) &&
+        !PrebuiltEntryIt->second.isInStableDir())
+      PrebuiltEntryIt->second.updateDependentsNotInStableDirs(
+          PrebuiltModulesASTMap);
+    CurrentFile = Filename.str();
+  }
+
+  /// Check the header search options for a given module when considering
+  /// if the module comes from stable directories.
+  bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
+                               StringRef ModuleFilename, StringRef ContextHash,
+                               bool Complain) override {
+
+    auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
+    PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
+    if (PrebuiltMapEntry.second)
+      PrebuiltModule.setInStableDir(!StableDirs.empty());
+
+    if (PrebuiltModule.isInStableDir())
+      PrebuiltModule.setInStableDir(areOptionsInStableDir(StableDirs, HSOpts));
+
+    return false;
+  }
+
+  /// Accumulate vfsoverlays used to build these prebuilt modules.
+  bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
+                             bool Complain) override {
+
+    auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
+    PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
+    if (PrebuiltMapEntry.second)
+      PrebuiltModule.setInStableDir(!StableDirs.empty());
+
+    PrebuiltModule.setVFS(
+        llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles));
+
+    return checkHeaderSearchPaths(
+        HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
+  }
+
+private:
+  PrebuiltModuleFilesT &PrebuiltModuleFiles;
+  llvm::SmallVector<std::string> &NewModuleFiles;
+  PrebuiltModulesAttrsMap &PrebuiltModulesASTMap;
+  const HeaderSearchOptions &ExistingHSOpts;
+  const LangOptions &ExistingLangOpts;
+  DiagnosticsEngine &Diags;
+  std::string CurrentFile;
+  const ArrayRef<StringRef> StableDirs;
+};
+
+/// Visit the given prebuilt module and collect all of the modules it
+/// transitively imports and contributing input files.
+bool visitPrebuiltModule(StringRef PrebuiltModuleFilename, CompilerInstance &CI,
+                         PrebuiltModuleFilesT &ModuleFiles,
+                         PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
+                         DiagnosticsEngine &Diags,
+                         const ArrayRef<StringRef> StableDirs) {
+  // List of module files to be processed.
+  llvm::SmallVector<std::string> Worklist;
+
+  PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap,
+                                  CI.getHeaderSearchOpts(), CI.getLangOpts(),
+                                  Diags, StableDirs);
+
+  Listener.visitModuleFile(ModuleFileName::makeExplicit(PrebuiltModuleFilename),
+                           serialization::MK_ExplicitModule,
+                           /*DirectlyImported=*/true);
+  if (ASTReader::readASTFileControlBlock(
+          PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
+          CI.getPCHContainerReader(),
+          /*FindModuleFileExtensions=*/false, Listener,
+          /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate))
+    return true;
+
+  while (!Worklist.empty()) {
+    // FIXME: This is assuming the PCH only refers to explicitly-built modules,
+    // which technically is not guaranteed. To remove the assumption, we'd need
+    // to also rework how the module files are handled to the scan, specifically
+    // change the values of HeaderSearchOptions::PrebuiltModuleFiles from plain
+    // paths to ModuleFileName.
+    Listener.visitModuleFile(ModuleFileName::makeExplicit(Worklist.back()),
+                             serialization::MK_ExplicitModule,
+                             /*DirectlyImported=*/false);
+    if (ASTReader::readASTFileControlBlock(
+            Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
+            CI.getPCHContainerReader(),
+            /*FindModuleFileExtensions=*/false, Listener,
+            /*ValidateDiagnosticOptions=*/false))
+      return true;
+  }
+  return false;
+}
+
+/// Transform arbitrary file name into an object-like file name.
+std::string makeObjFileName(StringRef FileName) {
+  SmallString<128> ObjFileName(FileName);
+  llvm::sys::path::replace_extension(ObjFileName, "o");
+  return std::string(ObjFileName);
+}
+
+/// Deduce the dependency target based on the output file and input files.
+std::string
+deduceDepTarget(const std::string &OutputFile,
+                const SmallVectorImpl<FrontendInputFile> &InputFiles) {
+  if (OutputFile != "-")
+    return OutputFile;
+
+  if (InputFiles.empty() || !InputFiles.front().isFile())
+    return "clang-scan-deps\\ dependency";
+
+  return makeObjFileName(InputFiles.front().getFile());
+}
+
+// Clang implements -D and -U by splatting text into a predefines buffer. This
+// allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
+// define the same macro, or adding C++ style comments before the macro name.
+//
+// This function checks that the first non-space characters in the macro
+// obviously form an identifier that can be uniqued on without lexing. Failing
+// to do this could lead to changing the final definition of a macro.
+//
+// We could set up a preprocessor and actually lex the name, but that's very
+// heavyweight for a situation that will almost never happen in practice.
+std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
+  StringRef Name = Macro.split("=").first.ltrim(" \t");
+  std::size_t I = 0;
+
+  auto FinishName = [&]() -> std::optional<StringRef> {
+    StringRef SimpleName = Name.slice(0, I);
+    if (SimpleName.empty())
+      return std::nullopt;
+    return SimpleName;
+  };
+
+  for (; I != Name.size(); ++I) {
+    switch (Name[I]) {
+    case '(': // Start of macro parameter list
+    case ' ': // End of macro name
+    case '\t':
+      return FinishName();
+    case '_':
+      continue;
+    default:
+      if (llvm::isAlnum(Name[I]))
+        continue;
+      return std::nullopt;
+    }
+  }
+  return FinishName();
+}
+
+void canonicalizeDefines(PreprocessorOptions &PPOpts) {
+  using MacroOpt = std::pair<StringRef, std::size_t>;
+  std::vector<MacroOpt> SimpleNames;
+  SimpleNames.reserve(PPOpts.Macros.size());
+  std::size_t Index = 0;
+  for (const auto &M : PPOpts.Macros) {
+    auto SName = getSimpleMacroName(M.first);
+    // Skip optimizing if we can't guarantee we can preserve relative order.
+    if (!SName)
+      return;
+    SimpleNames.emplace_back(*SName, Index);
+    ++Index;
+  }
+
+  llvm::stable_sort(SimpleNames, llvm::less_first());
+  // Keep the last instance of each macro name by going in reverse
+  auto NewEnd = std::unique(
+      SimpleNames.rbegin(), SimpleNames.rend(),
+      [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });
+  SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
+
+  // Apply permutation.
+  decltype(PPOpts.Macros) NewMacros;
+  NewMacros.reserve(SimpleNames.size());
+  for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {
+    std::size_t OriginalIndex = SimpleNames[I].second;
+    // We still emit undefines here as they may be undefining a predefined macro
+    NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));
+  }
+  std::swap(PPOpts.Macros, NewMacros);
+}
+
+class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter {
+  DependencyScanningWorkerFilesystem *DepFS;
+
+public:
+  ScanningDependencyDirectivesGetter(FileManager &FileMgr) : DepFS(nullptr) {
+    FileMgr.getVirtualFileSystem().visit([&](llvm::vfs::FileSystem &FS) {
+      auto *DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS);
+      if (DFS) {
+        assert(!DepFS && "Found multiple scanning VFSs");
+        DepFS = DFS;
+      }
+    });
+    assert(DepFS && "Did not find scanning VFS");
+  }
+
+  std::unique_ptr<DependencyDirectivesGetter>
+  cloneFor(FileManager &FileMgr) override {
+    return std::make_unique<ScanningDependencyDirectivesGetter>(FileMgr);
+  }
+
+  std::optional<ArrayRef<dependency_directives_scan::Directive>>
+  operator()(FileEntryRef File) override {
+    return DepFS->getDirectiveTokens(File.getName());
+  }
+};
+
+/// Sanitize diagnostic options for dependency scan.
+void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
+  // Don't print 'X warnings and Y errors generated'.
+  DiagOpts.ShowCarets = false;
+  // Don't write out diagnostic file.
+  DiagOpts.DiagnosticSerializationFile.clear();
+  // Don't emit warnings except for scanning specific warnings.
+  // TODO: It would be useful to add a more principled way to ignore all
+  //       warnings that come from source code. The issue is that we need to
+  //       ignore warnings that could be surpressed by
+  //       `#pragma clang diagnostic`, while still allowing some scanning
+  //       warnings for things we're not ready to turn into errors yet.
+  //       See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.
+  llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {
+    return llvm::StringSwitch<bool>(Warning)
+        .Cases({"pch-vfs-diff", "error=pch-vfs-diff"}, false)
+        .StartsWith("no-error=", false)
+        .Default(true);
+  });
+}
+
+std::unique_ptr<CompilerInvocation>
+createCompilerInvocation(ArrayRef<std::string> CommandLine,
+                         DiagnosticsEngine &Diags) {
+  llvm::opt::ArgStringList Argv;
+  for (const std::string &Str : ArrayRef(CommandLine).drop_front())
+    Argv.push_back(Str.c_str());
+
+  auto Invocation = std::make_unique<CompilerInvocation>();
+  if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) {
+    // FIXME: Should we just go on like cc1_main does?
+    return nullptr;
+  }
+  return Invocation;
+}
+
+void initializeScanCompilerInstance(
+    CompilerInstance &ScanInstance,
+    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
+    DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
+    IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS) {
+  ScanInstance.setBuildingModule(false);
+  ScanInstance.createVirtualFileSystem(FS, DiagConsumer);
+  ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
+  if (!Service.getOpts().EmitWarnings)
+    ScanInstance.getDiagnostics().setIgnoreAllWarnings(true);
+  ScanInstance.createFileManager();
+  ScanInstance.createSourceManager();
+
+  // Use DepFS for getting the dependency directives if requested to do so.
+  if (Service.getOpts().Mode == ScanningMode::DependencyDirectivesScan)
+    ScanInstance.setDependencyDirectivesGetter(
+        std::make_unique<ScanningDependencyDirectivesGetter>(
+            ScanInstance.getFileManager()));
+}
+
+std::shared_ptr<CompilerInvocation>
+createScanCompilerInvocation(const CompilerInvocation &Invocation,
+                             const DependencyScanningService &Service,
+                             DependencyActionController &Controller) {
+  auto ScanInvocation = std::make_shared<CompilerInvocation>(Invocation);
+
+  sanitizeDiagOpts(ScanInvocation->getDiagnosticOpts());
+
+  ScanInvocation->getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
+      true;
+
+  if (ScanInvocation->getHeaderSearchOpts().ModulesValidateOncePerBuildSession)
+    ScanInvocation->getHeaderSearchOpts().BuildSessionTimestamp =
+        Service.getOpts().BuildSessionTimestamp;
+
+  ScanInvocation->getFrontendOpts().DisableFree = false;
+  ScanInvocation->getFrontendOpts().GenerateGlobalModuleIndex = false;
+  ScanInvocation->getFrontendOpts().UseGlobalModuleIndex = false;
+  ScanInvocation->getFrontendOpts().GenReducedBMI = false;
+  ScanInvocation->getFrontendOpts().ModuleOutputPath.clear();
+  // This will prevent us compiling individual modules asynchronously since
+  // FileManager is not thread-safe, but it does improve performance for now.
+  ScanInvocation->getFrontendOpts().ModulesShareFileManager = true;
+  ScanInvocation->getHeaderSearchOpts().ModuleFormat = "raw";
+  ScanInvocation->getHeaderSearchOpts().ModulesIncludeVFSUsage =
+      any(Service.getOpts().OptimizeArgs & ScanningOptimizations::VFS);
+
+  // Consider different header search and diagnostic options to create
+  // different modules. This avoids the unsound aliasing of module PCMs.
+  //
+  // TODO: Implement diagnostic bucketing to reduce the impact of strict
+  // context hashing.
+  ScanInvocation->getHeaderSearchOpts().ModulesStrictContextHash = true;
+  ScanInvocation->getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true;
+  ScanInvocation->getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
+  ScanInvocation->getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
+  ScanInvocation->getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings =
+      true;
+  ScanInvocation->getHeaderSearchOpts().ModulesForceValidateUserHeaders = false;
+
+  // FIXME: Do this even with PCHs by marking the option as something like
+  // "preprocessor benign" in LangOptions.def so that it passes the
+  // compatibility checks in ASTReader.
+  if (ScanInvocation->getPreprocessorOpts().ImplicitPCHInclude.empty()) {
+    // Application extension only affects the handling of availability
+    // attributes, which cannot change the dependencies.
+    ScanInvocation->getLangOpts().AppExt = false;
+  }
+
+  // Ensure that the scanner does not create new dependency collectors,
+  // and thus won't write out the extra '.d' files to disk.
+  ScanInvocation->getDependencyOutputOpts() = {};
+
+  Controller.initializeScanInvocation(*ScanInvocation);
+
+  return ScanInvocation;
+}
+
+llvm::SmallVector<StringRef>
+getInitialStableDirs(const CompilerInstance &ScanInstance) {
+  // Create a collection of stable directories derived from the ScanInstance
+  // for determining whether module dependencies would fully resolve from
+  // those directories.
+  llvm::SmallVector<StringRef> StableDirs;
+  const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
+  if (!Sysroot.empty() && (llvm::sys::path::root_directory(Sysroot) != Sysroot))
+    StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir};
+  return StableDirs;
+}
+
+std::optional<PrebuiltModulesAttrsMap>
+computePrebuiltModulesASTMap(CompilerInstance &ScanInstance,
+                             llvm::SmallVector<StringRef> &StableDirs) {
+  // Store a mapping of prebuilt module files and their properties like header
+  // search options. This will prevent the implicit build to create duplicate
+  // modules and will force reuse of the existing prebuilt module files
+  // instead.
+  PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
+
+  if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
+    if (visitPrebuiltModule(
+            ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance,
+            ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
+            PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs))
+      return {};
+
+  return PrebuiltModulesASTMap;
+}
+
+std::unique_ptr<DependencyOutputOptions>
+createDependencyOutputOptions(const CompilerInvocation &Invocation) {
+  auto Opts = std::make_unique<DependencyOutputOptions>(
+      Invocation.getDependencyOutputOpts());
+  // We need at least one -MT equivalent for the generator of make dependency
+  // files to work.
+  if (Opts->Targets.empty())
+    Opts->Targets = {deduceDepTarget(Invocation.getFrontendOpts().OutputFile,
+                                     Invocation.getFrontendOpts().Inputs)};
+  Opts->IncludeSystemHeaders = true;
+
+  return Opts;
+}
+
+std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector(
+    CompilerInstance &ScanInstance,
+    std::unique_ptr<DependencyOutputOptions> DepOutputOpts,
+    DependencyScanningService &Service, CompilerInvocation &Inv,
+    DependencyActionController &Controller,
+    PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
+    SmallVector<StringRef> &StableDirs) {
+  auto MDC = std::make_shared<ModuleDepCollector>(
+      Service, std::move(DepOutputOpts), ScanInstance, Controller, Inv,
+      std::move(PrebuiltModulesASTMap), StableDirs);
+  ScanInstance.addDependencyCollector(MDC);
+  return MDC;
+}
+
+/// Manages (and terminates) the asynchronous compilation of modules.
+class AsyncModuleCompiles {
+  std::mutex Mutex;
+  bool Stop = false;
+  // FIXME: Have the service own a thread pool and use that instead.
+  std::vector<std::thread> Compiles;
+
+public:
+  /// Registers the module compilation, unless this instance is about to be
+  /// destroyed.
+  void add(llvm::unique_function<void()> Compile) {
+    std::lock_guard<std::mutex> Lock(Mutex);
+    if (!Stop)
+      Compiles.emplace_back(std::move(Compile));
+  }
+
+  ~AsyncModuleCompiles() {
+    {
+      std::lock_guard<std::mutex> Lock(Mutex);
+      Stop = true;
+    }
+    for (std::thread &Compile : Compiles)
+      Compile.join();
+  }
+};
+
+struct SingleModuleWithAsyncModuleCompiles : PreprocessOnlyAction {
+  DependencyScanningService &Service;
+  DependencyActionController &Controller;
+  AsyncModuleCompiles &Compiles;
+
+  SingleModuleWithAsyncModuleCompiles(DependencyScanningService &Service,
+                                      DependencyActionController &Controller,
+                                      AsyncModuleCompiles &Compiles)
+      : Service(Service), Controller(Controller), Compiles(Compiles) {}
+
+  bool BeginSourceFileAction(CompilerInstance &CI) override;
+};
+
+// Runs the preprocessor on a TU with single-module-parse-mode and compiles
+/// modules asynchronously without blocking or importing them.
+struct SingleTUWithAsyncModuleCompiles : PreprocessOnlyAction {
+  DependencyScanningService &Service;
+  DependencyActionController &Controller;
+  AsyncModuleCompiles &Compiles;
+
+  SingleTUWithAsyncModuleCompiles(DependencyScanningService &Service,
+                                  DependencyActionController &Controller,
+                                  AsyncModuleCompiles &Compiles)
+      : Service(Service), Controller(Controller), Compiles(Compiles) {}
+
+  bool BeginSourceFileAction(CompilerInstance &CI) override;
+};
+
+// The preprocessor callback that takes care of initiating an asynchronous
+/// module compilation if needed.
+struct AsyncModuleCompile : PPCallbacks {
+  CompilerInstance &CI;
+  DependencyScanningService &Service;
+  DependencyActionController &Controller;
+  AsyncModuleCompiles &Compiles;
+
+  AsyncModuleCompile(CompilerInstance &CI, DependencyScanningService &Service,
+                     DependencyActionController &Controller,
+                     AsyncModuleCompiles &Compiles)
+      : CI(CI), Service(Service), Controller(Controller), Compiles(Compiles) {}
+
+  void moduleLoadSkipped(Module *M) override {
+    M = M->getTopLevelModule();
+
+    HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+    ModuleCache &ModCache = CI.getModuleCache();
+    ModuleFileName ModuleFileName = HS.getCachedModuleFileName(M);
+
+    uint64_t Timestamp = ModCache.getModuleTimestamp(ModuleFileName);
+    // Someone else already built/validated the PCM.
+    if (Timestamp > CI.getHeaderSearchOpts().BuildSessionTimestamp)
+      return;
+
+    if (!CI.getASTReader())
+      CI.createASTReader();
+    SmallVector<ASTReader::ImportedModule, 0> Imported;
+    // Only calling ReadASTCore() to avoid the expensive eager deserialization
+    // of the clang::Module objects in ReadAST().
+    // FIXME: Consider doing this in the new thread depending on how expensive
+    // the read turns out to be.
+    switch (CI.getASTReader()->ReadASTCore(
+        ModuleFileName, serialization::MK_ImplicitModule, SourceLocation(),
+        nullptr, Imported, {}, {}, {},
+        ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing |
+            ASTReader::ARR_TreatModuleWithErrorsAsOutOfDate)) {
+    case ASTReader::Success:
+      // We successfully read a valid, up-to-date PCM.
+      // FIXME: This could update the timestamp. Regular calls to
+      // ASTReader::ReadAST() would do so unless they encountered corrupted
+      // AST block, corrupted extension block, or did not read the expected
+      // top-level module.
+      return;
+    case ASTReader::OutOfDate:
+    case ASTReader::Missing:
+      // The most interesting case.
+      break;
+    default:
+      // Let the regular scan diagnose this.
+      return;
+    }
+
+    auto Lock = ModCache.getLock(ModuleFileName);
+    bool Owned;
+    llvm::Error LockErr = Lock->tryLock().moveInto(Owned);
+    // Someone else is building the PCM right now.
+    if (!LockErr && !Owned)
+      return;
+    // We should build the PCM.
+    IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
+        llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
+            Service, Service.getOpts().MakeVFS());
+    VFS = createVFSFromCompilerInvocation(CI.getInvocation(),
+                                          CI.getDiagnostics(), std::move(VFS));
+    auto DC = std::make_unique<DiagnosticConsumer>();
+    auto MC = makeInProcessModuleCache(Service.getModuleCacheEntries(),
+                                       Service.getLogger());
+    CompilerInstance::ThreadSafeCloneConfig CloneConfig(std::move(VFS), *DC,
+                                                        std::move(MC));
+    auto ModCI1 = CI.cloneForModuleCompile(SourceLocation(), M, ModuleFileName,
+                                           CloneConfig);
+    auto ModCI2 = CI.cloneForModuleCompile(SourceLocation(), M, ModuleFileName,
+                                           CloneConfig);
+
+    auto ModController = Controller.clone();
+
+    // Note: This lock belongs to a module cache that might not outlive the
+    // thread. This works, because the in-process lock only refers to an object
+    // managed by the service, which does outlive the thread.
+    Compiles.add([Lock = std::move(Lock), ModCI1 = std::move(ModCI1),
+                  ModCI2 = std::move(ModCI2), DC = std::move(DC),
+                  ModController = std::move(ModController), Service = &Service,
+                  Compiles = &Compiles] {
+      llvm::CrashRecoveryContext CRC;
+      (void)CRC.RunSafely([&] {
+        // Quickly discovers and compiles modules for the real scan below.
+        SingleModuleWithAsyncModuleCompiles Action1(*Service, *ModController,
+                                                    *Compiles);
+        (void)ModCI1->ExecuteAction(Action1);
+        // The real scan below.
+        ModCI2->getPreprocessorOpts().SingleModuleParseMode = false;
+        GenerateModuleFromModuleMapAction Action2;
+        (void)ModCI2->ExecuteAction(Action2);
+      });
+    });
+  }
+};
+
+bool SingleModuleWithAsyncModuleCompiles::BeginSourceFileAction(
+    CompilerInstance &CI) {
+  CI.getInvocation().getPreprocessorOpts().SingleModuleParseMode = true;
+  CI.getPreprocessor().addPPCallbacks(
+      std::make_unique<AsyncModuleCompile>(CI, Service, Controller, Compiles));
+  return true;
+}
+
+bool SingleTUWithAsyncModuleCompiles::BeginSourceFileAction(
+    CompilerInstance &CI) {
+  CI.getInvocation().getPreprocessorOpts().SingleModuleParseMode = true;
+  CI.getPreprocessor().addPPCallbacks(
+      std::make_unique<AsyncModuleCompile>(CI, Service, Controller, Compiles));
+  return true;
+}
+
+void runTUModulePrescan(CompilerInstance &PrescanCI,
+                        DependencyScanningService &Service,
+                        DependencyActionController &Controller,
+                        AsyncModuleCompiles &Compiles) {
+  SingleTUWithAsyncModuleCompiles Action(Service, Controller, Compiles);
+  (void)PrescanCI.ExecuteAction(Action);
+}
+} // namespace
+
 namespace clang {
 namespace dependencies {
+
+DiagnosticsEngineWithDiagOpts::DiagnosticsEngineWithDiagOpts(
+    ArrayRef<std::string> CommandLine,
+    IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC) {
+  std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
+  llvm::transform(CommandLine, CCommandLine.begin(),
+                  [](const std::string &Str) { return Str.c_str(); });
+  DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
+  sanitizeDiagOpts(*DiagOpts);
+  DiagEngine = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DC,
+                                                   /*ShouldOwnClient=*/false);
+}
+
 class CompilerInstanceWithContext {
   // Context
   DependencyScanningWorker &Worker;
diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp
index e2a41cc77a0c7..525e526e95c11 100644
--- a/clang/lib/Tooling/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanningTool.cpp
@@ -9,7 +9,6 @@
 #include "clang/Tooling/DependencyScanningTool.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticFrontend.h"
-#include "clang/DependencyScanning/DependencyScannerImpl.h"
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
 #include "clang/Driver/Tool.h"



More information about the llvm-branch-commits mailing list