124 lines · cpp
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-exception6//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(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(PathStore))32 return;33 llvm::sys::path::remove_dots(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({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(PathLookup, /*remove_dot_dot=*/true);47 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);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 or59 // 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 file72 // doesn't necessarily go through this FS. This puts some extra work on73 // preamble build, but it should be worth it as preamble can be reused74 // many times (e.g. code completion) and the repeated status call is75 // 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());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(getUnderlyingFS(), *S, 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(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(CanonPath, /*remove_dot_dot=*/true);119 return CanonPath.str().str();120}121 122} // namespace clangd123} // namespace clang124