1 //===--- FS.cpp - File system related utils ----------------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
10 #include "clang/Basic/LLVM.h"
11 #include "llvm/ADT/None.h"
12 #include "llvm/Support/Path.h"
13 #include "llvm/Support/VirtualFileSystem.h"
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());
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
))
31 llvm::sys::path::remove_dots(PathStore
, /*remove_dot_dot=*/true);
32 // Do not cache status for the main file.
33 if (PathStore
== MainFilePath
)
35 // Stores the latest status in cache as it can change in a preamble build.
36 StatCache
.insert({PathStore
, std::move(S
)});
39 llvm::Optional
<llvm::vfs::Status
>
40 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);
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
);
53 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
>
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
{
60 CollectFS(llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> FS
,
61 PreambleFileStatusCache
&StatCache
)
62 : ProxyFileSystem(std::move(FS
)), StatCache(StatCache
) {}
64 llvm::ErrorOr
<std::unique_ptr
<llvm::vfs::File
>>
65 openFileForRead(const llvm::Twine
&Path
) override
{
66 auto File
= getUnderlyingFS().openFileForRead(Path
);
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
));
79 llvm::ErrorOr
<llvm::vfs::Status
> status(const llvm::Twine
&Path
) override
{
80 auto S
= getUnderlyingFS().status(Path
);
82 StatCache
.update(getUnderlyingFS(), *S
);
87 PreambleFileStatusCache
&StatCache
;
89 return llvm::IntrusiveRefCntPtr
<CollectFS
>(
90 new CollectFS(std::move(FS
), *this));
93 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
>
94 PreambleFileStatusCache::getConsumingFS(
95 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> FS
) const {
96 class CacheVFS
: public llvm::vfs::ProxyFileSystem
{
98 CacheVFS(llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> FS
,
99 const PreambleFileStatusCache
&StatCache
)
100 : ProxyFileSystem(std::move(FS
)), StatCache(StatCache
) {}
102 llvm::ErrorOr
<llvm::vfs::Status
> status(const llvm::Twine
&Path
) override
{
103 if (auto S
= StatCache
.lookup(Path
.str()))
105 return getUnderlyingFS().status(Path
);
109 const PreambleFileStatusCache
&StatCache
;
111 return llvm::IntrusiveRefCntPtr
<CacheVFS
>(new CacheVFS(std::move(FS
), *this));
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();
120 } // namespace clangd