• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "llvm/Support/VirtualFileSystem.h"
14 
15 namespace clang {
16 namespace clangd {
17 
PreambleFileStatusCache(llvm::StringRef MainFilePath)18 PreambleFileStatusCache::PreambleFileStatusCache(llvm::StringRef MainFilePath){
19   assert(llvm::sys::path::is_absolute(MainFilePath));
20   llvm::SmallString<256> MainFileCanonical(MainFilePath);
21   llvm::sys::path::remove_dots(MainFileCanonical, /*remove_dot_dot=*/true);
22   this->MainFilePath = std::string(MainFileCanonical.str());
23 }
24 
update(const llvm::vfs::FileSystem & FS,llvm::vfs::Status S)25 void PreambleFileStatusCache::update(const llvm::vfs::FileSystem &FS,
26                                      llvm::vfs::Status S) {
27   // Canonicalize path for later lookup, which is usually by absolute path.
28   llvm::SmallString<32> PathStore(S.getName());
29   if (FS.makeAbsolute(PathStore))
30     return;
31   llvm::sys::path::remove_dots(PathStore, /*remove_dot_dot=*/true);
32   // Do not cache status for the main file.
33   if (PathStore == MainFilePath)
34     return;
35   // Stores the latest status in cache as it can change in a preamble build.
36   StatCache.insert({PathStore, std::move(S)});
37 }
38 
39 llvm::Optional<llvm::vfs::Status>
lookup(llvm::StringRef File) const40 PreambleFileStatusCache::lookup(llvm::StringRef File) const {
41   // Canonicalize to match the cached form.
42   // Lookup tends to be first by absolute path, so no need to make absolute.
43   llvm::SmallString<256> PathLookup(File);
44   llvm::sys::path::remove_dots(PathLookup, /*remove_dot_dot=*/true);
45 
46   auto I = StatCache.find(PathLookup);
47   if (I != StatCache.end())
48     // Returned Status name should always match the requested File.
49     return llvm::vfs::Status::copyWithNewName(I->getValue(), File);
50   return None;
51 }
52 
53 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
getProducingFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)54 PreambleFileStatusCache::getProducingFS(
55     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
56   // This invalidates old status in cache if files are re-`open()`ed or
57   // re-`stat()`ed in case file status has changed during preamble build.
58   class CollectFS : public llvm::vfs::ProxyFileSystem {
59   public:
60     CollectFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
61               PreambleFileStatusCache &StatCache)
62         : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {}
63 
64     llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
65     openFileForRead(const llvm::Twine &Path) override {
66       auto File = getUnderlyingFS().openFileForRead(Path);
67       if (!File || !*File)
68         return File;
69       // Eagerly stat opened file, as the followup `status` call on the file
70       // doesn't necessarily go through this FS. This puts some extra work on
71       // preamble build, but it should be worth it as preamble can be reused
72       // many times (e.g. code completion) and the repeated status call is
73       // likely to be cached in the underlying file system anyway.
74       if (auto S = File->get()->status())
75         StatCache.update(getUnderlyingFS(), std::move(*S));
76       return File;
77     }
78 
79     llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override {
80       auto S = getUnderlyingFS().status(Path);
81       if (S)
82         StatCache.update(getUnderlyingFS(), *S);
83       return S;
84     }
85 
86   private:
87     PreambleFileStatusCache &StatCache;
88   };
89   return llvm::IntrusiveRefCntPtr<CollectFS>(
90       new CollectFS(std::move(FS), *this));
91 }
92 
93 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
getConsumingFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) const94 PreambleFileStatusCache::getConsumingFS(
95     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) const {
96   class CacheVFS : public llvm::vfs::ProxyFileSystem {
97   public:
98     CacheVFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
99              const PreambleFileStatusCache &StatCache)
100         : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {}
101 
102     llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override {
103       if (auto S = StatCache.lookup(Path.str()))
104         return *S;
105       return getUnderlyingFS().status(Path);
106     }
107 
108   private:
109     const PreambleFileStatusCache &StatCache;
110   };
111   return llvm::IntrusiveRefCntPtr<CacheVFS>(new CacheVFS(std::move(FS), *this));
112 }
113 
removeDots(PathRef File)114 Path removeDots(PathRef File) {
115   llvm::SmallString<128> CanonPath(File);
116   llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
117   return CanonPath.str().str();
118 }
119 
120 } // namespace clangd
121 } // namespace clang
122