xref: /llvm-project/clang/include/clang/Basic/FileEntry.h (revision d01c11df04ae45a3d5b08e69bb683c760bbddd54)
1 //===- clang/Basic/FileEntry.h - File references ----------------*- 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 /// \file
10 /// Defines interfaces for clang::FileEntry and clang::FileEntryRef.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_BASIC_FILEENTRY_H
15 #define LLVM_CLANG_BASIC_FILEENTRY_H
16 
17 #include "clang/Basic/CustomizableOptional.h"
18 #include "clang/Basic/DirectoryEntry.h"
19 #include "clang/Basic/LLVM.h"
20 #include "llvm/ADT/DenseMapInfo.h"
21 #include "llvm/ADT/Hashing.h"
22 #include "llvm/ADT/PointerUnion.h"
23 #include "llvm/ADT/StringMap.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/Support/ErrorOr.h"
26 #include "llvm/Support/FileSystem/UniqueID.h"
27 
28 #include <optional>
29 #include <utility>
30 
31 namespace llvm {
32 
33 class MemoryBuffer;
34 
35 namespace vfs {
36 
37 class File;
38 
39 } // namespace vfs
40 } // namespace llvm
41 
42 namespace clang {
43 
44 class FileEntryRef;
45 
46 namespace optional_detail {
47 
48 /// Forward declare a template specialization for OptionalStorage.
49 template <> class OptionalStorage<clang::FileEntryRef>;
50 
51 } // namespace optional_detail
52 
53 class FileEntry;
54 
55 /// A reference to a \c FileEntry that includes the name of the file as it was
56 /// accessed by the FileManager's client.
57 class FileEntryRef {
58 public:
59   /// The name of this FileEntry. If a VFS uses 'use-external-name', this is
60   /// the redirected name. See getRequestedName().
61   StringRef getName() const { return getBaseMapEntry().first(); }
62 
63   /// The name of this FileEntry, as originally requested without applying any
64   /// remappings for VFS 'use-external-name'.
65   ///
66   /// FIXME: this should be the semantics of getName(). See comment in
67   /// FileManager::getFileRef().
68   StringRef getNameAsRequested() const { return ME->first(); }
69 
70   const FileEntry &getFileEntry() const {
71     return *cast<FileEntry *>(getBaseMapEntry().second->V);
72   }
73 
74   // This function is used if the buffer size needs to be increased
75   // due to potential z/OS EBCDIC -> UTF-8 conversion
76   inline void updateFileEntryBufferSize(unsigned BufferSize);
77 
78   DirectoryEntryRef getDir() const { return ME->second->Dir; }
79 
80   inline off_t getSize() const;
81   inline unsigned getUID() const;
82   inline const llvm::sys::fs::UniqueID &getUniqueID() const;
83   inline time_t getModificationTime() const;
84   inline bool isNamedPipe() const;
85   inline void closeFile() const;
86 
87   /// Check if the underlying FileEntry is the same, intentially ignoring
88   /// whether the file was referenced with the same spelling of the filename.
89   friend bool operator==(const FileEntryRef &LHS, const FileEntryRef &RHS) {
90     return &LHS.getFileEntry() == &RHS.getFileEntry();
91   }
92   friend bool operator==(const FileEntry *LHS, const FileEntryRef &RHS) {
93     return LHS == &RHS.getFileEntry();
94   }
95   friend bool operator==(const FileEntryRef &LHS, const FileEntry *RHS) {
96     return &LHS.getFileEntry() == RHS;
97   }
98   friend bool operator!=(const FileEntryRef &LHS, const FileEntryRef &RHS) {
99     return !(LHS == RHS);
100   }
101   friend bool operator!=(const FileEntry *LHS, const FileEntryRef &RHS) {
102     return !(LHS == RHS);
103   }
104   friend bool operator!=(const FileEntryRef &LHS, const FileEntry *RHS) {
105     return !(LHS == RHS);
106   }
107 
108   /// Hash code is based on the FileEntry, not the specific named reference,
109   /// just like operator==.
110   friend llvm::hash_code hash_value(FileEntryRef Ref) {
111     return llvm::hash_value(&Ref.getFileEntry());
112   }
113 
114   struct MapValue;
115 
116   /// Type used in the StringMap.
117   using MapEntry = llvm::StringMapEntry<llvm::ErrorOr<MapValue>>;
118 
119   /// Type stored in the StringMap.
120   struct MapValue {
121     /// The pointer at another MapEntry is used when the FileManager should
122     /// silently forward from one name to another, which occurs in Redirecting
123     /// VFSs that use external names. In that case, the \c FileEntryRef
124     /// returned by the \c FileManager will have the external name, and not the
125     /// name that was used to lookup the file.
126     llvm::PointerUnion<FileEntry *, const MapEntry *> V;
127 
128     /// Directory the file was found in.
129     DirectoryEntryRef Dir;
130 
131     MapValue() = delete;
132     MapValue(FileEntry &FE, DirectoryEntryRef Dir) : V(&FE), Dir(Dir) {}
133     MapValue(MapEntry &ME, DirectoryEntryRef Dir) : V(&ME), Dir(Dir) {}
134   };
135 
136   /// Check if RHS referenced the file in exactly the same way.
137   bool isSameRef(const FileEntryRef &RHS) const { return ME == RHS.ME; }
138 
139   /// Allow FileEntryRef to degrade into 'const FileEntry*' to facilitate
140   /// incremental adoption.
141   ///
142   /// The goal is to avoid code churn due to dances like the following:
143   /// \code
144   /// // Old code.
145   /// lvalue = rvalue;
146   ///
147   /// // Temporary code from an incremental patch.
148   /// lvalue = &rvalue.getFileEntry();
149   ///
150   /// // Final code.
151   /// lvalue = rvalue;
152   /// \endcode
153   ///
154   /// FIXME: Once FileEntryRef is "everywhere" and FileEntry::LastRef and
155   /// FileEntry::getName have been deleted, delete this implicit conversion.
156   operator const FileEntry *() const { return &getFileEntry(); }
157 
158   FileEntryRef() = delete;
159   explicit FileEntryRef(const MapEntry &ME) : ME(&ME) {
160     assert(ME.second && "Expected payload");
161     assert(ME.second->V && "Expected non-null");
162   }
163 
164   /// Expose the underlying MapEntry to simplify packing in a PointerIntPair or
165   /// PointerUnion and allow construction in Optional.
166   const clang::FileEntryRef::MapEntry &getMapEntry() const { return *ME; }
167 
168   /// Retrieve the base MapEntry after redirects.
169   const MapEntry &getBaseMapEntry() const {
170     const MapEntry *Base = ME;
171     while (const auto *Next = Base->second->V.dyn_cast<const MapEntry *>())
172       Base = Next;
173     return *Base;
174   }
175 
176 private:
177   friend class FileMgr::MapEntryOptionalStorage<FileEntryRef>;
178   struct optional_none_tag {};
179 
180   // Private constructor for use by OptionalStorage.
181   FileEntryRef(optional_none_tag) : ME(nullptr) {}
182   bool hasOptionalValue() const { return ME; }
183 
184   friend struct llvm::DenseMapInfo<FileEntryRef>;
185   struct dense_map_empty_tag {};
186   struct dense_map_tombstone_tag {};
187 
188   // Private constructors for use by DenseMapInfo.
189   FileEntryRef(dense_map_empty_tag)
190       : ME(llvm::DenseMapInfo<const MapEntry *>::getEmptyKey()) {}
191   FileEntryRef(dense_map_tombstone_tag)
192       : ME(llvm::DenseMapInfo<const MapEntry *>::getTombstoneKey()) {}
193   bool isSpecialDenseMapKey() const {
194     return isSameRef(FileEntryRef(dense_map_empty_tag())) ||
195            isSameRef(FileEntryRef(dense_map_tombstone_tag()));
196   }
197 
198   const MapEntry *ME;
199 };
200 
201 static_assert(sizeof(FileEntryRef) == sizeof(const FileEntry *),
202               "FileEntryRef must avoid size overhead");
203 
204 static_assert(std::is_trivially_copyable<FileEntryRef>::value,
205               "FileEntryRef must be trivially copyable");
206 
207 using OptionalFileEntryRef = CustomizableOptional<FileEntryRef>;
208 
209 namespace optional_detail {
210 
211 /// Customize OptionalStorage<FileEntryRef> to use FileEntryRef and its
212 /// optional_none_tag to keep it the size of a single pointer.
213 template <>
214 class OptionalStorage<clang::FileEntryRef>
215     : public clang::FileMgr::MapEntryOptionalStorage<clang::FileEntryRef> {
216   using StorageImpl =
217       clang::FileMgr::MapEntryOptionalStorage<clang::FileEntryRef>;
218 
219 public:
220   OptionalStorage() = default;
221 
222   template <class... ArgTypes>
223   explicit OptionalStorage(std::in_place_t, ArgTypes &&...Args)
224       : StorageImpl(std::in_place_t{}, std::forward<ArgTypes>(Args)...) {}
225 
226   OptionalStorage &operator=(clang::FileEntryRef Ref) {
227     StorageImpl::operator=(Ref);
228     return *this;
229   }
230 };
231 
232 static_assert(sizeof(OptionalFileEntryRef) == sizeof(FileEntryRef),
233               "OptionalFileEntryRef must avoid size overhead");
234 
235 static_assert(std::is_trivially_copyable<OptionalFileEntryRef>::value,
236               "OptionalFileEntryRef should be trivially copyable");
237 
238 } // end namespace optional_detail
239 } // namespace clang
240 
241 namespace llvm {
242 
243 /// Specialisation of DenseMapInfo for FileEntryRef.
244 template <> struct DenseMapInfo<clang::FileEntryRef> {
245   static inline clang::FileEntryRef getEmptyKey() {
246     return clang::FileEntryRef(clang::FileEntryRef::dense_map_empty_tag());
247   }
248 
249   static inline clang::FileEntryRef getTombstoneKey() {
250     return clang::FileEntryRef(clang::FileEntryRef::dense_map_tombstone_tag());
251   }
252 
253   static unsigned getHashValue(clang::FileEntryRef Val) {
254     return hash_value(Val);
255   }
256 
257   static bool isEqual(clang::FileEntryRef LHS, clang::FileEntryRef RHS) {
258     // Catch the easy cases: both empty, both tombstone, or the same ref.
259     if (LHS.isSameRef(RHS))
260       return true;
261 
262     // Confirm LHS and RHS are valid.
263     if (LHS.isSpecialDenseMapKey() || RHS.isSpecialDenseMapKey())
264       return false;
265 
266     // It's safe to use operator==.
267     return LHS == RHS;
268   }
269 
270   /// Support for finding `const FileEntry *` in a `DenseMap<FileEntryRef, T>`.
271   /// @{
272   static unsigned getHashValue(const clang::FileEntry *Val) {
273     return llvm::hash_value(Val);
274   }
275   static bool isEqual(const clang::FileEntry *LHS, clang::FileEntryRef RHS) {
276     if (RHS.isSpecialDenseMapKey())
277       return false;
278     return LHS == RHS;
279   }
280   /// @}
281 };
282 
283 } // end namespace llvm
284 
285 namespace clang {
286 
287 inline bool operator==(const FileEntry *LHS, const OptionalFileEntryRef &RHS) {
288   return LHS == (RHS ? &RHS->getFileEntry() : nullptr);
289 }
290 inline bool operator==(const OptionalFileEntryRef &LHS, const FileEntry *RHS) {
291   return (LHS ? &LHS->getFileEntry() : nullptr) == RHS;
292 }
293 inline bool operator!=(const FileEntry *LHS, const OptionalFileEntryRef &RHS) {
294   return !(LHS == RHS);
295 }
296 inline bool operator!=(const OptionalFileEntryRef &LHS, const FileEntry *RHS) {
297   return !(LHS == RHS);
298 }
299 
300 /// Cached information about one file (either on disk
301 /// or in the virtual file system).
302 ///
303 /// If the 'File' member is valid, then this FileEntry has an open file
304 /// descriptor for the file.
305 class FileEntry {
306   friend class FileManager;
307   friend class FileEntryTestHelper;
308   FileEntry();
309   FileEntry(const FileEntry &) = delete;
310   FileEntry &operator=(const FileEntry &) = delete;
311 
312   std::string RealPathName;   // Real path to the file; could be empty.
313   off_t Size = 0;             // File size in bytes.
314   time_t ModTime = 0;         // Modification time of file.
315   const DirectoryEntry *Dir = nullptr; // Directory file lives in.
316   llvm::sys::fs::UniqueID UniqueID;
317   unsigned UID = 0; // A unique (small) ID for the file.
318   bool IsNamedPipe = false;
319 
320   /// The open file, if it is owned by the \p FileEntry.
321   mutable std::unique_ptr<llvm::vfs::File> File;
322 
323   /// The file content, if it is owned by the \p FileEntry.
324   std::unique_ptr<llvm::MemoryBuffer> Content;
325 
326 public:
327   ~FileEntry();
328 
329   StringRef tryGetRealPathName() const { return RealPathName; }
330   off_t getSize() const { return Size; }
331   // Size may increase due to potential z/OS EBCDIC -> UTF-8 conversion.
332   void setSize(off_t NewSize) { Size = NewSize; }
333   unsigned getUID() const { return UID; }
334   const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; }
335   time_t getModificationTime() const { return ModTime; }
336 
337   /// Return the directory the file lives in.
338   const DirectoryEntry *getDir() const { return Dir; }
339 
340   /// Check whether the file is a named pipe (and thus can't be opened by
341   /// the native FileManager methods).
342   bool isNamedPipe() const { return IsNamedPipe; }
343 
344   void closeFile() const;
345 };
346 
347 off_t FileEntryRef::getSize() const { return getFileEntry().getSize(); }
348 
349 unsigned FileEntryRef::getUID() const { return getFileEntry().getUID(); }
350 
351 const llvm::sys::fs::UniqueID &FileEntryRef::getUniqueID() const {
352   return getFileEntry().getUniqueID();
353 }
354 
355 time_t FileEntryRef::getModificationTime() const {
356   return getFileEntry().getModificationTime();
357 }
358 
359 bool FileEntryRef::isNamedPipe() const { return getFileEntry().isNamedPipe(); }
360 
361 void FileEntryRef::closeFile() const { getFileEntry().closeFile(); }
362 
363 void FileEntryRef::updateFileEntryBufferSize(unsigned BufferSize) {
364   cast<FileEntry *>(getBaseMapEntry().second->V)->setSize(BufferSize);
365 }
366 
367 } // end namespace clang
368 
369 #endif // LLVM_CLANG_BASIC_FILEENTRY_H
370