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