[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