1 //===--- FS.cpp - File system related utils ----------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "FS.h"
10 #include "clang/Basic/LLVM.h"
11 #include "llvm/ADT/None.h"
12 #include "llvm/Support/Path.h"
13 
14 namespace clang {
15 namespace clangd {
16 
17 PreambleFileStatusCache::PreambleFileStatusCache(llvm::StringRef MainFilePath)
18     : MainFilePath(MainFilePath) {
19   assert(llvm::sys::path::is_absolute(MainFilePath));
20 }
21 
22 void PreambleFileStatusCache::update(const llvm::vfs::FileSystem &FS,
23                                      llvm::vfs::Status S) {
24   llvm::SmallString<32> PathStore(S.getName());
25   if (FS.makeAbsolute(PathStore))
26     return;
27   // Do not cache status for the main file.
28   if (PathStore == MainFilePath)
29     return;
30   // Stores the latest status in cache as it can change in a preamble build.
31   StatCache.insert({PathStore, std::move(S)});
32 }
33 
34 llvm::Optional<llvm::vfs::Status>
35 PreambleFileStatusCache::lookup(llvm::StringRef File) const {
36   auto I = StatCache.find(File);
37   if (I != StatCache.end())
38     return I->getValue();
39   return None;
40 }
41 
42 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
43 PreambleFileStatusCache::getProducingFS(
44     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
45   // This invalidates old status in cache if files are re-`open()`ed or
46   // re-`stat()`ed in case file status has changed during preamble build.
47   class CollectFS : public llvm::vfs::ProxyFileSystem {
48   public:
49     CollectFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
50               PreambleFileStatusCache &StatCache)
51         : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {}
52 
53     llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
54     openFileForRead(const llvm::Twine &Path) override {
55       auto File = getUnderlyingFS().openFileForRead(Path);
56       if (!File || !*File)
57         return File;
58       // Eagerly stat opened file, as the followup `status` call on the file
59       // doesn't necessarily go through this FS. This puts some extra work on
60       // preamble build, but it should be worth it as preamble can be reused
61       // many times (e.g. code completion) and the repeated status call is
62       // likely to be cached in the underlying file system anyway.
63       if (auto S = File->get()->status())
64         StatCache.update(getUnderlyingFS(), std::move(*S));
65       return File;
66     }
67 
68     llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override {
69       auto S = getUnderlyingFS().status(Path);
70       if (S)
71         StatCache.update(getUnderlyingFS(), *S);
72       return S;
73     }
74 
75   private:
76     PreambleFileStatusCache &StatCache;
77   };
78   return llvm::IntrusiveRefCntPtr<CollectFS>(
79       new CollectFS(std::move(FS), *this));
80 }
81 
82 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
83 PreambleFileStatusCache::getConsumingFS(
84     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) const {
85   class CacheVFS : public llvm::vfs::ProxyFileSystem {
86   public:
87     CacheVFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
88              const PreambleFileStatusCache &StatCache)
89         : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {}
90 
91     llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override {
92       if (auto S = StatCache.lookup(Path.str()))
93         return *S;
94       return getUnderlyingFS().status(Path);
95     }
96 
97   private:
98     const PreambleFileStatusCache &StatCache;
99   };
100   return llvm::IntrusiveRefCntPtr<CacheVFS>(new CacheVFS(std::move(FS), *this));
101 }
102 
103 } // namespace clangd
104 } // namespace clang
105