Skip to content

Commit 2eabcea

Browse files
committed
[clang][ScanDeps] Allow PCHs to have different VFS overlays
It turns out it's not that uncommon for real code to pass a different set of VFSs while building a PCH than while using the PCH. This can cause problems as seen in `test/ClangScanDeps/optimize-vfs-pch.m`. If you scan `compile-commands-tu-no-vfs-error.json` without -Werror and run the resulting commands, Clang will emit a fatal error while trying to emit a note saying that it can't find a remapped header. This also adds textual tracking of VFSs for prebuilt modules that are part of an included PCH, as the same issue can occur in a module we are building if we drop VFSs. This has to be textual because we have no guarantee the PCH had the same list of VFSs as the current TU.
1 parent 052e2e7 commit 2eabcea

File tree

6 files changed

+191
-31
lines changed

6 files changed

+191
-31
lines changed

clang/include/clang/Basic/DiagnosticSerializationKinds.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ def err_pch_diagopt_mismatch : Error<"%0 is currently enabled, but was not in "
4444
"the PCH file">;
4545
def err_pch_modulecache_mismatch : Error<"PCH was compiled with module cache "
4646
"path '%0', but the path is currently '%1'">;
47-
def err_pch_vfsoverlay_mismatch : Error<"PCH was compiled with different VFS overlay files than are currently in use">;
47+
def warn_pch_vfsoverlay_mismatch : Warning<
48+
"PCH was compiled with different VFS overlay files than are currently in use">,
49+
InGroup<DiagGroup<"pch-vfs-diff">>;
4850
def note_pch_vfsoverlay_files : Note<"%select{PCH|current translation unit}0 has the following VFS overlays:\n%1">;
4951
def note_pch_vfsoverlay_empty : Note<"%select{PCH|current translation unit}0 has no VFS overlays">;
5052

clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ struct ModuleDeps {
161161
BuildInfo;
162162
};
163163

164+
using PrebuiltModuleVFSMapT = llvm::StringMap<llvm::StringSet<>>;
165+
164166
class ModuleDepCollector;
165167

166168
/// Callback that records textual includes and direct modular includes/imports
@@ -226,6 +228,7 @@ class ModuleDepCollector final : public DependencyCollector {
226228
CompilerInstance &ScanInstance, DependencyConsumer &C,
227229
DependencyActionController &Controller,
228230
CompilerInvocation OriginalCI,
231+
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap,
229232
ScanningOptimizations OptimizeArgs, bool EagerLoadModules,
230233
bool IsStdModuleP1689Format);
231234

