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