[clang] [clang-tools-extra] [clang][deps] Extract service config into a struct (PR #181405)

Jan Svoboda via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 13 13:08:40 PST 2026


https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/181405

>From 6f6fab65c8047220e661af0d8d1eeabaa8db1693 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 13 Feb 2026 11:37:32 -0800
Subject: [PATCH 1/2] [clang][deps] Extract service config into a struct

Adding new configuration knobs in the scanner is fairly painful now, especially with a diverging downstream. This patch extracts what was previously passed into the service constructor into a struct. This encourages one knob customization per line, reduces difficult merge conflicts, `/*ArgName=*/`-style comments with copy-pasted defaults, etc.
---
 .../DependencyScanningService.h               | 58 ++++++++-----------
 .../DependencyScannerImpl.cpp                 | 17 +++---
 .../DependencyScanningService.cpp             | 13 ++---
 .../DependencyScanningWorker.cpp              |  2 +-
 .../DependencyScanning/ModuleDepCollector.cpp | 20 +++----
 clang/tools/clang-scan-deps/ClangScanDeps.cpp | 11 +++-
 .../DependencyScanningWorkerTest.cpp          |  5 +-
 .../Tooling/DependencyScannerTest.cpp         | 10 ++--
 8 files changed, 67 insertions(+), 69 deletions(-)

diff --git a/clang/include/clang/DependencyScanning/DependencyScanningService.h b/clang/include/clang/DependencyScanning/DependencyScanningService.h
index 9e8041836bc26..509b6a579ecb4 100644
--- a/clang/include/clang/DependencyScanning/DependencyScanningService.h
+++ b/clang/include/clang/DependencyScanning/DependencyScanningService.h
@@ -12,7 +12,6 @@
 #include "clang/DependencyScanning/DependencyScanningFilesystem.h"
 #include "clang/DependencyScanning/InProcessModuleCache.h"
 #include "llvm/ADT/BitmaskEnum.h"
-#include "llvm/Support/Chrono.h"
 
 namespace clang {
 namespace dependencies {
@@ -77,29 +76,34 @@ enum class ScanningOptimizations {
 
 #undef DSS_LAST_BITMASK_ENUM
 
+/// The configuration knobs for the dependency scanning service.
+struct DependencyScanningServiceOptions {
+  DependencyScanningServiceOptions();
+
+  /// Whether to use optimized dependency directive scan or full preprocessing.
+  ScanningMode Mode = ScanningMode::DependencyDirectivesScan;
+  /// What output format are we expected to produce.
+  ScanningOutputFormat Format = ScanningOutputFormat::Full;
+  /// How to optimize resulting explicit module command lines.
+  ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default;
+  /// Whether the resulting command lines should load explicit PCMs eagerly.
+  bool EagerLoadModules = false;
+  /// Whether to trace VFS accesses during the scan.
+  bool TraceVFS = false;
+  /// Whether to scan modules asynchronously.
+  bool AsyncScanModules = false;
+  /// The build session timestamp for validate-once-per-build-session logic.
+  std::time_t BuildSessionTimestamp; // = std::chrono::system_clock::now();
+};
+
 /// The dependency scanning service contains shared configuration and state that
 /// is used by the individual dependency scanning workers.
 class DependencyScanningService {
 public:
-  DependencyScanningService(
-      ScanningMode Mode, ScanningOutputFormat Format,
-      ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default,
-      bool EagerLoadModules = false, bool TraceVFS = false,
-      bool AsyncScanModules = false,
-      std::time_t BuildSessionTimestamp =
-          llvm::sys::toTimeT(std::chrono::system_clock::now()));
-
-  ScanningMode getMode() const { return Mode; }
-
-  ScanningOutputFormat getFormat() const { return Format; }
+  explicit DependencyScanningService(DependencyScanningServiceOptions Opts)
+      : Opts(std::move(Opts)) {}
 
-  ScanningOptimizations getOptimizeArgs() const { return OptimizeArgs; }
-
-  bool shouldEagerLoadModules() const { return EagerLoadModules; }
-
-  bool shouldTraceVFS() const { return TraceVFS; }
-
-  bool shouldScanModulesAsynchronously() const { return AsyncScanModules; }
+  const DependencyScanningServiceOptions &getOpts() const { return Opts; }
 
   DependencyScanningFilesystemSharedCache &getSharedCache() {
     return SharedCache;
@@ -107,25 +111,13 @@ class DependencyScanningService {
 
   ModuleCacheEntries &getModuleCacheEntries() { return ModCacheEntries; }
 
-  std::time_t getBuildSessionTimestamp() const { return BuildSessionTimestamp; }
-
 private:
-  const ScanningMode Mode;
-  const ScanningOutputFormat Format;
-  /// Whether to optimize the modules' command-line arguments.
-  const ScanningOptimizations OptimizeArgs;
-  /// Whether to set up command-lines to load PCM files eagerly.
-  const bool EagerLoadModules;
-  /// Whether to trace VFS accesses.
-  const bool TraceVFS;
-  /// Whether to scan modules asynchronously.
-  const bool AsyncScanModules;
+  /// The options customizing dependency scanning behavior.
+  DependencyScanningServiceOptions Opts;
   /// The global file system cache.
   DependencyScanningFilesystemSharedCache SharedCache;
   /// The global module cache entries.
   ModuleCacheEntries ModCacheEntries;
-  /// The build session timestamp.
-  std::time_t BuildSessionTimestamp;
 };
 
 } // end namespace dependencies
diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
index cafd3eb976312..28fa2571a24dc 100644
--- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
+++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
@@ -414,7 +414,7 @@ void dependencies::initializeScanCompilerInstance(
   ScanInstance.createSourceManager();
 
   // Use DepFS for getting the dependency directives if requested to do so.
-  if (Service.getMode() == ScanningMode::DependencyDirectivesScan) {
+  if (Service.getOpts().Mode == ScanningMode::DependencyDirectivesScan) {
     DepFS->resetBypassedPathPrefix();
     SmallString<256> ModulesCachePath;
     normalizeModuleCachePath(ScanInstance.getFileManager(),
@@ -442,7 +442,7 @@ createScanCompilerInvocation(const CompilerInvocation &Invocation,
 
   if (ScanInvocation->getHeaderSearchOpts().ModulesValidateOncePerBuildSession)
     ScanInvocation->getHeaderSearchOpts().BuildSessionTimestamp =
-        Service.getBuildSessionTimestamp();
+        Service.getOpts().BuildSessionTimestamp;
 
   ScanInvocation->getFrontendOpts().DisableFree = false;
   ScanInvocation->getFrontendOpts().GenerateGlobalModuleIndex = false;
@@ -454,7 +454,7 @@ createScanCompilerInvocation(const CompilerInvocation &Invocation,
   ScanInvocation->getFrontendOpts().ModulesShareFileManager = true;
   ScanInvocation->getHeaderSearchOpts().ModuleFormat = "raw";
   ScanInvocation->getHeaderSearchOpts().ModulesIncludeVFSUsage =
-      any(Service.getOptimizeArgs() & ScanningOptimizations::VFS);
+      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.
@@ -536,7 +536,7 @@ dependencies::initializeScanInstanceDependencyCollector(
     PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
     llvm::SmallVector<StringRef> &StableDirs) {
   std::shared_ptr<ModuleDepCollector> MDC;
-  switch (Service.getFormat()) {
+  switch (Service.getOpts().Format) {
   case ScanningOutputFormat::Make:
     ScanInstance.addDependencyCollector(
         std::make_shared<DependencyConsumerForwarder>(
@@ -689,7 +689,7 @@ bool DependencyScanningAction::runInvocation(
     DiagnosticConsumer *DiagConsumer) {
   // Making sure that we canonicalize the defines early to avoid unnecessary
   // variants in both the scanner and in the resulting  explicit command lines.
-  if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros))
+  if (any(Service.getOpts().OptimizeArgs & ScanningOptimizations::Macros))
     canonicalizeDefines(OriginalInvocation->getPreprocessorOpts());
 
   if (Scanned) {
@@ -711,7 +711,7 @@ bool DependencyScanningAction::runInvocation(
       createScanCompilerInvocation(*OriginalInvocation, Service);
 
   // Quickly discovers and compiles modules for the real scan below.
-  if (Service.shouldScanModulesAsynchronously()) {
+  if (Service.getOpts().AsyncScanModules) {
     auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries());
     auto ScanInstanceStorage = std::make_unique<CompilerInstance>(
         std::make_shared<CompilerInvocation>(*ScanInvocation), PCHContainerOps,
@@ -761,7 +761,7 @@ bool DependencyScanningAction::runInvocation(
 
   std::unique_ptr<FrontendAction> Action;
 
-  if (Service.getFormat() == ScanningOutputFormat::P1689)
+  if (Service.getOpts().Format == ScanningOutputFormat::P1689)
     Action = std::make_unique<PreprocessOnlyAction>();
   else
     Action = std::make_unique<ReadPCHAndPreprocessAction>();
@@ -809,7 +809,8 @@ bool CompilerInstanceWithContext::initialize(
     return false;
   }
 
-  if (any(Worker.Service.getOptimizeArgs() & ScanningOptimizations::Macros))
+  if (any(Worker.Service.getOpts().OptimizeArgs &
+          ScanningOptimizations::Macros))
     canonicalizeDefines(OriginalInvocation->getPreprocessorOpts());
 
   // Create the CompilerInstance.
diff --git a/clang/lib/DependencyScanning/DependencyScanningService.cpp b/clang/lib/DependencyScanning/DependencyScanningService.cpp
index 9224de39b40cc..3651b9b20a70f 100644
--- a/clang/lib/DependencyScanning/DependencyScanningService.cpp
+++ b/clang/lib/DependencyScanning/DependencyScanningService.cpp
@@ -8,14 +8,11 @@
 
 #include "clang/DependencyScanning/DependencyScanningService.h"
 
+#include "llvm/Support/Chrono.h"
+
 using namespace clang;
 using namespace dependencies;
 
-DependencyScanningService::DependencyScanningService(
-    ScanningMode Mode, ScanningOutputFormat Format,
-    ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool TraceVFS,
-    bool AsyncScanModules, std::time_t BuildSessionTimestamp)
-    : Mode(Mode), Format(Format), OptimizeArgs(OptimizeArgs),
-      EagerLoadModules(EagerLoadModules), TraceVFS(TraceVFS),
-      AsyncScanModules(AsyncScanModules),
-      BuildSessionTimestamp(BuildSessionTimestamp) {}
+DependencyScanningServiceOptions::DependencyScanningServiceOptions()
+    : BuildSessionTimestamp(
+          llvm::sys::toTimeT(std::chrono::system_clock::now())) {}
diff --git a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
index 25c8a092d38c2..7d0c93138d78c 100644
--- a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
@@ -30,7 +30,7 @@ DependencyScanningWorker::DependencyScanningWorker(
   // The scanner itself writes only raw ast files.
   PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
 
-  if (Service.shouldTraceVFS())
+  if (Service.getOpts().TraceVFS)
     BaseFS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(
         std::move(BaseFS));
 
diff --git a/clang/lib/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
index bbdcd7d8e2b44..46a0f79bfd38e 100644
--- a/clang/lib/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
@@ -334,7 +334,7 @@ ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs(
     // TODO: Verify this works fine when modulemap for module A is eagerly
     // loaded from A.pcm, and module map passed on the command line contains
     // definition of a submodule: "explicit module A.Private { ... }".
-    if (Service.shouldEagerLoadModules() &&
+    if (Service.getOpts().EagerLoadModules &&
         DepModuleMapFiles.contains(*ModuleMapEntry))
       continue;
 
@@ -385,7 +385,7 @@ llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles(
 
 void ModuleDepCollector::addModuleMapFiles(
     CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
-  if (Service.shouldEagerLoadModules())
+  if (Service.getOpts().EagerLoadModules)
     return; // Only pcm is needed for eager load.
 
   for (const ModuleID &MID : ClangModuleDeps) {
@@ -402,7 +402,7 @@ void ModuleDepCollector::addModuleFiles(
     std::string PCMPath =
         Controller.lookupModuleOutput(*MD, ModuleOutputKind::ModuleFile);
 
-    if (Service.shouldEagerLoadModules())
+    if (Service.getOpts().EagerLoadModules)
       CI.getFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));
     else
       CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
@@ -417,7 +417,7 @@ void ModuleDepCollector::addModuleFiles(
     std::string PCMPath =
         Controller.lookupModuleOutput(*MD, ModuleOutputKind::ModuleFile);
 
-    if (Service.shouldEagerLoadModules())
+    if (Service.getOpts().EagerLoadModules)
       CI.getMutFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));
     else
       CI.getMutHeaderSearchOpts().PrebuiltModuleFiles.insert(
@@ -525,7 +525,7 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
 void ModuleDepCollector::associateWithContextHash(
     const CowCompilerInvocation &CI, bool IgnoreCWD, ModuleDeps &Deps) {
   Deps.ID.ContextHash =
-      getModuleContextHash(Deps, CI, Service.shouldEagerLoadModules(),
+      getModuleContextHash(Deps, CI, Service.getOpts().EagerLoadModules,
                            IgnoreCWD, ScanInstance.getVirtualFileSystem());
   bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second;
   (void)Inserted;
@@ -752,21 +752,21 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
   CowCompilerInvocation CI =
       MDC.getInvocationAdjustedForModuleBuildWithoutOutputs(
           MD, [&](CowCompilerInvocation &BuildInvocation) {
-            if (any(MDC.Service.getOptimizeArgs() &
+            if (any(MDC.Service.getOpts().OptimizeArgs &
                     (ScanningOptimizations::HeaderSearch |
                      ScanningOptimizations::VFS)))
               optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(),
                                        *MDC.ScanInstance.getASTReader(), *MF,
                                        MDC.PrebuiltModulesASTMap,
-                                       MDC.Service.getOptimizeArgs());
+                                       MDC.Service.getOpts().OptimizeArgs);
 
-            if (any(MDC.Service.getOptimizeArgs() &
+            if (any(MDC.Service.getOpts().OptimizeArgs &
                     ScanningOptimizations::SystemWarnings))
               optimizeDiagnosticOpts(
                   BuildInvocation.getMutDiagnosticOpts(),
                   BuildInvocation.getFrontendOpts().IsSystemModule);
 
-            IgnoreCWD = any(MDC.Service.getOptimizeArgs() &
+            IgnoreCWD = any(MDC.Service.getOpts().OptimizeArgs &
                             ScanningOptimizations::IgnoreCWD) &&
                         isSafeToIgnoreCWD(BuildInvocation);
             if (IgnoreCWD) {
@@ -960,7 +960,7 @@ static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
 }
 
 void ModuleDepCollector::addFileDep(StringRef Path) {
-  if (Service.getFormat() == ScanningOutputFormat::P1689) {
+  if (Service.getOpts().Format == ScanningOutputFormat::P1689) {
     // Within P1689 format, we don't want all the paths to be absolute path
     // since it may violate the traditional make style dependencies info.
     FileDeps.emplace_back(Path);
diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index efa9bff019a60..30fb8d47f8d5d 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -1138,9 +1138,14 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
     });
   };
 
-  DependencyScanningService Service(ScanMode, Format, OptimizeArgs,
-                                    EagerLoadModules, /*TraceVFS=*/Verbose,
-                                    AsyncScanModules);
+  DependencyScanningServiceOptions Opts;
+  Opts.Mode = ScanMode;
+  Opts.Format = Format;
+  Opts.OptimizeArgs = OptimizeArgs;
+  Opts.EagerLoadModules = EagerLoadModules;
+  Opts.TraceVFS = Verbose;
+  Opts.AsyncScanModules = AsyncScanModules;
+  DependencyScanningService Service(std::move(Opts));
 
   llvm::Timer T;
   T.startTimer();
diff --git a/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp b/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp
index 872c7effde3d3..ca51fc3f6fbcb 100644
--- a/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp
+++ b/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp
@@ -31,8 +31,9 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) {
                llvm::MemoryBuffer::getMemBuffer("#include \"header.h\"\n"));
   VFS->addFile(AsmPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
 
-  DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,
-                                    ScanningOutputFormat::Make);
+  DependencyScanningServiceOptions Opts;
+  Opts.Format = ScanningOutputFormat::Make;
+  DependencyScanningService Service(std::move(Opts));
   DependencyScanningWorker Worker(Service, VFS);
 
   llvm::DenseSet<ModuleID> AlreadySeen;
diff --git a/clang/unittests/Tooling/DependencyScannerTest.cpp b/clang/unittests/Tooling/DependencyScannerTest.cpp
index 42d1a7b242aa9..a764a206c4670 100644
--- a/clang/unittests/Tooling/DependencyScannerTest.cpp
+++ b/clang/unittests/Tooling/DependencyScannerTest.cpp
@@ -228,8 +228,9 @@ TEST(DependencyScanner, ScanDepsWithFS) {
   VFS->addFile(TestPath, 0,
                llvm::MemoryBuffer::getMemBuffer("#include \"header.h\"\n"));
 
-  DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,
-                                    ScanningOutputFormat::Make);
+  DependencyScanningServiceOptions Opts;
+  Opts.Format = ScanningOutputFormat::Make;
+  DependencyScanningService Service(std::move(Opts));
   DependencyScanningTool ScanTool(Service, VFS);
 
   TextDiagnosticBuffer DiagConsumer;
@@ -285,8 +286,9 @@ TEST(DependencyScanner, ScanDepsWithModuleLookup) {
 
   auto InterceptFS = llvm::makeIntrusiveRefCnt<InterceptorFS>(VFS);
 
-  DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,
-                                    ScanningOutputFormat::Make);
+  DependencyScanningServiceOptions Opts;
+  Opts.Format = ScanningOutputFormat::Make;
+  DependencyScanningService Service(std::move(Opts));
   DependencyScanningTool ScanTool(Service, InterceptFS);
 
   // This will fail with "fatal error: module 'Foo' not found" but it doesn't

>From 4c6a893782ffe5b96bfef5c7710530811b5c0e1d Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 13 Feb 2026 13:08:27 -0800
Subject: [PATCH 2/2] Fix ScanningProjectModules.cpp

---
 clang-tools-extra/clangd/ScanningProjectModules.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/clangd/ScanningProjectModules.cpp b/clang-tools-extra/clangd/ScanningProjectModules.cpp
index 860ce377be532..e2d710e3794d4 100644
--- a/clang-tools-extra/clangd/ScanningProjectModules.cpp
+++ b/clang-tools-extra/clangd/ScanningProjectModules.cpp
@@ -35,9 +35,12 @@ class ModuleDependencyScanner {
   ModuleDependencyScanner(
       std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
       const ThreadsafeFS &TFS)
-      : CDB(CDB), TFS(TFS),
-        Service(dependencies::ScanningMode::CanonicalPreprocessing,
-                dependencies::ScanningOutputFormat::P1689) {}
+      : CDB(CDB), TFS(TFS), Service([] {
+          dependencies::DependencyScanningServiceOptions Opts;
+          Opts.Mode = dependencies::ScanningMode::CanonicalPreprocessing;
+          Opts.Format = dependencies::ScanningOutputFormat::P1689;
+          return Opts;
+        }()) {}
 
   /// The scanned modules dependency information for a specific source file.
   struct ModuleDependencyInfo {



More information about the cfe-commits mailing list