[clang] [clang][deps] Make dependency directives getter thread-safe (PR #136178)
Jan Svoboda via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 22 13:21:43 PDT 2025
https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/136178
>From 51449e05b19f3b0f237305159ae150bc92588fd9 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 11 Apr 2025 14:11:36 -0700
Subject: [PATCH 1/3] [clang][deps] Make dependency directives getter
thread-safe
---
.../include/clang/Frontend/CompilerInstance.h | 8 +++
.../clang/Lex/DependencyDirectivesScanner.h | 6 ++
clang/include/clang/Lex/Preprocessor.h | 10 +++
clang/include/clang/Lex/PreprocessorOptions.h | 13 ----
.../DependencyScanningFilesystem.h | 10 +++
clang/lib/Frontend/CompilerInstance.cpp | 5 ++
clang/lib/Lex/PPLexerChange.cpp | 14 ++---
.../DependencyScanningWorker.cpp | 61 ++++++++++++++++---
.../Lex/PPDependencyDirectivesTest.cpp | 12 ++--
9 files changed, 100 insertions(+), 39 deletions(-)
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 41dc7f1fef461..ecd1f5cabc79e 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -16,6 +16,7 @@
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Frontend/Utils.h"
+#include "clang/Lex/DependencyDirectivesScanner.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/ModuleLoader.h"
#include "llvm/ADT/ArrayRef.h"
@@ -99,6 +100,9 @@ class CompilerInstance : public ModuleLoader {
/// The cache of PCM files.
IntrusiveRefCntPtr<ModuleCache> ModCache;
+ /// Function for getting the dependency preprocessor directives of a file.
+ GetDependencyDirectivesFn GetDependencyDirectives;
+
/// The preprocessor.
std::shared_ptr<Preprocessor> PP;
@@ -697,6 +701,10 @@ class CompilerInstance : public ModuleLoader {
/// and replace any existing one with it.
void createPreprocessor(TranslationUnitKind TUKind);
+ void setDependencyDirectivesGetter(GetDependencyDirectivesFn Fn) {
+ GetDependencyDirectives = Fn;
+ }
+
std::string getSpecificModuleCachePath(StringRef ModuleHash);
std::string getSpecificModuleCachePath() {
return getSpecificModuleCachePath(getInvocation().getModuleHash());
diff --git a/clang/include/clang/Lex/DependencyDirectivesScanner.h b/clang/include/clang/Lex/DependencyDirectivesScanner.h
index 0e115906fbfe5..c975311f8bf33 100644
--- a/clang/include/clang/Lex/DependencyDirectivesScanner.h
+++ b/clang/include/clang/Lex/DependencyDirectivesScanner.h
@@ -21,6 +21,7 @@
#include "llvm/ADT/ArrayRef.h"
namespace clang {
+class FileManager;
namespace tok {
enum TokenKind : unsigned short;
@@ -135,6 +136,11 @@ void printDependencyDirectivesAsSource(
ArrayRef<dependency_directives_scan::Directive> Directives,
llvm::raw_ostream &OS);
+// FIXME: Allow returning an error.
+using GetDependencyDirectivesFn = std::function<
+ std::optional<ArrayRef<dependency_directives_scan::Directive>>(
+ FileManager &, FileEntryRef)>;
+
} // end namespace clang
#endif // LLVM_CLANG_LEX_DEPENDENCYDIRECTIVESSCANNER_H
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 24bb524783e93..8554068b19607 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -140,6 +140,12 @@ class Preprocessor {
friend class VariadicMacroScopeGuard;
llvm::unique_function<void(const clang::Token &)> OnToken;
+ /// Function for getting the dependency preprocessor directives of a file.
+ ///
+ /// These are directives derived from a special form of lexing where the
+ /// source input is scanned for the preprocessor directives that might have an
+ /// effect on the dependencies for a compilation unit.
+ GetDependencyDirectivesFn GetDependencyDirectives;
const PreprocessorOptions &PPOpts;
DiagnosticsEngine *Diags;
const LangOptions &LangOpts;
@@ -1326,6 +1332,10 @@ class Preprocessor {
OnToken = std::move(F);
}
+ void setDependencyDirectivesGetter(GetDependencyDirectivesFn Fn) {
+ GetDependencyDirectives = Fn;
+ }
+
void setPreprocessToken(bool Preprocess) { PreprocessToken = Preprocess; }
bool isMacroDefined(StringRef Id) {
diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h
index c2e3d68333024..d4c4e1ccbf2c4 100644
--- a/clang/include/clang/Lex/PreprocessorOptions.h
+++ b/clang/include/clang/Lex/PreprocessorOptions.h
@@ -189,19 +189,6 @@ class PreprocessorOptions {
/// with support for lifetime-qualified pointers.
ObjCXXARCStandardLibraryKind ObjCXXARCStandardLibrary = ARCXX_nolib;
- /// Function for getting the dependency preprocessor directives of a file.
- ///
- /// These are directives derived from a special form of lexing where the
- /// source input is scanned for the preprocessor directives that might have an
- /// effect on the dependencies for a compilation unit.
- ///
- /// Enables a client to cache the directives for a file and provide them
- /// across multiple compiler invocations.
- /// FIXME: Allow returning an error.
- std::function<std::optional<ArrayRef<dependency_directives_scan::Directive>>(
- FileEntryRef)>
- DependencyDirectivesForFile;
-
/// Set up preprocessor for RunAnalysis action.
bool SetUpStaticAnalyzer = false;
diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h
index d12814e7c9253..f07bd5e820dab 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h
@@ -371,6 +371,16 @@ class DependencyScanningWorkerFilesystem
/// false if not (i.e. this entry is not a file or its scan fails).
bool ensureDirectiveTokensArePopulated(EntryRef Entry);
+ /// \returns The scanned preprocessor directive tokens of the file that are
+ /// used to speed up preprocessing, if available.
+ std::optional<ArrayRef<dependency_directives_scan::Directive>>
+ getDirectiveTokens(const Twine &Path) {
+ if (llvm::ErrorOr<EntryRef> Entry = getOrCreateFileSystemEntry(Path.str()))
+ if (ensureDirectiveTokensArePopulated(*Entry))
+ return Entry->getDirectiveTokens();
+ return std::nullopt;
+ }
+
/// Check whether \p Path exists. By default checks cached result of \c
/// status(), and falls back on FS if unable to do so.
bool exists(const Twine &Path) override;
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 243e0a3c15b05..0efe0ada4873f 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -535,6 +535,9 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
/*ShowAllHeaders=*/true, /*OutputPath=*/"",
/*ShowDepth=*/true, /*MSStyle=*/true);
}
+
+ if (GetDependencyDirectives)
+ PP->setDependencyDirectivesGetter(GetDependencyDirectives);
}
std::string CompilerInstance::getSpecificModuleCachePath(StringRef ModuleHash) {
@@ -1237,6 +1240,8 @@ std::unique_ptr<CompilerInstance> CompilerInstance::cloneForModuleCompileImpl(
// Make a copy for the new instance.
Instance.FailedModules = FailedModules;
+ Instance.GetDependencyDirectives = GetDependencyDirectives;
+
// If we're collecting module dependencies, we need to share a collector
// between all of the module CompilerInstances. Other than that, we don't
// want to produce any dependency output from the module build.
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index a373a52506a24..09ef921f67202 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -92,16 +92,10 @@ bool Preprocessor::EnterSourceFile(FileID FID, ConstSearchDirIterator CurDir,
}
Lexer *TheLexer = new Lexer(FID, *InputFile, *this, IsFirstIncludeOfFile);
- if (getPreprocessorOpts().DependencyDirectivesForFile &&
- FID != PredefinesFileID) {
- if (OptionalFileEntryRef File = SourceMgr.getFileEntryRefForID(FID)) {
- if (std::optional<ArrayRef<dependency_directives_scan::Directive>>
- DepDirectives =
- getPreprocessorOpts().DependencyDirectivesForFile(*File)) {
- TheLexer->DepDirectives = *DepDirectives;
- }
- }
- }
+ if (GetDependencyDirectives && FID != PredefinesFileID)
+ if (OptionalFileEntryRef File = SourceMgr.getFileEntryRefForID(FID))
+ if (auto MaybeDepDirectives = GetDependencyDirectives(FileMgr, *File))
+ TheLexer->DepDirectives = *MaybeDepDirectives;
EnterSourceFileWithLexer(TheLexer, CurDir);
return false;
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index 6595f8ff5dc55..a9e35e0484ac9 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -357,6 +357,57 @@ static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
std::swap(PPOpts.Macros, NewMacros);
}
+static GetDependencyDirectivesFn makeDepDirectivesGetter() {
+ /// This is a functor that conforms to \c GetDependencyDirectivesFn.
+ /// It ensures it's always invoked with the same \c FileManager and caches the
+ /// extraction of the scanning VFS for better performance.
+ struct DepDirectivesGetter {
+ DepDirectivesGetter() : DepFS(nullptr), FM(nullptr) {}
+
+ /// It's important copies do not carry over the cached members. The copies
+ /// are likely to be used from distinct \c CompilerInstance objects with
+ /// distinct \c FileManager \c llvm::vfs::FileSystem.
+ DepDirectivesGetter(const DepDirectivesGetter &)
+ : DepFS(nullptr), FM(nullptr) {}
+ DepDirectivesGetter &operator=(const DepDirectivesGetter &) {
+ DepFS = nullptr;
+ FM = nullptr;
+ return *this;
+ }
+
+ auto operator()(FileManager &FileMgr, FileEntryRef File) {
+ ensureConsistentFileManager(FileMgr);
+ ensurePopulatedFileSystem(FileMgr);
+ return DepFS->getDirectiveTokens(File.getName());
+ }
+
+ private:
+ DependencyScanningWorkerFilesystem *DepFS;
+ FileManager *FM;
+
+ void ensureConsistentFileManager(FileManager &FileMgr) {
+ if (!FM)
+ FM = &FileMgr;
+ assert(&FileMgr == FM);
+ }
+
+ void ensurePopulatedFileSystem(FileManager &FM) {
+ if (DepFS)
+ return;
+ FM.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");
+ }
+ };
+
+ return DepDirectivesGetter{};
+}
+
/// A clang tool that runs the preprocessor in a mode that's optimized for
/// dependency scanning for the given compiler invocation.
class DependencyScanningAction : public tooling::ToolAction {
@@ -433,15 +484,7 @@ class DependencyScanningAction : public tooling::ToolAction {
if (!ModulesCachePath.empty())
DepFS->setBypassedPathPrefix(ModulesCachePath);
- ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
- [LocalDepFS = DepFS](FileEntryRef File)
- -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
- if (llvm::ErrorOr<EntryRef> Entry =
- LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
- if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
- return Entry->getDirectiveTokens();
- return std::nullopt;
- };
+ ScanInstance.setDependencyDirectivesGetter(makeDepDirectivesGetter());
}
// Create a new FileManager to match the invocation's FileSystemOptions.
diff --git a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
index 03f1432d990cb..c52d5091eabfb 100644
--- a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
+++ b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
@@ -105,8 +105,7 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
};
SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;
- auto getDependencyDirectives = [&](FileEntryRef File)
- -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
+ auto GetDependencyDirectives = [&](FileManager &FileMgr, FileEntryRef File) {
DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());
StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();
bool Err = scanSourceForDependencyDirectives(
@@ -117,11 +116,6 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
};
PreprocessorOptions PPOpts;
- PPOpts.DependencyDirectivesForFile = [&](FileEntryRef File)
- -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
- return getDependencyDirectives(File);
- };
-
HeaderSearchOptions HSOpts;
TrivialModuleLoader ModLoader;
HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get());
@@ -130,6 +124,10 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
+ PP.setDependencyDirectivesGetter([&](FileManager &FM, FileEntryRef File) {
+ return GetDependencyDirectives(FM, File);
+ });
+
SmallVector<StringRef> IncludedFiles;
PP.addPPCallbacks(std::make_unique<IncludeCollector>(PP, IncludedFiles));
PP.EnterMainSourceFile();
>From 00099ccf341ba443e356b514a4f81dae2b01e0bd Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Thu, 17 Apr 2025 11:44:01 -0700
Subject: [PATCH 2/3] Simplify test
---
clang/unittests/Lex/PPDependencyDirectivesTest.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
index c52d5091eabfb..5f4c2706a519d 100644
--- a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
+++ b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
@@ -124,9 +124,7 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
- PP.setDependencyDirectivesGetter([&](FileManager &FM, FileEntryRef File) {
- return GetDependencyDirectives(FM, File);
- });
+ PP.setDependencyDirectivesGetter(GetDependencyDirectives);
SmallVector<StringRef> IncludedFiles;
PP.addPPCallbacks(std::make_unique<IncludeCollector>(PP, IncludedFiles));
>From 105e148b597907b9ef21c492f401c10725cf49e2 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Tue, 22 Apr 2025 13:21:28 -0700
Subject: [PATCH 3/3] Interface with explicit `cloneFor(FileManager&)` API
---
.../include/clang/Frontend/CompilerInstance.h | 9 ++-
.../clang/Lex/DependencyDirectivesScanner.h | 18 +++--
clang/include/clang/Lex/Preprocessor.h | 8 +-
clang/lib/Frontend/CompilerInstance.cpp | 6 +-
clang/lib/Lex/PPLexerChange.cpp | 2 +-
.../DependencyScanningWorker.cpp | 77 +++++++------------
.../Lex/PPDependencyDirectivesTest.cpp | 34 +++++---
7 files changed, 78 insertions(+), 76 deletions(-)
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index ecd1f5cabc79e..078044ed2d039 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -100,8 +100,8 @@ class CompilerInstance : public ModuleLoader {
/// The cache of PCM files.
IntrusiveRefCntPtr<ModuleCache> ModCache;
- /// Function for getting the dependency preprocessor directives of a file.
- GetDependencyDirectivesFn GetDependencyDirectives;
+ /// Functor for getting the dependency preprocessor directives of a file.
+ std::unique_ptr<DependencyDirectivesGetter> GetDependencyDirectives;
/// The preprocessor.
std::shared_ptr<Preprocessor> PP;
@@ -701,8 +701,9 @@ class CompilerInstance : public ModuleLoader {
/// and replace any existing one with it.
void createPreprocessor(TranslationUnitKind TUKind);
- void setDependencyDirectivesGetter(GetDependencyDirectivesFn Fn) {
- GetDependencyDirectives = Fn;
+ void setDependencyDirectivesGetter(
+ std::unique_ptr<DependencyDirectivesGetter> Getter) {
+ GetDependencyDirectives = std::move(Getter);
}
std::string getSpecificModuleCachePath(StringRef ModuleHash);
diff --git a/clang/include/clang/Lex/DependencyDirectivesScanner.h b/clang/include/clang/Lex/DependencyDirectivesScanner.h
index c975311f8bf33..acdc9e2bf9aa4 100644
--- a/clang/include/clang/Lex/DependencyDirectivesScanner.h
+++ b/clang/include/clang/Lex/DependencyDirectivesScanner.h
@@ -136,11 +136,19 @@ void printDependencyDirectivesAsSource(
ArrayRef<dependency_directives_scan::Directive> Directives,
llvm::raw_ostream &OS);
-// FIXME: Allow returning an error.
-using GetDependencyDirectivesFn = std::function<
- std::optional<ArrayRef<dependency_directives_scan::Directive>>(
- FileManager &, FileEntryRef)>;
-
+/// Functor that returns the dependency directives for a given file.
+class DependencyDirectivesGetter {
+public:
+ /// Clone the getter for a new \c FileManager instance.
+ virtual std::unique_ptr<DependencyDirectivesGetter>
+ cloneFor(FileManager &FileMgr) = 0;
+
+ /// Get the dependency directives for the given file.
+ virtual std::optional<ArrayRef<dependency_directives_scan::Directive>>
+ operator()(FileEntryRef File) = 0;
+
+ virtual ~DependencyDirectivesGetter() = default;
+};
} // end namespace clang
#endif // LLVM_CLANG_LEX_DEPENDENCYDIRECTIVESSCANNER_H
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 8554068b19607..3ec02a754138f 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -140,12 +140,12 @@ class Preprocessor {
friend class VariadicMacroScopeGuard;
llvm::unique_function<void(const clang::Token &)> OnToken;
- /// Function for getting the dependency preprocessor directives of a file.
+ /// Functor for getting the dependency preprocessor directives of a file.
///
/// These are directives derived from a special form of lexing where the
/// source input is scanned for the preprocessor directives that might have an
/// effect on the dependencies for a compilation unit.
- GetDependencyDirectivesFn GetDependencyDirectives;
+ DependencyDirectivesGetter *GetDependencyDirectives = nullptr;
const PreprocessorOptions &PPOpts;
DiagnosticsEngine *Diags;
const LangOptions &LangOpts;
@@ -1332,8 +1332,8 @@ class Preprocessor {
OnToken = std::move(F);
}
- void setDependencyDirectivesGetter(GetDependencyDirectivesFn Fn) {
- GetDependencyDirectives = Fn;
+ void setDependencyDirectivesGetter(DependencyDirectivesGetter &Get) {
+ GetDependencyDirectives = &Get;
}
void setPreprocessToken(bool Preprocess) { PreprocessToken = Preprocess; }
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 0efe0ada4873f..9f27224ddd6f0 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -537,7 +537,7 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
}
if (GetDependencyDirectives)
- PP->setDependencyDirectivesGetter(GetDependencyDirectives);
+ PP->setDependencyDirectivesGetter(*GetDependencyDirectives);
}
std::string CompilerInstance::getSpecificModuleCachePath(StringRef ModuleHash) {
@@ -1240,7 +1240,9 @@ std::unique_ptr<CompilerInstance> CompilerInstance::cloneForModuleCompileImpl(
// Make a copy for the new instance.
Instance.FailedModules = FailedModules;
- Instance.GetDependencyDirectives = GetDependencyDirectives;
+ if (GetDependencyDirectives)
+ Instance.GetDependencyDirectives =
+ GetDependencyDirectives->cloneFor(Instance.getFileManager());
// If we're collecting module dependencies, we need to share a collector
// between all of the module CompilerInstances. Other than that, we don't
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index 09ef921f67202..f95e38317a872 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -94,7 +94,7 @@ bool Preprocessor::EnterSourceFile(FileID FID, ConstSearchDirIterator CurDir,
Lexer *TheLexer = new Lexer(FID, *InputFile, *this, IsFirstIncludeOfFile);
if (GetDependencyDirectives && FID != PredefinesFileID)
if (OptionalFileEntryRef File = SourceMgr.getFileEntryRefForID(FID))
- if (auto MaybeDepDirectives = GetDependencyDirectives(FileMgr, *File))
+ if (auto MaybeDepDirectives = (*GetDependencyDirectives)(*File))
TheLexer->DepDirectives = *MaybeDepDirectives;
EnterSourceFileWithLexer(TheLexer, CurDir);
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index a9e35e0484ac9..ef38c7c9defee 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -357,56 +357,31 @@ static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
std::swap(PPOpts.Macros, NewMacros);
}
-static GetDependencyDirectivesFn makeDepDirectivesGetter() {
- /// This is a functor that conforms to \c GetDependencyDirectivesFn.
- /// It ensures it's always invoked with the same \c FileManager and caches the
- /// extraction of the scanning VFS for better performance.
- struct DepDirectivesGetter {
- DepDirectivesGetter() : DepFS(nullptr), FM(nullptr) {}
-
- /// It's important copies do not carry over the cached members. The copies
- /// are likely to be used from distinct \c CompilerInstance objects with
- /// distinct \c FileManager \c llvm::vfs::FileSystem.
- DepDirectivesGetter(const DepDirectivesGetter &)
- : DepFS(nullptr), FM(nullptr) {}
- DepDirectivesGetter &operator=(const DepDirectivesGetter &) {
- DepFS = nullptr;
- FM = nullptr;
- return *this;
- }
-
- auto operator()(FileManager &FileMgr, FileEntryRef File) {
- ensureConsistentFileManager(FileMgr);
- ensurePopulatedFileSystem(FileMgr);
- return DepFS->getDirectiveTokens(File.getName());
- }
+class ActualDependencyDirectivesGetter : public DependencyDirectivesGetter {
+ DependencyScanningWorkerFilesystem *DepFS;
- private:
- DependencyScanningWorkerFilesystem *DepFS;
- FileManager *FM;
-
- void ensureConsistentFileManager(FileManager &FileMgr) {
- if (!FM)
- FM = &FileMgr;
- assert(&FileMgr == FM);
- }
+public:
+ ActualDependencyDirectivesGetter(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");
+ }
- void ensurePopulatedFileSystem(FileManager &FM) {
- if (DepFS)
- return;
- FM.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<ActualDependencyDirectivesGetter>(FileMgr);
+ }
- return DepDirectivesGetter{};
-}
+ std::optional<ArrayRef<dependency_directives_scan::Directive>>
+ operator()(FileEntryRef File) override {
+ return DepFS->getDirectiveTokens(File.getName());
+ }
+};
/// A clang tool that runs the preprocessor in a mode that's optimized for
/// dependency scanning for the given compiler invocation.
@@ -475,6 +450,9 @@ class DependencyScanningAction : public tooling::ToolAction {
ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
DriverFileMgr->getVirtualFileSystemPtr());
+ // Create a new FileManager to match the invocation's FileSystemOptions.
+ auto *FileMgr = ScanInstance.createFileManager(FS);
+
// Use the dependency scanning optimized file system if requested to do so.
if (DepFS) {
StringRef ModulesCachePath =
@@ -484,11 +462,10 @@ class DependencyScanningAction : public tooling::ToolAction {
if (!ModulesCachePath.empty())
DepFS->setBypassedPathPrefix(ModulesCachePath);
- ScanInstance.setDependencyDirectivesGetter(makeDepDirectivesGetter());
+ ScanInstance.setDependencyDirectivesGetter(
+ std::make_unique<ActualDependencyDirectivesGetter>(*FileMgr));
}
- // Create a new FileManager to match the invocation's FileSystemOptions.
- auto *FileMgr = ScanInstance.createFileManager(FS);
ScanInstance.createSourceManager(*FileMgr);
// Store a mapping of prebuilt module files and their properties like header
diff --git a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
index 5f4c2706a519d..6ab80ba01677e 100644
--- a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
+++ b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
@@ -103,17 +103,31 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
SmallVector<dependency_directives_scan::Token> Tokens;
SmallVector<dependency_directives_scan::Directive> Directives;
};
- SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;
-
- auto GetDependencyDirectives = [&](FileManager &FileMgr, FileEntryRef File) {
- DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());
- StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();
- bool Err = scanSourceForDependencyDirectives(
- Input, DepDirectivesObjects.back()->Tokens,
- DepDirectivesObjects.back()->Directives);
- EXPECT_FALSE(Err);
- return llvm::ArrayRef(DepDirectivesObjects.back()->Directives);
+
+ class TestDependencyDirectivesGetter : public DependencyDirectivesGetter {
+ FileManager &FileMgr;
+ SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;
+
+ public:
+ TestDependencyDirectivesGetter(FileManager &FileMgr) : FileMgr(FileMgr) {}
+
+ std::unique_ptr<DependencyDirectivesGetter>
+ cloneFor(FileManager &FileMgr) override {
+ return std::make_unique<TestDependencyDirectivesGetter>(FileMgr);
+ }
+
+ std::optional<ArrayRef<dependency_directives_scan::Directive>>
+ operator()(FileEntryRef File) override {
+ DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());
+ StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();
+ bool Err = scanSourceForDependencyDirectives(
+ Input, DepDirectivesObjects.back()->Tokens,
+ DepDirectivesObjects.back()->Directives);
+ EXPECT_FALSE(Err);
+ return DepDirectivesObjects.back()->Directives;
+ }
};
+ TestDependencyDirectivesGetter GetDependencyDirectives(FileMgr);
PreprocessorOptions PPOpts;
HeaderSearchOptions HSOpts;
More information about the cfe-commits
mailing list