1a7dea167SDimitry Andric //===- DependencyScanningFilesystem.cpp - clang-scan-deps fs --------------===//
2a7dea167SDimitry Andric //
3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7dea167SDimitry Andric //
7a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
8a7dea167SDimitry Andric 
9a7dea167SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
10a7dea167SDimitry Andric #include "clang/Lex/DependencyDirectivesSourceMinimizer.h"
11a7dea167SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
120eae32dcSDimitry Andric #include "llvm/Support/SmallVectorMemoryBuffer.h"
13a7dea167SDimitry Andric #include "llvm/Support/Threading.h"
14a7dea167SDimitry Andric 
15a7dea167SDimitry Andric using namespace clang;
16a7dea167SDimitry Andric using namespace tooling;
17a7dea167SDimitry Andric using namespace dependencies;
18a7dea167SDimitry Andric 
19*04eeddc0SDimitry Andric llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry>
20*04eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
21a7dea167SDimitry Andric   // Load the file and its content from the file system.
22*04eeddc0SDimitry Andric   auto MaybeFile = getUnderlyingFS().openFileForRead(Filename);
23a7dea167SDimitry Andric   if (!MaybeFile)
24a7dea167SDimitry Andric     return MaybeFile.getError();
250eae32dcSDimitry Andric   auto File = std::move(*MaybeFile);
26a7dea167SDimitry Andric 
270eae32dcSDimitry Andric   auto MaybeStat = File->status();
280eae32dcSDimitry Andric   if (!MaybeStat)
290eae32dcSDimitry Andric     return MaybeStat.getError();
300eae32dcSDimitry Andric   auto Stat = std::move(*MaybeStat);
310eae32dcSDimitry Andric 
320eae32dcSDimitry Andric   auto MaybeBuffer = File->getBuffer(Stat.getName());
33a7dea167SDimitry Andric   if (!MaybeBuffer)
34a7dea167SDimitry Andric     return MaybeBuffer.getError();
350eae32dcSDimitry Andric   auto Buffer = std::move(*MaybeBuffer);
360eae32dcSDimitry Andric 
37*04eeddc0SDimitry Andric   // If the file size changed between read and stat, pretend it didn't.
38*04eeddc0SDimitry Andric   if (Stat.getSize() != Buffer->getBufferSize())
39*04eeddc0SDimitry Andric     Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize());
40*04eeddc0SDimitry Andric 
41*04eeddc0SDimitry Andric   return TentativeEntry(Stat, std::move(Buffer));
420eae32dcSDimitry Andric }
430eae32dcSDimitry Andric 
44*04eeddc0SDimitry Andric EntryRef DependencyScanningWorkerFilesystem::minimizeIfNecessary(
45*04eeddc0SDimitry Andric     const CachedFileSystemEntry &Entry, StringRef Filename, bool Disable) {
46*04eeddc0SDimitry Andric   if (Entry.isError() || Entry.isDirectory() || Disable ||
47*04eeddc0SDimitry Andric       !shouldMinimize(Filename, Entry.getUniqueID()))
48*04eeddc0SDimitry Andric     return EntryRef(/*Minimized=*/false, Filename, Entry);
49*04eeddc0SDimitry Andric 
50*04eeddc0SDimitry Andric   CachedFileContents *Contents = Entry.getContents();
51*04eeddc0SDimitry Andric   assert(Contents && "contents not initialized");
52*04eeddc0SDimitry Andric 
53*04eeddc0SDimitry Andric   // Double-checked locking.
54*04eeddc0SDimitry Andric   if (Contents->MinimizedAccess.load())
55*04eeddc0SDimitry Andric     return EntryRef(/*Minimized=*/true, Filename, Entry);
56*04eeddc0SDimitry Andric 
57*04eeddc0SDimitry Andric   std::lock_guard<std::mutex> GuardLock(Contents->ValueLock);
58*04eeddc0SDimitry Andric 
59*04eeddc0SDimitry Andric   // Double-checked locking.
60*04eeddc0SDimitry Andric   if (Contents->MinimizedAccess.load())
61*04eeddc0SDimitry Andric     return EntryRef(/*Minimized=*/true, Filename, Entry);
62a7dea167SDimitry Andric 
63a7dea167SDimitry Andric   llvm::SmallString<1024> MinimizedFileContents;
64a7dea167SDimitry Andric   // Minimize the file down to directives that might affect the dependencies.
65a7dea167SDimitry Andric   SmallVector<minimize_source_to_dependency_directives::Token, 64> Tokens;
66*04eeddc0SDimitry Andric   if (minimizeSourceToDependencyDirectives(Contents->Original->getBuffer(),
670eae32dcSDimitry Andric                                            MinimizedFileContents, Tokens)) {
680eae32dcSDimitry Andric     // FIXME: Propagate the diagnostic if desired by the client.
690eae32dcSDimitry Andric     // Use the original file if the minimization failed.
70*04eeddc0SDimitry Andric     Contents->MinimizedStorage =
71*04eeddc0SDimitry Andric         llvm::MemoryBuffer::getMemBuffer(*Contents->Original);
72*04eeddc0SDimitry Andric     Contents->MinimizedAccess.store(Contents->MinimizedStorage.get());
73*04eeddc0SDimitry Andric     return EntryRef(/*Minimized=*/true, Filename, Entry);
74a7dea167SDimitry Andric   }
75a7dea167SDimitry Andric 
76a7dea167SDimitry Andric   // The contents produced by the minimizer must be null terminated.
77a7dea167SDimitry Andric   assert(MinimizedFileContents.data()[MinimizedFileContents.size()] == '\0' &&
78a7dea167SDimitry Andric          "not null terminated contents");
79a7dea167SDimitry Andric 
80a7dea167SDimitry Andric   // Compute the skipped PP ranges that speedup skipping over inactive
81a7dea167SDimitry Andric   // preprocessor blocks.
82a7dea167SDimitry Andric   llvm::SmallVector<minimize_source_to_dependency_directives::SkippedRange, 32>
83a7dea167SDimitry Andric       SkippedRanges;
84a7dea167SDimitry Andric   minimize_source_to_dependency_directives::computeSkippedRanges(Tokens,
85a7dea167SDimitry Andric                                                                  SkippedRanges);
86a7dea167SDimitry Andric   PreprocessorSkippedRangeMapping Mapping;
87a7dea167SDimitry Andric   for (const auto &Range : SkippedRanges) {
88a7dea167SDimitry Andric     if (Range.Length < 16) {
89a7dea167SDimitry Andric       // Ignore small ranges as non-profitable.
90a7dea167SDimitry Andric       // FIXME: This is a heuristic, its worth investigating the tradeoffs
91a7dea167SDimitry Andric       // when it should be applied.
92a7dea167SDimitry Andric       continue;
93a7dea167SDimitry Andric     }
94a7dea167SDimitry Andric     Mapping[Range.Offset] = Range.Length;
95a7dea167SDimitry Andric   }
96*04eeddc0SDimitry Andric   Contents->PPSkippedRangeMapping = std::move(Mapping);
97a7dea167SDimitry Andric 
98*04eeddc0SDimitry Andric   Contents->MinimizedStorage = std::make_unique<llvm::SmallVectorMemoryBuffer>(
990eae32dcSDimitry Andric       std::move(MinimizedFileContents));
100*04eeddc0SDimitry Andric   // This function performed double-checked locking using `MinimizedAccess`.
101*04eeddc0SDimitry Andric   // Assigning it must be the last thing this function does. If we were to
102*04eeddc0SDimitry Andric   // assign it before `PPSkippedRangeMapping`, other threads may skip the
103*04eeddc0SDimitry Andric   // critical section (`MinimizedAccess != nullptr`) and access the mappings
104*04eeddc0SDimitry Andric   // that are about to be initialized, leading to a data race.
105*04eeddc0SDimitry Andric   Contents->MinimizedAccess.store(Contents->MinimizedStorage.get());
106*04eeddc0SDimitry Andric   return EntryRef(/*Minimized=*/true, Filename, Entry);
107a7dea167SDimitry Andric }
108a7dea167SDimitry Andric 
1090eae32dcSDimitry Andric DependencyScanningFilesystemSharedCache::
1100eae32dcSDimitry Andric     DependencyScanningFilesystemSharedCache() {
111a7dea167SDimitry Andric   // This heuristic was chosen using a empirical testing on a
112a7dea167SDimitry Andric   // reasonably high core machine (iMacPro 18 cores / 36 threads). The cache
113a7dea167SDimitry Andric   // sharding gives a performance edge by reducing the lock contention.
114a7dea167SDimitry Andric   // FIXME: A better heuristic might also consider the OS to account for
115a7dea167SDimitry Andric   // the different cost of lock contention on different OSes.
1165ffd83dbSDimitry Andric   NumShards =
1175ffd83dbSDimitry Andric       std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4);
118a7dea167SDimitry Andric   CacheShards = std::make_unique<CacheShard[]>(NumShards);
119a7dea167SDimitry Andric }
120a7dea167SDimitry Andric 
121*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard &
122*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::getShardForFilename(
123*04eeddc0SDimitry Andric     StringRef Filename) const {
124*04eeddc0SDimitry Andric   return CacheShards[llvm::hash_value(Filename) % NumShards];
125*04eeddc0SDimitry Andric }
126*04eeddc0SDimitry Andric 
127*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard &
128*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::getShardForUID(
129*04eeddc0SDimitry Andric     llvm::sys::fs::UniqueID UID) const {
130*04eeddc0SDimitry Andric   auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile());
131*04eeddc0SDimitry Andric   return CacheShards[Hash % NumShards];
132*04eeddc0SDimitry Andric }
133*04eeddc0SDimitry Andric 
134*04eeddc0SDimitry Andric const CachedFileSystemEntry *
135*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename(
136*04eeddc0SDimitry Andric     StringRef Filename) const {
137*04eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
138*04eeddc0SDimitry Andric   auto It = EntriesByFilename.find(Filename);
139*04eeddc0SDimitry Andric   return It == EntriesByFilename.end() ? nullptr : It->getValue();
140*04eeddc0SDimitry Andric }
141*04eeddc0SDimitry Andric 
142*04eeddc0SDimitry Andric const CachedFileSystemEntry *
143*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findEntryByUID(
144*04eeddc0SDimitry Andric     llvm::sys::fs::UniqueID UID) const {
145*04eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
146*04eeddc0SDimitry Andric   auto It = EntriesByUID.find(UID);
147*04eeddc0SDimitry Andric   return It == EntriesByUID.end() ? nullptr : It->getSecond();
148*04eeddc0SDimitry Andric }
149*04eeddc0SDimitry Andric 
150*04eeddc0SDimitry Andric const CachedFileSystemEntry &
151*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::
152*04eeddc0SDimitry Andric     getOrEmplaceEntryForFilename(StringRef Filename,
153*04eeddc0SDimitry Andric                                  llvm::ErrorOr<llvm::vfs::Status> Stat) {
154*04eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
155*04eeddc0SDimitry Andric   auto Insertion = EntriesByFilename.insert({Filename, nullptr});
156*04eeddc0SDimitry Andric   if (Insertion.second)
157*04eeddc0SDimitry Andric     Insertion.first->second =
158*04eeddc0SDimitry Andric         new (EntryStorage.Allocate()) CachedFileSystemEntry(std::move(Stat));
159*04eeddc0SDimitry Andric   return *Insertion.first->second;
160*04eeddc0SDimitry Andric }
161*04eeddc0SDimitry Andric 
162*04eeddc0SDimitry Andric const CachedFileSystemEntry &
163*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::getOrEmplaceEntryForUID(
164*04eeddc0SDimitry Andric     llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat,
165*04eeddc0SDimitry Andric     std::unique_ptr<llvm::MemoryBuffer> Contents) {
166*04eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
167*04eeddc0SDimitry Andric   auto Insertion = EntriesByUID.insert({UID, nullptr});
168*04eeddc0SDimitry Andric   if (Insertion.second) {
169*04eeddc0SDimitry Andric     CachedFileContents *StoredContents = nullptr;
170*04eeddc0SDimitry Andric     if (Contents)
171*04eeddc0SDimitry Andric       StoredContents = new (ContentsStorage.Allocate())
172*04eeddc0SDimitry Andric           CachedFileContents(std::move(Contents));
173*04eeddc0SDimitry Andric     Insertion.first->second = new (EntryStorage.Allocate())
174*04eeddc0SDimitry Andric         CachedFileSystemEntry(std::move(Stat), StoredContents);
175*04eeddc0SDimitry Andric   }
176*04eeddc0SDimitry Andric   return *Insertion.first->second;
177*04eeddc0SDimitry Andric }
178*04eeddc0SDimitry Andric 
179*04eeddc0SDimitry Andric const CachedFileSystemEntry &
180*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::
181*04eeddc0SDimitry Andric     getOrInsertEntryForFilename(StringRef Filename,
182*04eeddc0SDimitry Andric                                 const CachedFileSystemEntry &Entry) {
183*04eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
184*04eeddc0SDimitry Andric   return *EntriesByFilename.insert({Filename, &Entry}).first->getValue();
185a7dea167SDimitry Andric }
186a7dea167SDimitry Andric 
187480093f4SDimitry Andric /// Whitelist file extensions that should be minimized, treating no extension as
188480093f4SDimitry Andric /// a source file that should be minimized.
189480093f4SDimitry Andric ///
190480093f4SDimitry Andric /// This is kinda hacky, it would be better if we knew what kind of file Clang
191480093f4SDimitry Andric /// was expecting instead.
1924824e7fdSDimitry Andric static bool shouldMinimizeBasedOnExtension(StringRef Filename) {
193480093f4SDimitry Andric   StringRef Ext = llvm::sys::path::extension(Filename);
194480093f4SDimitry Andric   if (Ext.empty())
195480093f4SDimitry Andric     return true; // C++ standard library
196480093f4SDimitry Andric   return llvm::StringSwitch<bool>(Ext)
197480093f4SDimitry Andric       .CasesLower(".c", ".cc", ".cpp", ".c++", ".cxx", true)
198480093f4SDimitry Andric       .CasesLower(".h", ".hh", ".hpp", ".h++", ".hxx", true)
199480093f4SDimitry Andric       .CasesLower(".m", ".mm", true)
200480093f4SDimitry Andric       .CasesLower(".i", ".ii", ".mi", ".mmi", true)
201480093f4SDimitry Andric       .CasesLower(".def", ".inc", true)
202480093f4SDimitry Andric       .Default(false);
203480093f4SDimitry Andric }
204480093f4SDimitry Andric 
205480093f4SDimitry Andric static bool shouldCacheStatFailures(StringRef Filename) {
206480093f4SDimitry Andric   StringRef Ext = llvm::sys::path::extension(Filename);
207480093f4SDimitry Andric   if (Ext.empty())
208480093f4SDimitry Andric     return false; // This may be the module cache directory.
2094824e7fdSDimitry Andric   // Only cache stat failures on source files.
2104824e7fdSDimitry Andric   return shouldMinimizeBasedOnExtension(Filename);
211480093f4SDimitry Andric }
212480093f4SDimitry Andric 
2134824e7fdSDimitry Andric void DependencyScanningWorkerFilesystem::disableMinimization(
214*04eeddc0SDimitry Andric     StringRef Filename) {
215*04eeddc0SDimitry Andric   // Since we're not done setting up `NotToBeMinimized` yet, we need to disable
216*04eeddc0SDimitry Andric   // minimization explicitly.
217*04eeddc0SDimitry Andric   if (llvm::ErrorOr<EntryRef> Result =
218*04eeddc0SDimitry Andric           getOrCreateFileSystemEntry(Filename, /*DisableMinimization=*/true))
219*04eeddc0SDimitry Andric     NotToBeMinimized.insert(Result->getStatus().getUniqueID());
2204824e7fdSDimitry Andric }
2214824e7fdSDimitry Andric 
222*04eeddc0SDimitry Andric bool DependencyScanningWorkerFilesystem::shouldMinimize(
223*04eeddc0SDimitry Andric     StringRef Filename, llvm::sys::fs::UniqueID UID) {
224*04eeddc0SDimitry Andric   return shouldMinimizeBasedOnExtension(Filename) &&
225*04eeddc0SDimitry Andric          !NotToBeMinimized.contains(UID);
2264824e7fdSDimitry Andric }
2274824e7fdSDimitry Andric 
228*04eeddc0SDimitry Andric const CachedFileSystemEntry &
229*04eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
230*04eeddc0SDimitry Andric     TentativeEntry TEntry) {
231*04eeddc0SDimitry Andric   auto &Shard = SharedCache.getShardForUID(TEntry.Status.getUniqueID());
232*04eeddc0SDimitry Andric   return Shard.getOrEmplaceEntryForUID(TEntry.Status.getUniqueID(),
233*04eeddc0SDimitry Andric                                        std::move(TEntry.Status),
234*04eeddc0SDimitry Andric                                        std::move(TEntry.Contents));
235*04eeddc0SDimitry Andric }
236*04eeddc0SDimitry Andric 
237*04eeddc0SDimitry Andric const CachedFileSystemEntry *
238*04eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough(
239*04eeddc0SDimitry Andric     StringRef Filename) {
240*04eeddc0SDimitry Andric   if (const auto *Entry = LocalCache.findEntryByFilename(Filename))
241*04eeddc0SDimitry Andric     return Entry;
242*04eeddc0SDimitry Andric   auto &Shard = SharedCache.getShardForFilename(Filename);
243*04eeddc0SDimitry Andric   if (const auto *Entry = Shard.findEntryByFilename(Filename))
244*04eeddc0SDimitry Andric     return &LocalCache.insertEntryForFilename(Filename, *Entry);
245*04eeddc0SDimitry Andric   return nullptr;
246*04eeddc0SDimitry Andric }
247*04eeddc0SDimitry Andric 
248*04eeddc0SDimitry Andric llvm::ErrorOr<const CachedFileSystemEntry &>
249*04eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::computeAndStoreResult(StringRef Filename) {
250*04eeddc0SDimitry Andric   llvm::ErrorOr<llvm::vfs::Status> Stat = getUnderlyingFS().status(Filename);
251*04eeddc0SDimitry Andric   if (!Stat) {
252*04eeddc0SDimitry Andric     if (!shouldCacheStatFailures(Filename))
253*04eeddc0SDimitry Andric       return Stat.getError();
254*04eeddc0SDimitry Andric     const auto &Entry =
255*04eeddc0SDimitry Andric         getOrEmplaceSharedEntryForFilename(Filename, Stat.getError());
256*04eeddc0SDimitry Andric     return insertLocalEntryForFilename(Filename, Entry);
257*04eeddc0SDimitry Andric   }
258*04eeddc0SDimitry Andric 
259*04eeddc0SDimitry Andric   if (const auto *Entry = findSharedEntryByUID(*Stat))
260*04eeddc0SDimitry Andric     return insertLocalEntryForFilename(Filename, *Entry);
261*04eeddc0SDimitry Andric 
262*04eeddc0SDimitry Andric   auto TEntry =
263*04eeddc0SDimitry Andric       Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(Filename);
264*04eeddc0SDimitry Andric 
265*04eeddc0SDimitry Andric   const CachedFileSystemEntry *SharedEntry = [&]() {
266*04eeddc0SDimitry Andric     if (TEntry) {
267*04eeddc0SDimitry Andric       const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry));
268*04eeddc0SDimitry Andric       return &getOrInsertSharedEntryForFilename(Filename, UIDEntry);
269*04eeddc0SDimitry Andric     }
270*04eeddc0SDimitry Andric     return &getOrEmplaceSharedEntryForFilename(Filename, TEntry.getError());
271*04eeddc0SDimitry Andric   }();
272*04eeddc0SDimitry Andric 
273*04eeddc0SDimitry Andric   return insertLocalEntryForFilename(Filename, *SharedEntry);
274fe6060f1SDimitry Andric }
275fe6060f1SDimitry Andric 
2760eae32dcSDimitry Andric llvm::ErrorOr<EntryRef>
277a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
278*04eeddc0SDimitry Andric     StringRef Filename, bool DisableMinimization) {
279*04eeddc0SDimitry Andric   if (const auto *Entry = findEntryByFilenameWithWriteThrough(Filename))
280*04eeddc0SDimitry Andric     return minimizeIfNecessary(*Entry, Filename, DisableMinimization)
281*04eeddc0SDimitry Andric         .unwrapError();
282*04eeddc0SDimitry Andric   auto MaybeEntry = computeAndStoreResult(Filename);
283*04eeddc0SDimitry Andric   if (!MaybeEntry)
284*04eeddc0SDimitry Andric     return MaybeEntry.getError();
285*04eeddc0SDimitry Andric   return minimizeIfNecessary(*MaybeEntry, Filename, DisableMinimization)
286*04eeddc0SDimitry Andric       .unwrapError();
287a7dea167SDimitry Andric }
288a7dea167SDimitry Andric 
289a7dea167SDimitry Andric llvm::ErrorOr<llvm::vfs::Status>
290a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::status(const Twine &Path) {
291a7dea167SDimitry Andric   SmallString<256> OwnedFilename;
292a7dea167SDimitry Andric   StringRef Filename = Path.toStringRef(OwnedFilename);
2930eae32dcSDimitry Andric 
2940eae32dcSDimitry Andric   llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename);
295a7dea167SDimitry Andric   if (!Result)
296a7dea167SDimitry Andric     return Result.getError();
2970eae32dcSDimitry Andric   return Result->getStatus();
298a7dea167SDimitry Andric }
299a7dea167SDimitry Andric 
300a7dea167SDimitry Andric namespace {
301a7dea167SDimitry Andric 
302a7dea167SDimitry Andric /// The VFS that is used by clang consumes the \c CachedFileSystemEntry using
303a7dea167SDimitry Andric /// this subclass.
304a7dea167SDimitry Andric class MinimizedVFSFile final : public llvm::vfs::File {
305a7dea167SDimitry Andric public:
306a7dea167SDimitry Andric   MinimizedVFSFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
307a7dea167SDimitry Andric                    llvm::vfs::Status Stat)
308a7dea167SDimitry Andric       : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {}
309a7dea167SDimitry Andric 
310e8d8bef9SDimitry Andric   static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
3110eae32dcSDimitry Andric   create(EntryRef Entry,
312e8d8bef9SDimitry Andric          ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings);
313a7dea167SDimitry Andric 
314e8d8bef9SDimitry Andric   llvm::ErrorOr<llvm::vfs::Status> status() override { return Stat; }
315a7dea167SDimitry Andric 
316a7dea167SDimitry Andric   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
317a7dea167SDimitry Andric   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
318a7dea167SDimitry Andric             bool IsVolatile) override {
319a7dea167SDimitry Andric     return std::move(Buffer);
320a7dea167SDimitry Andric   }
321a7dea167SDimitry Andric 
322a7dea167SDimitry Andric   std::error_code close() override { return {}; }
323a7dea167SDimitry Andric 
324a7dea167SDimitry Andric private:
325a7dea167SDimitry Andric   std::unique_ptr<llvm::MemoryBuffer> Buffer;
326a7dea167SDimitry Andric   llvm::vfs::Status Stat;
327a7dea167SDimitry Andric };
328a7dea167SDimitry Andric 
329e8d8bef9SDimitry Andric } // end anonymous namespace
330e8d8bef9SDimitry Andric 
331e8d8bef9SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> MinimizedVFSFile::create(
3320eae32dcSDimitry Andric     EntryRef Entry, ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings) {
333*04eeddc0SDimitry Andric   assert(!Entry.isError() && "error");
334*04eeddc0SDimitry Andric 
3350eae32dcSDimitry Andric   if (Entry.isDirectory())
3360eae32dcSDimitry Andric     return std::make_error_code(std::errc::is_a_directory);
3370eae32dcSDimitry Andric 
338a7dea167SDimitry Andric   auto Result = std::make_unique<MinimizedVFSFile>(
339*04eeddc0SDimitry Andric       llvm::MemoryBuffer::getMemBuffer(Entry.getContents(),
340*04eeddc0SDimitry Andric                                        Entry.getStatus().getName(),
341a7dea167SDimitry Andric                                        /*RequiresNullTerminator=*/false),
342*04eeddc0SDimitry Andric       Entry.getStatus());
3430eae32dcSDimitry Andric 
3440eae32dcSDimitry Andric   const auto *EntrySkipMappings = Entry.getPPSkippedRangeMapping();
3450eae32dcSDimitry Andric   if (EntrySkipMappings && !EntrySkipMappings->empty() && PPSkipMappings)
3460eae32dcSDimitry Andric     (*PPSkipMappings)[Result->Buffer->getBufferStart()] = EntrySkipMappings;
3470eae32dcSDimitry Andric 
348a7dea167SDimitry Andric   return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
349a7dea167SDimitry Andric       std::unique_ptr<llvm::vfs::File>(std::move(Result)));
350a7dea167SDimitry Andric }
351a7dea167SDimitry Andric 
352a7dea167SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
353a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) {
354a7dea167SDimitry Andric   SmallString<256> OwnedFilename;
355a7dea167SDimitry Andric   StringRef Filename = Path.toStringRef(OwnedFilename);
356a7dea167SDimitry Andric 
3570eae32dcSDimitry Andric   llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename);
358a7dea167SDimitry Andric   if (!Result)
359a7dea167SDimitry Andric     return Result.getError();
360e8d8bef9SDimitry Andric   return MinimizedVFSFile::create(Result.get(), PPSkipMappings);
361a7dea167SDimitry Andric }
362