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/Support/Path.h"
12#include "llvm/Support/VirtualFileSystem.h"
13#include <optional>
14#include <utility>
15
16namespace clang {
17namespace clangd {
18
19PreambleFileStatusCache::PreambleFileStatusCache(llvm::StringRef MainFilePath){
20 assert(llvm::sys::path::is_absolute(MainFilePath));
21 llvm::SmallString<256> MainFileCanonical(MainFilePath);
22 llvm::sys::path::remove_dots(path&: MainFileCanonical, /*remove_dot_dot=*/true);
23 this->MainFilePath = std::string(MainFileCanonical);
24}
25
26void PreambleFileStatusCache::update(const llvm::vfs::FileSystem &FS,
27 llvm::vfs::Status S,
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(Path&: PathStore))
32 return;
33 llvm::sys::path::remove_dots(path&: PathStore, /*remove_dot_dot=*/true);
34 // Do not cache status for the main file.
35 if (PathStore == MainFilePath)
36 return;
37 // Stores the latest status in cache as it can change in a preamble build.
38 StatCache.insert(KV: {PathStore, std::move(S)});
39}
40
41std::optional<llvm::vfs::Status>
42PreambleFileStatusCache::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(path&: PathLookup, /*remove_dot_dot=*/true);
47
48 auto I = StatCache.find(Key: PathLookup);
49 if (I != StatCache.end())
50 // Returned Status name should always match the requested File.
51 return llvm::vfs::Status::copyWithNewName(In: I->getValue(), NewName: File);
52 return std::nullopt;
53}
54
55llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
56PreambleFileStatusCache::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 {
61 public:
62 CollectFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
63 PreambleFileStatusCache &StatCache)
64 : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {}
65
66 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
67 openFileForRead(const llvm::Twine &Path) override {
68 auto File = getUnderlyingFS().openFileForRead(Path);
69 if (!File || !*File)
70 return File;
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(FS: getUnderlyingFS(), S: std::move(*S), File: Path.str());
78 return File;
79 }
80
81 llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override {
82 auto S = getUnderlyingFS().status(Path);
83 if (S)
84 StatCache.update(FS: getUnderlyingFS(), S: *S, File: Path.str());
85 return S;
86 }
87
88 private:
89 PreambleFileStatusCache &StatCache;
90 };
91 return llvm::IntrusiveRefCntPtr<CollectFS>(
92 new CollectFS(std::move(FS), *this));
93}
94
95llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
96PreambleFileStatusCache::getConsumingFS(
97 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) const {
98 class CacheVFS : public llvm::vfs::ProxyFileSystem {
99 public:
100 CacheVFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
101 const PreambleFileStatusCache &StatCache)
102 : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {}
103
104 llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override {
105 if (auto S = StatCache.lookup(File: Path.str()))
106 return *S;
107 return getUnderlyingFS().status(Path);
108 }
109
110 private:
111 const PreambleFileStatusCache &StatCache;
112 };
113 return llvm::IntrusiveRefCntPtr<CacheVFS>(new CacheVFS(std::move(FS), *this));
114}
115
116Path removeDots(PathRef File) {
117 llvm::SmallString<128> CanonPath(File);
118 llvm::sys::path::remove_dots(path&: CanonPath, /*remove_dot_dot=*/true);
119 return CanonPath.str().str();
120}
121
122} // namespace clangd
123} // namespace clang
124

source code of clang-tools-extra/clangd/FS.cpp