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 *>(Val: 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(ptr: &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(RHS: FileEntryRef(dense_map_empty_tag())) || |
195 | isSameRef(RHS: 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(Ref: 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(ptr: 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 *>(Val: getBaseMapEntry().second->V)->setSize(BufferSize); |
365 | } |
366 | |
367 | } // end namespace clang |
368 | |
369 | #endif // LLVM_CLANG_BASIC_FILEENTRY_H |
370 | |