@@ -245,6 +248,8 @@ class ModuleDepCollector final : public DependencyCollector {
245248
DependencyConsumer &Consumer;
246249
/// Callbacks for computing dependency information.
247250
DependencyActionController &Controller;
251+
/// Mapping from prebuilt AST files to their sorted list of VFS overlay files.
252+
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
248253
/// Path to the main source file.
249254
std::string MainFile;
250255
/// Hash identifying the compilation conditions of the current TU.

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
7777
const HeaderSearchOptions &ExistingHSOpts,
7878
DiagnosticsEngine *Diags,
7979
const LangOptions &LangOpts) {
80-
if (LangOpts.Modules) {
80+
if (LangOpts.Modules && ExistingHSOpts.ModulesIncludeVFSUsage) {
8181
if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {
8282
if (Diags) {
83-
Diags->Report(diag::err_pch_vfsoverlay_mismatch);
83+
Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
8484
auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {
8585
if (VFSOverlays.empty()) {
8686
Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
@@ -92,7 +92,6 @@ static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
9292
VFSNote(0, HSOpts.VFSOverlayFiles);
9393
VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
9494
}
95-
return true;
9695
}
9796
}
9897
return false;
@@ -107,9 +106,11 @@ class PrebuiltModuleListener : public ASTReaderListener {
107106
PrebuiltModuleListener(CompilerInstance &CI,
108107
PrebuiltModuleFilesT &PrebuiltModuleFiles,
109108
llvm::SmallVector<std::string> &NewModuleFiles,
109+
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
110110
DiagnosticsEngine &Diags)
111111
: CI(CI), PrebuiltModuleFiles(PrebuiltModuleFiles),
112-
NewModuleFiles(NewModuleFiles), Diags(Diags) {}
112+
NewModuleFiles(NewModuleFiles),
113+
PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), Diags(Diags) {}
113114

114115
bool needsImportVisitation() const override { return true; }
115116

@@ -118,10 +119,19 @@ class PrebuiltModuleListener : public ASTReaderListener {
118119
NewModuleFiles.push_back(Filename.str());
119120
}
120121

122+
void visitModuleFile(StringRef Filename,
123+
serialization::ModuleKind Kind) override {
124+
CurrentFile = Filename;
125+
}
126+
121127
bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
122128
bool Complain) override {
123-
return checkHeaderSearchPaths(
124-
HSOpts, CI.getHeaderSearchOpts(), Complain ? &Diags : nullptr, CI.getLangOpts());
129+
std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;
130+
PrebuiltModuleVFSMap.insert(
131+
{CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});
132+
return checkHeaderSearchPaths(HSOpts, CI.getHeaderSearchOpts(),
133+
Complain ? &Diags : nullptr,
134+
CI.getLangOpts());
125135
}
126136

127137
bool readModuleCacheKey(StringRef ModuleName, StringRef Filename,
@@ -136,19 +146,25 @@ class PrebuiltModuleListener : public ASTReaderListener {
136146
CompilerInstance &CI;
137147
PrebuiltModuleFilesT &PrebuiltModuleFiles;
138148
llvm::SmallVector<std::string> &NewModuleFiles;
149+
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap;
139150
DiagnosticsEngine &Diags;
151+
std::string CurrentFile;
140152
};
141153

142154
/// Visit the given prebuilt module and collect all of the modules it
143155
/// transitively imports and contributing input files.
144156
static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
145157
CompilerInstance &CI,
146158
PrebuiltModuleFilesT &ModuleFiles,
159+
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
147160
DiagnosticsEngine &Diags) {
148161
// List of module files to be processed.
149162
llvm::SmallVector<std::string> Worklist;
150-
PrebuiltModuleListener Listener(CI, ModuleFiles, Worklist, Diags);
163+
PrebuiltModuleListener Listener(CI, ModuleFiles, Worklist,
164+
PrebuiltModuleVFSMap, Diags);
151165

166+
Listener.visitModuleFile(PrebuiltModuleFilename,
167+
serialization::MK_ExplicitModule);
152168
if (ASTReader::readASTFileControlBlock(
153169
PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
154170
CI.getPCHContainerReader(),
@@ -157,6 +173,7 @@ static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
157173
return true;
158174

159175
while (!Worklist.empty()) {
176+
Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule);
160177
if (ASTReader::readASTFileControlBlock(
161178
Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
162179
CI.getPCHContainerReader(),
@@ -193,8 +210,18 @@ static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
193210
DiagOpts.ShowCarets = false;
194211
// Don't write out diagnostic file.
195212
DiagOpts.DiagnosticSerializationFile.clear();
196-
// Don't emit warnings as errors (and all other warnings too).
197-
DiagOpts.IgnoreWarnings = true;
213+
// Don't emit warnings except for scanning specific warnings.
214+
// TODO: It would be useful to add a more principled way to ignore all
215+
// warnings that come from source code. The issue is that we need to
216+
// ignore warnings that could be surpressed by
217+
// `#pragma clang diagnostic`, while still allowing some scanning
218+
// warnings for things we're not ready to turn into errors yet.
219+
// See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.
220+
llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {
221+
return llvm::StringSwitch<bool>(Warning)
222+
.Cases("pch-vfs-diff", "error=pch-vfs-diff", false)
223+
.Default(true);
224+
});
198225
}
199226

200227
// Clang implements -D and -U by splatting text into a predefines buffer. This
@@ -435,14 +462,19 @@ class DependencyScanningAction : public tooling::ToolAction {
435462
if (VerboseOS)
436463
ScanInstance.setVerboseOutputStream(*VerboseOS);
437464

465+
// Some DiagnosticConsumers require that finish() is called.
466+
auto DiagConsumerFinisher =
467+
llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });
468+
438469
ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
439470
true;
440471

441472
ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
442473
ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
443474
ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
444475
ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
445-
ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage = true;
476+
ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =
477+
any(OptimizeArgs & ScanningOptimizations::VFS);
446478

447479
ScanInstance.setFileManager(FileMgr);
448480
// Support for virtual file system overlays.
@@ -455,12 +487,13 @@ class DependencyScanningAction : public tooling::ToolAction {
455487
// Store the list of prebuilt module files into header search options. This
456488
// will prevent the implicit build to create duplicate modules and will
457489
// force reuse of the existing prebuilt module files instead.
490+
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
458491
if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
459492
if (visitPrebuiltModule(
460493
ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,
461494
ScanInstance,
462495
ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
463-
ScanInstance.getDiagnostics()))
496+
PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
464497
return false;
465498

466499
// Use the dependency scanning optimized file system if requested to do so.
@@ -544,8 +577,8 @@ class DependencyScanningAction : public tooling::ToolAction {
544577

545578
MDC = std::make_shared<ModuleDepCollector>(
546579
std::move(Opts), ScanInstance, Consumer, Controller,
547-
OriginalInvocation, OptimizeArgs, EagerLoadModules,
548-
Format == ScanningOutputFormat::P1689);
580+
OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
581+
EagerLoadModules, Format == ScanningOutputFormat::P1689);
549582
ScanInstance.addDependencyCollector(MDC);
550583
ScanInstance.setGenModuleActionWrapper(
551584
[&Controller = Controller](const FrontendOptions &Opts,
@@ -587,6 +620,8 @@ class DependencyScanningAction : public tooling::ToolAction {
587620
if (ScanInstance.getDiagnostics().hasErrorOccurred())
588621
return false;
589622

623+
// Each action is responsible for calling finish.
624+
DiagConsumerFinisher.release();
590625
if (!ScanInstance.ExecuteAction(*Action))
591626
return false;
592627

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ const std::vector<std::string> &ModuleDeps::getBuildArguments() {
3434
return std::get<std::vector<std::string>>(BuildInfo);
3535
}
3636

37-
static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
38-
ASTReader &Reader,
39-
const serialization::ModuleFile &MF,
40-
ScanningOptimizations OptimizeArgs) {
37+
static void
38+
optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader,
39+
const serialization::ModuleFile &MF,
40+
const PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
41+
ScanningOptimizations OptimizeArgs) {
4142
if (any(OptimizeArgs & ScanningOptimizations::HeaderSearch)) {
4243
// Only preserve search paths that were used during the dependency scan.
4344
std::vector<HeaderSearchOptions::Entry> Entries;
@@ -70,11 +71,25 @@ static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
7071
llvm::DenseSet<const serialization::ModuleFile *> Visited;
7172
std::function<void(const serialization::ModuleFile *)> VisitMF =
7273
[&](const serialization::ModuleFile *MF) {
73-
VFSUsage |= MF->VFSUsage;
7474
Visited.insert(MF);
75-
for (const serialization::ModuleFile *Import : MF->Imports)
76-
if (!Visited.contains(Import))
77-
VisitMF(Import);
75+
if (MF->Kind == serialization::MK_ImplicitModule) {
76+
VFSUsage |= MF->VFSUsage;
77+
// We only need to recurse into implicit modules. Other module types
78+
// will have the correct set of VFSs for anything they depend on.
79+
for (const serialization::ModuleFile *Import : MF->Imports)
80+
if (!Visited.contains(Import))
81+
VisitMF(Import);
82+
} else {
83+
// This is not an implicitly built module, so it may have different
84+
// VFS options. Fall back to a string comparison instead.
85+
auto VFSMap = PrebuiltModuleVFSMap.find(MF->FileName);
86+
if (VFSMap == PrebuiltModuleVFSMap.end())
87+
return;
88+
for (std::size_t I = 0, E = VFSOverlayFiles.size(); I != E; ++I) {
89+
if (VFSMap->second.contains(VFSOverlayFiles[I]))
90+
VFSUsage[I] = true;
91+
}
92+
}
7893
};
7994
VisitMF(&MF);
8095

@@ -682,6 +697,7 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
682697
ScanningOptimizations::VFS)))
683698
optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(),
684699
*MDC.ScanInstance.getASTReader(), *MF,
700+
MDC.PrebuiltModuleVFSMap,
685701
MDC.OptimizeArgs);
686702
if (any(MDC.OptimizeArgs & ScanningOptimizations::SystemWarnings))
687703
optimizeDiagnosticOpts(
@@ -805,9 +821,11 @@ ModuleDepCollector::ModuleDepCollector(
805821
std::unique_ptr<DependencyOutputOptions> Opts,
806822
CompilerInstance &ScanInstance, DependencyConsumer &C,
807823
DependencyActionController &Controller, CompilerInvocation OriginalCI,
824+
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap,
808825
ScanningOptimizations OptimizeArgs, bool EagerLoadModules,
809826
bool IsStdModuleP1689Format)
810827
: ScanInstance(ScanInstance), Consumer(C), Controller(Controller),
828+
PrebuiltModuleVFSMap(std::move(PrebuiltModuleVFSMap)),
811829
Opts(std::move(Opts)),
812830
CommonInvocation(
813831
makeCommonInvocationForModuleBuild(std::move(OriginalCI))),

0 commit comments

Comments
 (0)