[llvm-branch-commits] [clang] [clang][DependencyScanning] Use CompilerInstanceWithContext for TU Scanning (PR #211408)
Qiongsi Wu via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jul 22 15:46:49 PDT 2026
https://github.com/qiongsiwu created https://github.com/llvm/llvm-project/pull/211408
After `CompilerInstanceWithContext`'s relocation to `DependencyScanningWorker.cpp`, we can use it freely as an implementation engine for TU scanning. This PR does that, and unifies the by-name scanning and TU scanning so they all go through `CompilerInstanceWithContext`.
rdar://167034309
---
<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 6edc7e58bd654ee13412c8a6be5f50bc5e3937e5 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qiongsi_wu at apple.com>
Date: Wed, 22 Jul 2026 11:38:54 -0700
Subject: [PATCH] Use CompilerInstanceWithContext for TU scanning.
---
.../DependencyScannerImpl.h | 60 ++++---
.../DependencyScannerImpl.cpp | 152 +---------------
.../DependencyScanningWorker.cpp | 170 ++++++++++++++----
clang/test/ClangScanDeps/logging-simple.c | 1 +
.../Tooling/DependencyScannerTest.cpp | 35 ++++
5 files changed, 207 insertions(+), 211 deletions(-)
diff --git a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
index f973429a783c3..6c77dd936a092 100644
--- a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
+++ b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
@@ -15,6 +15,8 @@
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/Support/VirtualFileSystem.h"
+#include <mutex>
+#include <thread>
namespace clang {
class DiagnosticConsumer;
@@ -27,33 +29,6 @@ class DependencyConsumer;
class DependencyActionController;
class DependencyScanningWorkerFilesystem;
-class DependencyScanningAction {
-public:
- DependencyScanningAction(
- DependencyScanningService &Service, StringRef WorkingDirectory,
- DependencyConsumer &Consumer, DependencyActionController &Controller,
- IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS)
- : Service(Service), WorkingDirectory(WorkingDirectory),
- Consumer(Consumer), Controller(Controller), DepFS(std::move(DepFS)) {}
- bool runInvocation(std::string Executable,
- std::unique_ptr<CompilerInvocation> Invocation,
- IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
- std::shared_ptr<PCHContainerOperations> PCHContainerOps,
- DiagnosticConsumer *DiagConsumer);
-
- bool hasScanned() const { return Scanned; }
-
-private:
- DependencyScanningService &Service;
- StringRef WorkingDirectory;
- DependencyConsumer &Consumer;
- DependencyActionController &Controller;
- IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
- std::optional<CompilerInstance> ScanInstanceStorage;
- std::shared_ptr<ModuleDepCollector> MDC;
- bool Scanned = false;
-};
-
// Helper functions and data types.
std::unique_ptr<DiagnosticOptions>
createDiagOptions(ArrayRef<std::string> CommandLine);
@@ -110,6 +85,37 @@ std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector(
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
diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
index 2a264269652ca..003d0170df41f 100644
--- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
+++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
@@ -529,35 +529,6 @@ dependencies::initializeScanInstanceDependencyCollector(
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() {
- {
- // Prevent registration of further module compiles.
- std::lock_guard<std::mutex> Lock(Mutex);
- Stop = true;
- }
-
- // Wait for outstanding module compiles to finish.
- for (std::thread &Compile : Compiles)
- Compile.join();
- }
-};
-
struct SingleModuleWithAsyncModuleCompiles : PreprocessOnlyAction {
DependencyScanningService &Service;
DependencyActionController &Controller;
@@ -698,121 +669,10 @@ bool SingleModuleWithAsyncModuleCompiles::BeginSourceFileAction(
return true;
}
-bool DependencyScanningAction::runInvocation(
- std::string Executable,
- std::unique_ptr<CompilerInvocation> OriginalInvocation,
- IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
- std::shared_ptr<PCHContainerOperations> PCHContainerOps,
- 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.getOpts().OptimizeArgs & ScanningOptimizations::Macros))
- canonicalizeDefines(OriginalInvocation->getPreprocessorOpts());
-
- if (Scanned) {
- CompilerInstance &ScanInstance = *ScanInstanceStorage;
-
- // Scanning runs once for the first -cc1 invocation in a chain of driver
- // jobs. For any dependent jobs, reuse the scanning result and just
- // update the new invocation.
- // FIXME: to support multi-arch builds, each arch requires a separate scan
- if (MDC)
- MDC->applyDiscoveredDependencies(*OriginalInvocation);
-
- bool Success = OriginalInvocation->withCowRef<bool>(
- [&](CowCompilerInvocation &CowOriginalInvocation) {
- return Controller.finalize(ScanInstance, CowOriginalInvocation);
- });
- if (!Success)
- return false;
-
- Consumer.handleBuildCommand(
- {Executable, OriginalInvocation->getCC1CommandLine()});
- return true;
- }
-
- Scanned = true;
-
- // Create a compiler instance to handle the actual work.
- auto ScanInvocation =
- createScanCompilerInvocation(*OriginalInvocation, Service, Controller);
-
- // Quickly discovers and compiles modules for the real scan below.
- std::optional<AsyncModuleCompiles> AsyncCompiles;
- if (Service.getOpts().AsyncScanModules) {
- auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries(),
- Service.getLogger());
- auto ScanInstanceStorage = std::make_unique<CompilerInstance>(
- std::make_shared<CompilerInvocation>(*ScanInvocation), PCHContainerOps,
- std::move(ModCache));
- CompilerInstance &ScanInstance = *ScanInstanceStorage;
-
- DiagnosticConsumer DiagConsumer;
- initializeScanCompilerInstance(ScanInstance, FS, &DiagConsumer, Service,
- DepFS);
-
- // FIXME: Do this only once.
- SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance);
- auto MaybePrebuiltModulesASTMap =
- computePrebuiltModulesASTMap(ScanInstance, StableDirs);
- if (!MaybePrebuiltModulesASTMap)
- return false;
-
- // Normally this would be handled by GeneratePCHAction
- if (ScanInstance.getFrontendOpts().ProgramAction == frontend::GeneratePCH)
- ScanInstance.getLangOpts().CompilingPCH = true;
-
- AsyncCompiles.emplace();
- SingleTUWithAsyncModuleCompiles Action(Service, Controller, *AsyncCompiles);
- (void)ScanInstance.ExecuteAction(Action);
- }
-
- auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries(),
- Service.getLogger());
- ScanInstanceStorage.emplace(std::move(ScanInvocation),
- std::move(PCHContainerOps), std::move(ModCache));
- CompilerInstance &ScanInstance = *ScanInstanceStorage;
-
- initializeScanCompilerInstance(ScanInstance, FS, DiagConsumer, Service,
- DepFS);
-
- llvm::SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance);
- auto MaybePrebuiltModulesASTMap =
- computePrebuiltModulesASTMap(ScanInstance, StableDirs);
- if (!MaybePrebuiltModulesASTMap)
- return false;
-
- auto DepOutputOpts = createDependencyOutputOptions(*OriginalInvocation);
-
- MDC = initializeScanInstanceDependencyCollector(
- ScanInstance, std::move(DepOutputOpts), Service, *OriginalInvocation,
- Controller, *MaybePrebuiltModulesASTMap, StableDirs);
-
- if (ScanInstance.getDiagnostics().hasErrorOccurred())
- return false;
-
- if (!Controller.initialize(ScanInstance, *OriginalInvocation))
- return false;
-
- ReadPCHAndPreprocessAction Action;
- const bool Result = ScanInstance.ExecuteAction(Action);
-
- if (Result) {
- if (MDC) {
- MDC->run(Consumer);
- MDC->applyDiscoveredDependencies(*OriginalInvocation);
- }
-
- bool Success = OriginalInvocation->withCowRef<bool>(
- [&](CowCompilerInvocation &CowOriginalInvocation) {
- return Controller.finalize(ScanInstance, CowOriginalInvocation);
- });
- if (!Success)
- return false;
-
- Consumer.handleBuildCommand(
- {Executable, OriginalInvocation->getCC1CommandLine()});
- }
-
- return Result;
+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 6cb65f9464c89..488f338e0d541 100644
--- a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
@@ -30,10 +30,6 @@ class CompilerInstanceWithContext {
llvm::StringRef CWD;
std::vector<std::string> CommandLine;
- // Context - Diagnostics engine.
- DiagnosticConsumer *DiagConsumer = nullptr;
- std::unique_ptr<DiagnosticsEngineWithDiagOpts> DiagEngineWithCmdAndOpts;
-
// Context - compiler invocation
std::unique_ptr<CompilerInvocation> OriginalInvocation;
@@ -44,6 +40,9 @@ class CompilerInstanceWithContext {
llvm::SmallVector<StringRef> StableDirs;
PrebuiltModulesAttrsMap PrebuiltModuleASTMap;
+ // Context - used by AsyncScan's prescan pass
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> ScanFS;
+
// Compiler Instance
std::unique_ptr<CompilerInstance> CIPtr;
@@ -64,21 +63,26 @@ class CompilerInstanceWithContext {
CommandLine);
}
assert(DiagEngineWithDiagOpts && "Valid diagnostics engine required!");
- DiagEngineWithCmdAndOpts = std::move(DiagEngineWithDiagOpts);
- DiagConsumer = DiagEngineWithCmdAndOpts->DiagEngine->getClient();
-
- assert(OverlayFS && "OverlayFS required!");
- auto FS = Worker.makeEffectiveVFS(CWD, std::move(OverlayFS));
+ ScanFS = Worker.makeEffectiveVFS(CWD, std::move(OverlayFS));
OriginalInvocation = createCompilerInvocation(
- CommandLine, *DiagEngineWithCmdAndOpts->DiagEngine);
+ CommandLine, *DiagEngineWithDiagOpts->DiagEngine);
if (!OriginalInvocation) {
- DiagEngineWithCmdAndOpts->DiagEngine->Report(
+ DiagEngineWithDiagOpts->DiagEngine->Report(
diag::err_fe_expected_compiler_job)
<< llvm::join(CommandLine, " ");
return false;
}
+ return initializeScanInstance(
+ Controller, DiagEngineWithDiagOpts->DiagEngine->getClient());
+ }
+
+ bool initializeScanInstance(DependencyActionController &Controller,
+ DiagnosticConsumer *DiagConsumer) {
+ assert(OriginalInvocation && ScanFS &&
+ "OriginalInvocation and ScanFS must be set before this call");
+
if (any(Worker.Service.getOpts().OptimizeArgs &
ScanningOptimizations::Macros))
canonicalizeDefines(OriginalInvocation->getPreprocessorOpts());
@@ -92,9 +96,8 @@ class CompilerInstanceWithContext {
Worker.PCHContainerOps, std::move(ModCache));
auto &CI = *CIPtr;
- initializeScanCompilerInstance(
- CI, std::move(FS), DiagEngineWithCmdAndOpts->DiagEngine->getClient(),
- Worker.Service, Worker.DepFS);
+ initializeScanCompilerInstance(CI, ScanFS, DiagConsumer, Worker.Service,
+ Worker.DepFS);
StableDirs = getInitialStableDirs(CI);
auto MaybePrebuiltModulesASTMap =
@@ -115,6 +118,31 @@ class CompilerInstanceWithContext {
return true;
}
+ bool prescanModulesAsync(AsyncModuleCompiles &Compiles,
+ DependencyActionController &Controller) {
+ auto ModCache = makeInProcessModuleCache(
+ Worker.Service.getModuleCacheEntries(), Worker.Service.getLogger());
+ CompilerInstance PrescanCI(
+ std::make_shared<CompilerInvocation>(CIPtr->getInvocation()),
+ Worker.PCHContainerOps, std::move(ModCache));
+
+ DiagnosticConsumer DiagConsumer;
+ initializeScanCompilerInstance(PrescanCI, ScanFS, &DiagConsumer,
+ Worker.Service, Worker.DepFS);
+
+ // FIXME: reuse the StableDirs/PrebuiltModuleASTMap computed in
+ // initialize().
+ SmallVector<StringRef> PrescanStableDirs = getInitialStableDirs(PrescanCI);
+ if (!computePrebuiltModulesASTMap(PrescanCI, PrescanStableDirs))
+ return false;
+
+ if (PrescanCI.getFrontendOpts().ProgramAction == frontend::GeneratePCH)
+ PrescanCI.getLangOpts().CompilingPCH = true;
+
+ runTUModulePrescan(PrescanCI, Worker.Service, Controller, Compiles);
+ return true;
+ }
+
public:
static std::optional<CompilerInstanceWithContext>
initializeFromCC1Commandline(
@@ -241,6 +269,58 @@ class CompilerInstanceWithContext {
return true;
}
+
+ std::shared_ptr<ModuleDepCollector>
+ scanTranslationUnit(DependencyConsumer &Consumer,
+ DependencyActionController &Controller) {
+ assert(CIPtr && "CIPtr must be initialized before calling this method");
+ auto &CI = *CIPtr;
+
+ std::optional<AsyncModuleCompiles> AsyncCompiles;
+ if (Worker.Service.getOpts().AsyncScanModules) {
+ AsyncCompiles.emplace();
+ if (!prescanModulesAsync(*AsyncCompiles, Controller))
+ return nullptr;
+ }
+
+ auto MDC = initializeScanInstanceDependencyCollector(
+ CI, std::make_unique<DependencyOutputOptions>(*OutputOpts),
+ Worker.Service, *OriginalInvocation, Controller, PrebuiltModuleASTMap,
+ StableDirs);
+
+ if (CI.getDiagnostics().hasErrorOccurred())
+ return nullptr;
+
+ if (!Controller.initialize(CI, *OriginalInvocation))
+ return nullptr;
+
+ ReadPCHAndPreprocessAction Action;
+ if (!CI.ExecuteAction(Action))
+ return nullptr;
+
+ MDC->run(Consumer);
+ if (!applyAndReport(*MDC, *OriginalInvocation, Consumer, Controller,
+ CommandLine[0]))
+ return nullptr;
+ return MDC;
+ }
+
+ bool applyAndReport(ModuleDepCollector &MDC,
+ CompilerInvocation &ModuleInvocation,
+ DependencyConsumer &Consumer,
+ DependencyActionController &Controller,
+ StringRef Executable) {
+ MDC.applyDiscoveredDependencies(ModuleInvocation);
+ bool Success = ModuleInvocation.withCowRef<bool>(
+ [&](CowCompilerInvocation &CowModuleInvocation) {
+ return Controller.finalize(*CIPtr, CowModuleInvocation);
+ });
+ if (!Success)
+ return false;
+ Consumer.handleBuildCommand(
+ {Executable.str(), ModuleInvocation.getCC1CommandLine()});
+ return true;
+ }
};
} // namespace dependencies
} // namespace clang
@@ -269,20 +349,6 @@ DependencyScanningWorker::DependencyScanningWorker(
DependencyScanningWorker::~DependencyScanningWorker() = default;
-static bool createAndRunToolInvocation(
- ArrayRef<std::string> CommandLine, DependencyScanningAction &Action,
- IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
- std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
- DiagnosticsEngine &Diags) {
- auto Invocation = createCompilerInvocation(CommandLine, Diags);
- if (!Invocation)
- return false;
-
- return Action.runInvocation(CommandLine[0], std::move(Invocation),
- std::move(FS), PCHContainerOps,
- Diags.getClient());
-}
-
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
DependencyScanningWorker::makeEffectiveVFS(
StringRef WorkingDirectory,
@@ -303,10 +369,11 @@ bool DependencyScanningWorker::computeDependencies(
DependencyConsumer &DepConsumer, DependencyActionController &Controller,
DiagnosticConsumer &DiagConsumer,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS) {
- auto FS = makeEffectiveVFS(WorkingDirectory, std::move(OverlayFS));
+ auto FS = makeEffectiveVFS(WorkingDirectory, OverlayFS);
- DependencyScanningAction Action(Service, WorkingDirectory, DepConsumer,
- Controller, DepFS);
+ bool Scanned = false;
+ std::shared_ptr<ModuleDepCollector> MDC;
+ std::optional<CompilerInstanceWithContext> CIWC;
const bool Success = llvm::all_of(CommandLines, [&](const auto &Cmd) {
if (StringRef(Cmd[1]) != "-cc1") {
@@ -323,16 +390,43 @@ bool DependencyScanningWorker::computeDependencies(
});
auto DiagEngineWithDiagOpts =
- DiagnosticsEngineWithDiagOpts(Cmd, FS, DiagConsumer);
- auto &Diags = *DiagEngineWithDiagOpts.DiagEngine;
+ std::make_unique<DiagnosticsEngineWithDiagOpts>(Cmd, FS, DiagConsumer);
+ if (!Scanned) {
+ // Scanning runs once for the first -cc1 invocation in a chain of driver
+ // jobs.
+ // For any dependent jobs, reuse the scanning result and just update the
+ // new invocation.
+ // FIXME: to support multi-arch builds, each arch requires a separate
+ // scan.
+ Scanned = true;
+ auto Result = CompilerInstanceWithContext::initializeFromCC1Commandline(
+ *this, WorkingDirectory, Cmd, std::move(DiagEngineWithDiagOpts),
+ OverlayFS, Controller);
+ if (!Result)
+ return false;
+ CIWC.emplace(std::move(*Result));
+ MDC = CIWC->scanTranslationUnit(DepConsumer, Controller);
+ return MDC != nullptr;
+ }
+
+ auto Invocation =
+ createCompilerInvocation(Cmd, *DiagEngineWithDiagOpts->DiagEngine);
+ if (!Invocation)
+ return false;
+
+ // The first cc1 is canonicalized in initializeScanInstance; each sibling
+ // invocation must likewise be canonicalized before its cc1 command line is
+ // emitted. This is mostly relevant for multi-arch jobs where we currently
+ // do not do re-scans.
+ if (any(Service.getOpts().OptimizeArgs & ScanningOptimizations::Macros))
+ canonicalizeDefines(Invocation->getPreprocessorOpts());
- // Create an invocation that uses the underlying file system to ensure that
- // any file system requests that are made by the driver do not go through
- // the dependency scanning filesystem.
- return createAndRunToolInvocation(Cmd, Action, FS, PCHContainerOps, Diags);
+ assert(CIWC && "Must have an initialized CIWC");
+ return CIWC->applyAndReport(*MDC, *Invocation, DepConsumer, Controller,
+ Cmd.front());
});
- return Success && Action.hasScanned();
+ return Success && Scanned;
}
bool DependencyScanningWorker::computeDependenciesByName(
diff --git a/clang/test/ClangScanDeps/logging-simple.c b/clang/test/ClangScanDeps/logging-simple.c
index d85d660317c75..0022fb97cbc73 100644
--- a/clang/test/ClangScanDeps/logging-simple.c
+++ b/clang/test/ClangScanDeps/logging-simple.c
@@ -14,6 +14,7 @@
// less, strictly in this order. Changes to this list should be intentional.
// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID:]]: starting scanning command:{{.*}}tu.c
+// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: init_compiler_instance_with_context:{{.*}}
// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: timestamp_read: {{.*}}[[PCMFILE:.*\.pcm]]
// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: pcm_read_cached: {{.*}}[[PCMFILE]]
// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: pcm_read_disk: {{.*}}[[PCMFILE]]
diff --git a/clang/unittests/Tooling/DependencyScannerTest.cpp b/clang/unittests/Tooling/DependencyScannerTest.cpp
index 6b119000000cf..62782f8a56945 100644
--- a/clang/unittests/Tooling/DependencyScannerTest.cpp
+++ b/clang/unittests/Tooling/DependencyScannerTest.cpp
@@ -304,3 +304,38 @@ TEST(DependencyScanner, ScanDepsWithModuleLookup) {
EXPECT_TRUE(!llvm::is_contained(InterceptFS->StatPaths, OtherPath));
EXPECT_EQ(InterceptFS->ReadFiles, std::vector<std::string>{"test.m"});
}
+
+// When scanning from a TU buffer, the in-memory TU lives ONLY
+// in the overlay filesystem built from that buffer, never on the base VFS. If
+// DependencyScanningWorker::computeDependencies moves the overlay away before
+// initializing the scan CompilerInstance, the scanner falls back to the base
+// VFS, cannot find its input, and the scan fails.
+TEST(DependencyScanner, ScanDepsTUBufferOverlayReachesScan) {
+ std::vector<std::string> CommandLine = {
+ "clang", "-target", "x86_64-apple-macosx10.7", "-c", "-o", "tu.o"};
+ StringRef CWD = "/root";
+
+ // Base VFS intentionally does NOT contain the TU file.
+ auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+ VFS->setCurrentWorkingDirectory(CWD);
+
+ DependencyScanningServiceOptions Opts;
+ Opts.MakeVFS = [&] { return VFS; };
+ DependencyScanningService Service(std::move(Opts));
+ DependencyScanningTool ScanTool(Service);
+
+ auto Sept = llvm::sys::path::get_separator();
+ std::string TUPath = std::string(llvm::formatv("{0}root{0}tu.c", Sept));
+ auto TU = llvm::MemoryBuffer::getMemBuffer("int main(void) { return 0; }\n",
+ TUPath);
+
+ TextDiagnosticBuffer DiagConsumer;
+ llvm::DenseSet<ModuleID> AlreadySeen;
+ auto Result = ScanTool.getTranslationUnitDependencies(
+ CommandLine, CWD, DiagConsumer, AlreadySeen,
+ CallbackActionController::lookupUnreachableModuleOutput,
+ TU->getMemBufferRef());
+ ASSERT_TRUE(Result.has_value());
+ EXPECT_TRUE(llvm::any_of(Result->FileDeps,
+ [](StringRef F) { return F.contains("tu.c"); }));
+}
More information about the llvm-branch-commits
mailing list