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 "llvm/Support/MemoryBuffer.h"
110eae32dcSDimitry Andric #include "llvm/Support/SmallVectorMemoryBuffer.h"
12a7dea167SDimitry Andric #include "llvm/Support/Threading.h"
13bdd1243dSDimitry Andric #include <optional>
14a7dea167SDimitry Andric 
15a7dea167SDimitry Andric using namespace clang;
16a7dea167SDimitry Andric using namespace tooling;
17a7dea167SDimitry Andric using namespace dependencies;
18a7dea167SDimitry Andric 
1904eeddc0SDimitry Andric llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry>
2004eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
21a7dea167SDimitry Andric   // Load the file and its content from the file system.
2204eeddc0SDimitry 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 
3704eeddc0SDimitry Andric   // If the file size changed between read and stat, pretend it didn't.
3804eeddc0SDimitry Andric   if (Stat.getSize() != Buffer->getBufferSize())
3904eeddc0SDimitry Andric     Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize());
4004eeddc0SDimitry Andric 
4104eeddc0SDimitry Andric   return TentativeEntry(Stat, std::move(Buffer));
420eae32dcSDimitry Andric }
430eae32dcSDimitry Andric 
4481ad6265SDimitry Andric EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
4504eeddc0SDimitry Andric     const CachedFileSystemEntry &Entry, StringRef Filename, bool Disable) {
4604eeddc0SDimitry Andric   if (Entry.isError() || Entry.isDirectory() || Disable ||
4781ad6265SDimitry Andric       !shouldScanForDirectives(Filename))
4881ad6265SDimitry Andric     return EntryRef(Filename, Entry);
4904eeddc0SDimitry Andric 
5081ad6265SDimitry Andric   CachedFileContents *Contents = Entry.getCachedContents();
5104eeddc0SDimitry Andric   assert(Contents && "contents not initialized");
5204eeddc0SDimitry Andric 
5304eeddc0SDimitry Andric   // Double-checked locking.
5481ad6265SDimitry Andric   if (Contents->DepDirectives.load())
5581ad6265SDimitry Andric     return EntryRef(Filename, Entry);
5604eeddc0SDimitry Andric 
5704eeddc0SDimitry Andric   std::lock_guard<std::mutex> GuardLock(Contents->ValueLock);
5804eeddc0SDimitry Andric 
5904eeddc0SDimitry Andric   // Double-checked locking.
6081ad6265SDimitry Andric   if (Contents->DepDirectives.load())
6181ad6265SDimitry Andric     return EntryRef(Filename, Entry);
62a7dea167SDimitry Andric 
6381ad6265SDimitry Andric   SmallVector<dependency_directives_scan::Directive, 64> Directives;
6481ad6265SDimitry Andric   // Scan the file for preprocessor directives that might affect the
6581ad6265SDimitry Andric   // dependencies.
6681ad6265SDimitry Andric   if (scanSourceForDependencyDirectives(Contents->Original->getBuffer(),
6781ad6265SDimitry Andric                                         Contents->DepDirectiveTokens,
6881ad6265SDimitry Andric                                         Directives)) {
6981ad6265SDimitry Andric     Contents->DepDirectiveTokens.clear();
700eae32dcSDimitry Andric     // FIXME: Propagate the diagnostic if desired by the client.
71bdd1243dSDimitry Andric     Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>());
7281ad6265SDimitry Andric     return EntryRef(Filename, Entry);
73a7dea167SDimitry Andric   }
74a7dea167SDimitry Andric 
7581ad6265SDimitry Andric   // This function performed double-checked locking using `DepDirectives`.
7681ad6265SDimitry Andric   // Assigning it must be the last thing this function does, otherwise other
7781ad6265SDimitry Andric   // threads may skip the
7881ad6265SDimitry Andric   // critical section (`DepDirectives != nullptr`), leading to a data race.
7981ad6265SDimitry Andric   Contents->DepDirectives.store(
80bdd1243dSDimitry Andric       new std::optional<DependencyDirectivesTy>(std::move(Directives)));
8181ad6265SDimitry Andric   return EntryRef(Filename, Entry);
82a7dea167SDimitry Andric }
83a7dea167SDimitry Andric 
840eae32dcSDimitry Andric DependencyScanningFilesystemSharedCache::
850eae32dcSDimitry Andric     DependencyScanningFilesystemSharedCache() {
86a7dea167SDimitry Andric   // This heuristic was chosen using a empirical testing on a
87a7dea167SDimitry Andric   // reasonably high core machine (iMacPro 18 cores / 36 threads). The cache
88a7dea167SDimitry Andric   // sharding gives a performance edge by reducing the lock contention.
89a7dea167SDimitry Andric   // FIXME: A better heuristic might also consider the OS to account for
90a7dea167SDimitry Andric   // the different cost of lock contention on different OSes.
915ffd83dbSDimitry Andric   NumShards =
925ffd83dbSDimitry Andric       std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4);
93a7dea167SDimitry Andric   CacheShards = std::make_unique<CacheShard[]>(NumShards);
94a7dea167SDimitry Andric }
95a7dea167SDimitry Andric 
9604eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard &
9704eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::getShardForFilename(
9804eeddc0SDimitry Andric     StringRef Filename) const {
994542f901SDimitry Andric   assert(llvm::sys::path::is_absolute_gnu(Filename));
10004eeddc0SDimitry Andric   return CacheShards[llvm::hash_value(Filename) % NumShards];
10104eeddc0SDimitry Andric }
10204eeddc0SDimitry Andric 
10304eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard &
10404eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::getShardForUID(
10504eeddc0SDimitry Andric     llvm::sys::fs::UniqueID UID) const {
10604eeddc0SDimitry Andric   auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile());
10704eeddc0SDimitry Andric   return CacheShards[Hash % NumShards];
10804eeddc0SDimitry Andric }
10904eeddc0SDimitry Andric 
11004eeddc0SDimitry Andric const CachedFileSystemEntry *
11104eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename(
11204eeddc0SDimitry Andric     StringRef Filename) const {
1134542f901SDimitry Andric   assert(llvm::sys::path::is_absolute_gnu(Filename));
11404eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
11504eeddc0SDimitry Andric   auto It = EntriesByFilename.find(Filename);
11604eeddc0SDimitry Andric   return It == EntriesByFilename.end() ? nullptr : It->getValue();
11704eeddc0SDimitry Andric }
11804eeddc0SDimitry Andric 
11904eeddc0SDimitry Andric const CachedFileSystemEntry *
12004eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findEntryByUID(
12104eeddc0SDimitry Andric     llvm::sys::fs::UniqueID UID) const {
12204eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
12304eeddc0SDimitry Andric   auto It = EntriesByUID.find(UID);
12404eeddc0SDimitry Andric   return It == EntriesByUID.end() ? nullptr : It->getSecond();
12504eeddc0SDimitry Andric }
12604eeddc0SDimitry Andric 
12704eeddc0SDimitry Andric const CachedFileSystemEntry &
12804eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::
12904eeddc0SDimitry Andric     getOrEmplaceEntryForFilename(StringRef Filename,
13004eeddc0SDimitry Andric                                  llvm::ErrorOr<llvm::vfs::Status> Stat) {
13104eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
13204eeddc0SDimitry Andric   auto Insertion = EntriesByFilename.insert({Filename, nullptr});
13304eeddc0SDimitry Andric   if (Insertion.second)
13404eeddc0SDimitry Andric     Insertion.first->second =
13504eeddc0SDimitry Andric         new (EntryStorage.Allocate()) CachedFileSystemEntry(std::move(Stat));
13604eeddc0SDimitry Andric   return *Insertion.first->second;
13704eeddc0SDimitry Andric }
13804eeddc0SDimitry Andric 
13904eeddc0SDimitry Andric const CachedFileSystemEntry &
14004eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::getOrEmplaceEntryForUID(
14104eeddc0SDimitry Andric     llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat,
14204eeddc0SDimitry Andric     std::unique_ptr<llvm::MemoryBuffer> Contents) {
14304eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
14404eeddc0SDimitry Andric   auto Insertion = EntriesByUID.insert({UID, nullptr});
14504eeddc0SDimitry Andric   if (Insertion.second) {
14604eeddc0SDimitry Andric     CachedFileContents *StoredContents = nullptr;
14704eeddc0SDimitry Andric     if (Contents)
14804eeddc0SDimitry Andric       StoredContents = new (ContentsStorage.Allocate())
14904eeddc0SDimitry Andric           CachedFileContents(std::move(Contents));
15004eeddc0SDimitry Andric     Insertion.first->second = new (EntryStorage.Allocate())
15104eeddc0SDimitry Andric         CachedFileSystemEntry(std::move(Stat), StoredContents);
15204eeddc0SDimitry Andric   }
15304eeddc0SDimitry Andric   return *Insertion.first->second;
15404eeddc0SDimitry Andric }
15504eeddc0SDimitry Andric 
15604eeddc0SDimitry Andric const CachedFileSystemEntry &
15704eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::
15804eeddc0SDimitry Andric     getOrInsertEntryForFilename(StringRef Filename,
15904eeddc0SDimitry Andric                                 const CachedFileSystemEntry &Entry) {
16004eeddc0SDimitry Andric   std::lock_guard<std::mutex> LockGuard(CacheLock);
16104eeddc0SDimitry Andric   return *EntriesByFilename.insert({Filename, &Entry}).first->getValue();
162a7dea167SDimitry Andric }
163a7dea167SDimitry Andric 
164480093f4SDimitry Andric /// Whitelist file extensions that should be minimized, treating no extension as
165480093f4SDimitry Andric /// a source file that should be minimized.
166480093f4SDimitry Andric ///
167480093f4SDimitry Andric /// This is kinda hacky, it would be better if we knew what kind of file Clang
168480093f4SDimitry Andric /// was expecting instead.
16981ad6265SDimitry Andric static bool shouldScanForDirectivesBasedOnExtension(StringRef Filename) {
170480093f4SDimitry Andric   StringRef Ext = llvm::sys::path::extension(Filename);
171480093f4SDimitry Andric   if (Ext.empty())
172480093f4SDimitry Andric     return true; // C++ standard library
173480093f4SDimitry Andric   return llvm::StringSwitch<bool>(Ext)
174480093f4SDimitry Andric       .CasesLower(".c", ".cc", ".cpp", ".c++", ".cxx", true)
175480093f4SDimitry Andric       .CasesLower(".h", ".hh", ".hpp", ".h++", ".hxx", true)
176480093f4SDimitry Andric       .CasesLower(".m", ".mm", true)
177480093f4SDimitry Andric       .CasesLower(".i", ".ii", ".mi", ".mmi", true)
178480093f4SDimitry Andric       .CasesLower(".def", ".inc", true)
179480093f4SDimitry Andric       .Default(false);
180480093f4SDimitry Andric }
181480093f4SDimitry Andric 
182480093f4SDimitry Andric static bool shouldCacheStatFailures(StringRef Filename) {
183480093f4SDimitry Andric   StringRef Ext = llvm::sys::path::extension(Filename);
184480093f4SDimitry Andric   if (Ext.empty())
185480093f4SDimitry Andric     return false; // This may be the module cache directory.
18606c3fb27SDimitry Andric   // Only cache stat failures on files that are not expected to change during
18706c3fb27SDimitry Andric   // the build.
18806c3fb27SDimitry Andric   StringRef FName = llvm::sys::path::filename(Filename);
18906c3fb27SDimitry Andric   if (FName == "module.modulemap" || FName == "module.map")
19006c3fb27SDimitry Andric     return true;
19181ad6265SDimitry Andric   return shouldScanForDirectivesBasedOnExtension(Filename);
192480093f4SDimitry Andric }
193480093f4SDimitry Andric 
1944542f901SDimitry Andric DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
1954542f901SDimitry Andric     DependencyScanningFilesystemSharedCache &SharedCache,
1964542f901SDimitry Andric     IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
1974542f901SDimitry Andric     : ProxyFileSystem(std::move(FS)), SharedCache(SharedCache),
1984542f901SDimitry Andric       WorkingDirForCacheLookup(llvm::errc::invalid_argument) {
1994542f901SDimitry Andric   updateWorkingDirForCacheLookup();
2004542f901SDimitry Andric }
2014542f901SDimitry Andric 
20281ad6265SDimitry Andric bool DependencyScanningWorkerFilesystem::shouldScanForDirectives(
20304eeddc0SDimitry Andric     StringRef Filename) {
20481ad6265SDimitry Andric   return shouldScanForDirectivesBasedOnExtension(Filename);
2054824e7fdSDimitry Andric }
2064824e7fdSDimitry Andric 
20704eeddc0SDimitry Andric const CachedFileSystemEntry &
20804eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
20904eeddc0SDimitry Andric     TentativeEntry TEntry) {
21004eeddc0SDimitry Andric   auto &Shard = SharedCache.getShardForUID(TEntry.Status.getUniqueID());
21104eeddc0SDimitry Andric   return Shard.getOrEmplaceEntryForUID(TEntry.Status.getUniqueID(),
21204eeddc0SDimitry Andric                                        std::move(TEntry.Status),
21304eeddc0SDimitry Andric                                        std::move(TEntry.Contents));
21404eeddc0SDimitry Andric }
21504eeddc0SDimitry Andric 
21604eeddc0SDimitry Andric const CachedFileSystemEntry *
21704eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough(
21804eeddc0SDimitry Andric     StringRef Filename) {
21904eeddc0SDimitry Andric   if (const auto *Entry = LocalCache.findEntryByFilename(Filename))
22004eeddc0SDimitry Andric     return Entry;
22104eeddc0SDimitry Andric   auto &Shard = SharedCache.getShardForFilename(Filename);
22204eeddc0SDimitry Andric   if (const auto *Entry = Shard.findEntryByFilename(Filename))
22304eeddc0SDimitry Andric     return &LocalCache.insertEntryForFilename(Filename, *Entry);
22404eeddc0SDimitry Andric   return nullptr;
22504eeddc0SDimitry Andric }
22604eeddc0SDimitry Andric 
22704eeddc0SDimitry Andric llvm::ErrorOr<const CachedFileSystemEntry &>
2284542f901SDimitry Andric DependencyScanningWorkerFilesystem::computeAndStoreResult(
2294542f901SDimitry Andric     StringRef OriginalFilename, StringRef FilenameForLookup) {
2304542f901SDimitry Andric   llvm::ErrorOr<llvm::vfs::Status> Stat =
2314542f901SDimitry Andric       getUnderlyingFS().status(OriginalFilename);
23204eeddc0SDimitry Andric   if (!Stat) {
2334542f901SDimitry Andric     if (!shouldCacheStatFailures(OriginalFilename))
23404eeddc0SDimitry Andric       return Stat.getError();
23504eeddc0SDimitry Andric     const auto &Entry =
2364542f901SDimitry Andric         getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
2374542f901SDimitry Andric     return insertLocalEntryForFilename(FilenameForLookup, Entry);
23804eeddc0SDimitry Andric   }
23904eeddc0SDimitry Andric 
24004eeddc0SDimitry Andric   if (const auto *Entry = findSharedEntryByUID(*Stat))
2414542f901SDimitry Andric     return insertLocalEntryForFilename(FilenameForLookup, *Entry);
24204eeddc0SDimitry Andric 
24304eeddc0SDimitry Andric   auto TEntry =
2444542f901SDimitry Andric       Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename);
24504eeddc0SDimitry Andric 
24604eeddc0SDimitry Andric   const CachedFileSystemEntry *SharedEntry = [&]() {
24704eeddc0SDimitry Andric     if (TEntry) {
24804eeddc0SDimitry Andric       const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry));
2494542f901SDimitry Andric       return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry);
25004eeddc0SDimitry Andric     }
2514542f901SDimitry Andric     return &getOrEmplaceSharedEntryForFilename(FilenameForLookup,
2524542f901SDimitry Andric                                                TEntry.getError());
25304eeddc0SDimitry Andric   }();
25404eeddc0SDimitry Andric 
2554542f901SDimitry Andric   return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry);
256fe6060f1SDimitry Andric }
257fe6060f1SDimitry Andric 
2580eae32dcSDimitry Andric llvm::ErrorOr<EntryRef>
259a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
2604542f901SDimitry Andric     StringRef OriginalFilename, bool DisableDirectivesScanning) {
2614542f901SDimitry Andric   StringRef FilenameForLookup;
2624542f901SDimitry Andric   SmallString<256> PathBuf;
2634542f901SDimitry Andric   if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
2644542f901SDimitry Andric     FilenameForLookup = OriginalFilename;
2654542f901SDimitry Andric   } else if (!WorkingDirForCacheLookup) {
2664542f901SDimitry Andric     return WorkingDirForCacheLookup.getError();
2674542f901SDimitry Andric   } else {
2684542f901SDimitry Andric     StringRef RelFilename = OriginalFilename;
2694542f901SDimitry Andric     RelFilename.consume_front("./");
2704542f901SDimitry Andric     PathBuf = *WorkingDirForCacheLookup;
2714542f901SDimitry Andric     llvm::sys::path::append(PathBuf, RelFilename);
2724542f901SDimitry Andric     FilenameForLookup = PathBuf.str();
2734542f901SDimitry Andric   }
2744542f901SDimitry Andric   assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
2754542f901SDimitry Andric   if (const auto *Entry =
2764542f901SDimitry Andric           findEntryByFilenameWithWriteThrough(FilenameForLookup))
2774542f901SDimitry Andric     return scanForDirectivesIfNecessary(*Entry, OriginalFilename,
27881ad6265SDimitry Andric                                         DisableDirectivesScanning)
27904eeddc0SDimitry Andric         .unwrapError();
2804542f901SDimitry Andric   auto MaybeEntry = computeAndStoreResult(OriginalFilename, FilenameForLookup);
28104eeddc0SDimitry Andric   if (!MaybeEntry)
28204eeddc0SDimitry Andric     return MaybeEntry.getError();
2834542f901SDimitry Andric   return scanForDirectivesIfNecessary(*MaybeEntry, OriginalFilename,
28481ad6265SDimitry Andric                                       DisableDirectivesScanning)
28504eeddc0SDimitry Andric       .unwrapError();
286a7dea167SDimitry Andric }
287a7dea167SDimitry Andric 
288a7dea167SDimitry Andric llvm::ErrorOr<llvm::vfs::Status>
289a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::status(const Twine &Path) {
290a7dea167SDimitry Andric   SmallString<256> OwnedFilename;
291a7dea167SDimitry Andric   StringRef Filename = Path.toStringRef(OwnedFilename);
2920eae32dcSDimitry Andric 
293*5f757f3fSDimitry Andric   if (Filename.ends_with(".pcm"))
29406c3fb27SDimitry Andric     return getUnderlyingFS().status(Path);
29506c3fb27SDimitry Andric 
2960eae32dcSDimitry Andric   llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename);
297a7dea167SDimitry Andric   if (!Result)
298a7dea167SDimitry Andric     return Result.getError();
2990eae32dcSDimitry Andric   return Result->getStatus();
300a7dea167SDimitry Andric }
301a7dea167SDimitry Andric 
302a7dea167SDimitry Andric namespace {
303a7dea167SDimitry Andric 
304a7dea167SDimitry Andric /// The VFS that is used by clang consumes the \c CachedFileSystemEntry using
305a7dea167SDimitry Andric /// this subclass.
30681ad6265SDimitry Andric class DepScanFile final : public llvm::vfs::File {
307a7dea167SDimitry Andric public:
30881ad6265SDimitry Andric   DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
309a7dea167SDimitry Andric               llvm::vfs::Status Stat)
310a7dea167SDimitry Andric       : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {}
311a7dea167SDimitry Andric 
31281ad6265SDimitry Andric   static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(EntryRef Entry);
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 
33181ad6265SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
33281ad6265SDimitry Andric DepScanFile::create(EntryRef Entry) {
33304eeddc0SDimitry Andric   assert(!Entry.isError() && "error");
33404eeddc0SDimitry Andric 
3350eae32dcSDimitry Andric   if (Entry.isDirectory())
3360eae32dcSDimitry Andric     return std::make_error_code(std::errc::is_a_directory);
3370eae32dcSDimitry Andric 
33881ad6265SDimitry Andric   auto Result = std::make_unique<DepScanFile>(
33904eeddc0SDimitry Andric       llvm::MemoryBuffer::getMemBuffer(Entry.getContents(),
34004eeddc0SDimitry Andric                                        Entry.getStatus().getName(),
341a7dea167SDimitry Andric                                        /*RequiresNullTerminator=*/false),
34204eeddc0SDimitry Andric       Entry.getStatus());
3430eae32dcSDimitry Andric 
344a7dea167SDimitry Andric   return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
345a7dea167SDimitry Andric       std::unique_ptr<llvm::vfs::File>(std::move(Result)));
346a7dea167SDimitry Andric }
347a7dea167SDimitry Andric 
348a7dea167SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
349a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) {
350a7dea167SDimitry Andric   SmallString<256> OwnedFilename;
351a7dea167SDimitry Andric   StringRef Filename = Path.toStringRef(OwnedFilename);
352a7dea167SDimitry Andric 
353*5f757f3fSDimitry Andric   if (Filename.ends_with(".pcm"))
35406c3fb27SDimitry Andric     return getUnderlyingFS().openFileForRead(Path);
35506c3fb27SDimitry Andric 
3560eae32dcSDimitry Andric   llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename);
357a7dea167SDimitry Andric   if (!Result)
358a7dea167SDimitry Andric     return Result.getError();
35981ad6265SDimitry Andric   return DepScanFile::create(Result.get());
360a7dea167SDimitry Andric }
3614542f901SDimitry Andric 
3624542f901SDimitry Andric std::error_code DependencyScanningWorkerFilesystem::setCurrentWorkingDirectory(
3634542f901SDimitry Andric     const Twine &Path) {
3644542f901SDimitry Andric   std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
3654542f901SDimitry Andric   updateWorkingDirForCacheLookup();
3664542f901SDimitry Andric   return EC;
3674542f901SDimitry Andric }
3684542f901SDimitry Andric 
3694542f901SDimitry Andric void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
3704542f901SDimitry Andric   llvm::ErrorOr<std::string> CWD =
3714542f901SDimitry Andric       getUnderlyingFS().getCurrentWorkingDirectory();
3724542f901SDimitry Andric   if (!CWD) {
3734542f901SDimitry Andric     WorkingDirForCacheLookup = CWD.getError();
3744542f901SDimitry Andric   } else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
3754542f901SDimitry Andric     WorkingDirForCacheLookup = llvm::errc::invalid_argument;
3764542f901SDimitry Andric   } else {
3774542f901SDimitry Andric     WorkingDirForCacheLookup = *CWD;
3784542f901SDimitry Andric   }
3794542f901SDimitry Andric   assert(!WorkingDirForCacheLookup ||
3804542f901SDimitry Andric          llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
3814542f901SDimitry Andric }
382