[clang][deps] Make the service provide the VFS (#181424)

This PR makes it so that the base VFS used during a scan is provided to
the top-level service, not to each worker/tool individually. This
enables resolving a FIXME in the async-scan-modules mode of the scanner,
will clean up [downstream
code](0eb56baa1d/clang/tools/libclang/CDependencies.cpp (L619-L622)),
and will make it more difficult to have mismatching VFSs across workers,
which may cause non-deterministic poisoning of
`DependencyScanningFilesystemSharedCache`.
This commit is contained in:
Jan Svoboda 2026-02-17 12:25:39 -08:00 committed by GitHub
parent 8f6866c9e9
commit e7bad4ac4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 23 additions and 26 deletions

View File

@ -35,8 +35,9 @@ public:
ModuleDependencyScanner(
std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
const ThreadsafeFS &TFS)
: CDB(CDB), TFS(TFS), Service([] {
: CDB(CDB), Service([&TFS] {
dependencies::DependencyScanningServiceOptions Opts;
Opts.MakeVFS = [&] { return TFS.view(std::nullopt); };
Opts.Mode = dependencies::ScanningMode::CanonicalPreprocessing;
Opts.Format = dependencies::ScanningOutputFormat::P1689;
return Opts;
@ -79,7 +80,6 @@ public:
private:
std::shared_ptr<const clang::tooling::CompilationDatabase> CDB;
const ThreadsafeFS &TFS;
// Whether the scanner has scanned the project globally.
bool GlobalScanned = false;
@ -109,9 +109,7 @@ ModuleDependencyScanner::scan(PathRef FilePath,
using namespace clang::tooling;
llvm::SmallString<128> FilePathDir(FilePath);
llvm::sys::path::remove_filename(FilePathDir);
DependencyScanningTool ScanningTool(Service, TFS.view(FilePathDir));
DependencyScanningTool ScanningTool(Service);
std::string S;
llvm::raw_string_ostream OS(S);

View File

@ -80,6 +80,11 @@ enum class ScanningOptimizations {
struct DependencyScanningServiceOptions {
DependencyScanningServiceOptions();
/// The function invoked to create each worker's VFS. This function and the
/// VFS itself must be thread-safe whenever using multiple workers
/// concurrently or whenever \c AsyncScanModules is true.
std::function<IntrusiveRefCntPtr<llvm::vfs::FileSystem>()>
MakeVFS; // = [] { return llvm::vfs::createPhysicalFileSystem(); }
/// Whether to use optimized dependency directive scan or full preprocessing.
ScanningMode Mode = ScanningMode::DependencyDirectivesScan;
/// What output format are we expected to produce.

View File

@ -87,9 +87,7 @@ public:
/// Construct a dependency scanning worker.
///
/// @param Service The parent service. Must outlive the worker.
/// @param BaseFS The filesystem for the worker to use.
DependencyScanningWorker(DependencyScanningService &Service,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS);
DependencyScanningWorker(DependencyScanningService &Service);
~DependencyScanningWorker();

View File

@ -36,10 +36,8 @@ public:
/// Construct a dependency scanning tool.
///
/// @param Service The parent service. Must outlive the tool.
/// @param FS The filesystem for the tool to use. Defaults to the physical FS.
DependencyScanningTool(dependencies::DependencyScanningService &Service,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
llvm::vfs::createPhysicalFileSystem());
DependencyScanningTool(dependencies::DependencyScanningService &Service)
: Worker(Service) {}
/// Print out the dependency information into a string using the dependency
/// file format that is specified in the options (-MD is the default) and

View File

@ -620,10 +620,9 @@ struct AsyncModuleCompile : PPCallbacks {
if (!LockErr && !Owned)
return;
// We should build the PCM.
// FIXME: Pass the correct BaseFS to the worker FS.
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
Service.getSharedCache(), llvm::vfs::getRealFileSystem());
Service.getSharedCache(), Service.getOpts().MakeVFS());
VFS = createVFSFromCompilerInvocation(CI.getInvocation(),
CI.getDiagnostics(), std::move(VFS));
auto DC = std::make_unique<DiagnosticConsumer>();

View File

@ -14,5 +14,6 @@ using namespace clang;
using namespace dependencies;
DependencyScanningServiceOptions::DependencyScanningServiceOptions()
: BuildSessionTimestamp(
: MakeVFS([] { return llvm::vfs::createPhysicalFileSystem(); }),
BuildSessionTimestamp(
llvm::sys::toTimeT(std::chrono::system_clock::now())) {}

View File

@ -20,8 +20,7 @@ using namespace clang;
using namespace dependencies;
DependencyScanningWorker::DependencyScanningWorker(
DependencyScanningService &Service,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS)
DependencyScanningService &Service)
: Service(Service) {
PCHContainerOps = std::make_shared<PCHContainerOperations>();
// We need to read object files from PCH built outside the scanner.
@ -30,6 +29,8 @@ DependencyScanningWorker::DependencyScanningWorker(
// The scanner itself writes only raw ast files.
PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
auto BaseFS = Service.getOpts().MakeVFS();
if (Service.getOpts().TraceVFS)
BaseFS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(
std::move(BaseFS));

View File

@ -14,7 +14,6 @@
#include "clang/Frontend/Utils.h"
#include "llvm/ADT/SmallVectorExtras.h"
#include "llvm/ADT/iterator.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/TargetParser/Host.h"
#include <optional>
@ -22,11 +21,6 @@ using namespace clang;
using namespace tooling;
using namespace dependencies;
DependencyScanningTool::DependencyScanningTool(
DependencyScanningService &Service,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
: Worker(Service, std::move(FS)) {}
namespace {
/// Prints out all of the gathered dependencies into a string.
class MakeDependencyPrinterConsumer : public DependencyConsumer {

View File

@ -32,9 +32,10 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) {
VFS->addFile(AsmPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
DependencyScanningServiceOptions Opts;
Opts.MakeVFS = [&] { return VFS; };
Opts.Format = ScanningOutputFormat::Make;
DependencyScanningService Service(std::move(Opts));
DependencyScanningWorker Worker(Service, VFS);
DependencyScanningWorker Worker(Service);
llvm::DenseSet<ModuleID> AlreadySeen;
FullDependencyConsumer DC(AlreadySeen);

View File

@ -229,9 +229,10 @@ TEST(DependencyScanner, ScanDepsWithFS) {
llvm::MemoryBuffer::getMemBuffer("#include \"header.h\"\n"));
DependencyScanningServiceOptions Opts;
Opts.MakeVFS = [&] { return VFS; };
Opts.Format = ScanningOutputFormat::Make;
DependencyScanningService Service(std::move(Opts));
DependencyScanningTool ScanTool(Service, VFS);
DependencyScanningTool ScanTool(Service);
TextDiagnosticBuffer DiagConsumer;
std::optional<std::string> DepFile =
@ -287,9 +288,10 @@ TEST(DependencyScanner, ScanDepsWithModuleLookup) {
auto InterceptFS = llvm::makeIntrusiveRefCnt<InterceptorFS>(VFS);
DependencyScanningServiceOptions Opts;
Opts.MakeVFS = [&] { return InterceptFS; };
Opts.Format = ScanningOutputFormat::Make;
DependencyScanningService Service(std::move(Opts));
DependencyScanningTool ScanTool(Service, InterceptFS);
DependencyScanningTool ScanTool(Service);
// This will fail with "fatal error: module 'Foo' not found" but it doesn't
// matter, the point of the test is to check that files are not read