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 31namespace llvm { 32 33class MemoryBuffer; 34 35namespace vfs { 36 37class File; 38 39} // namespace vfs 40} // namespace llvm 41 42namespace clang { 43 44class FileEntryRef; 45 46namespace optional_detail { 47 48/// Forward declare a template specialization for OptionalStorage. 49template <> class OptionalStorage<clang::FileEntryRef>; 50 51} // namespace optional_detail 52 53class 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. 57class FileEntryRef { 58public: 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 *getBaseMapEntry().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(&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 /// 122 /// The second type is really a `const MapEntry *`, but that confuses 123 /// gcc5.3. Once that's no longer supported, change this back. 124 llvm::PointerUnion<FileEntry *, const void *> V; 125 126 /// Directory the file was found in. Set if and only if V is a FileEntry. 127 OptionalDirectoryEntryRef Dir; 128 129 MapValue() = delete; 130 MapValue(FileEntry &FE, DirectoryEntryRef Dir) : V(&FE), Dir(Dir) {} 131 MapValue(MapEntry &ME) : V(&ME) {} 132 }; 133 134 /// Check if RHS referenced the file in exactly the same way. 135 bool isSameRef(const FileEntryRef &RHS) const { return ME == RHS.ME; } 136 137 /// Allow FileEntryRef to degrade into 'const FileEntry*' to facilitate 138 /// incremental adoption. 139 /// 140 /// The goal is to avoid code churn due to dances like the following: 141 /// \code 142 /// // Old code. 143 /// lvalue = rvalue; 144 /// 145 /// // Temporary code from an incremental patch. 146 /// lvalue = &rvalue.getFileEntry(); 147 /// 148 /// // Final code. 149 /// lvalue = rvalue; 150 /// \endcode 151 /// 152 /// FIXME: Once FileEntryRef is "everywhere" and FileEntry::LastRef and 153 /// FileEntry::getName have been deleted, delete this implicit conversion. 154 operator const FileEntry *() const { return &getFileEntry(); } 155 156 FileEntryRef() = delete; 157 explicit FileEntryRef(const MapEntry &ME) : ME(&ME) { 158 assert(ME.second && "Expected payload"); 159 assert(ME.second->V && "Expected non-null"); 160 } 161 162 /// Expose the underlying MapEntry to simplify packing in a PointerIntPair or 163 /// PointerUnion and allow construction in Optional. 164 const clang::FileEntryRef::MapEntry &getMapEntry() const { return *ME; } 165 166 /// Retrieve the base MapEntry after redirects. 167 const MapEntry &getBaseMapEntry() const { 168 const MapEntry *ME = this->ME; 169 while (const void *Next = ME->second->V.dyn_cast<const void *>()) 170 ME = static_cast<const MapEntry *>(Next); 171 return *ME; 172 } 173 174private: 175 friend class FileMgr::MapEntryOptionalStorage<FileEntryRef>; 176 struct optional_none_tag {}; 177 178 // Private constructor for use by OptionalStorage. 179 FileEntryRef(optional_none_tag) : ME(nullptr) {} 180 bool hasOptionalValue() const { return ME; } 181 182 friend struct llvm::DenseMapInfo<FileEntryRef>; 183 struct dense_map_empty_tag {}; 184 struct dense_map_tombstone_tag {}; 185 186 // Private constructors for use by DenseMapInfo. 187 FileEntryRef(dense_map_empty_tag) 188 : ME(llvm::DenseMapInfo<const MapEntry *>::getEmptyKey()) {} 189 FileEntryRef(dense_map_tombstone_tag) 190 : ME(llvm::DenseMapInfo<const MapEntry *>::getTombstoneKey()) {} 191 bool isSpecialDenseMapKey() const { 192 return isSameRef(FileEntryRef(dense_map_empty_tag())) || 193 isSameRef(FileEntryRef(dense_map_tombstone_tag())); 194 } 195 196 const MapEntry *ME; 197}; 198 199static_assert(sizeof(FileEntryRef) == sizeof(const FileEntry *), 200 "FileEntryRef must avoid size overhead"); 201 202static_assert(std::is_trivially_copyable<FileEntryRef>::value, 203 "FileEntryRef must be trivially copyable"); 204 205using OptionalFileEntryRef = CustomizableOptional<FileEntryRef>; 206 207namespace optional_detail { 208 209/// Customize OptionalStorage<FileEntryRef> to use FileEntryRef and its 210/// optional_none_tag to keep it the size of a single pointer. 211template <> 212class OptionalStorage<clang::FileEntryRef> 213 : public clang::FileMgr::MapEntryOptionalStorage<clang::FileEntryRef> { 214 using StorageImpl = 215 clang::FileMgr::MapEntryOptionalStorage<clang::FileEntryRef>; 216 217public: 218 OptionalStorage() = default; 219 220 template <class... ArgTypes> 221 explicit OptionalStorage(std::in_place_t, ArgTypes &&...Args) 222 : StorageImpl(std::in_place_t{}, std::forward<ArgTypes>(Args)...) {} 223 224 OptionalStorage &operator=(clang::FileEntryRef Ref) { 225 StorageImpl::operator=(Ref); 226 return *this; 227 } 228}; 229 230static_assert(sizeof(OptionalFileEntryRef) == sizeof(FileEntryRef), 231 "OptionalFileEntryRef must avoid size overhead"); 232 233static_assert(std::is_trivially_copyable<OptionalFileEntryRef>::value, 234 "OptionalFileEntryRef should be trivially copyable"); 235 236} // end namespace optional_detail 237} // namespace clang 238 239namespace llvm { 240/// Specialisation of DenseMapInfo for FileEntryRef. 241template <> struct DenseMapInfo<clang::FileEntryRef> { 242 static inline clang::FileEntryRef getEmptyKey() { 243 return clang::FileEntryRef(clang::FileEntryRef::dense_map_empty_tag()); 244 } 245 246 static inline clang::FileEntryRef getTombstoneKey() { 247 return clang::FileEntryRef(clang::FileEntryRef::dense_map_tombstone_tag()); 248 } 249 250 static unsigned getHashValue(clang::FileEntryRef Val) { 251 return hash_value(Val); 252 } 253 254 static bool isEqual(clang::FileEntryRef LHS, clang::FileEntryRef RHS) { 255 // Catch the easy cases: both empty, both tombstone, or the same ref. 256 if (LHS.isSameRef(RHS)) 257 return true; 258 259 // Confirm LHS and RHS are valid. 260 if (LHS.isSpecialDenseMapKey() || RHS.isSpecialDenseMapKey()) 261 return false; 262 263 // It's safe to use operator==. 264 return LHS == RHS; 265 } 266}; 267 268} // end namespace llvm 269 270namespace clang { 271 272/// Wrapper around OptionalFileEntryRef that degrades to 'const FileEntry*', 273/// facilitating incremental patches to propagate FileEntryRef. 274/// 275/// This class can be used as return value or field where it's convenient for 276/// an OptionalFileEntryRef to degrade to a 'const FileEntry*'. The purpose 277/// is to avoid code churn due to dances like the following: 278/// \code 279/// // Old code. 280/// lvalue = rvalue; 281/// 282/// // Temporary code from an incremental patch. 283/// OptionalFileEntryRef MaybeF = rvalue; 284/// lvalue = MaybeF ? &MaybeF.getFileEntry() : nullptr; 285/// 286/// // Final code. 287/// lvalue = rvalue; 288/// \endcode 289/// 290/// FIXME: Once FileEntryRef is "everywhere" and FileEntry::LastRef and 291/// FileEntry::getName have been deleted, delete this class and replace 292/// instances with OptionalFileEntryRef. 293class OptionalFileEntryRefDegradesToFileEntryPtr : public OptionalFileEntryRef { 294public: 295 OptionalFileEntryRefDegradesToFileEntryPtr() = default; 296 OptionalFileEntryRefDegradesToFileEntryPtr( 297 OptionalFileEntryRefDegradesToFileEntryPtr &&) = default; 298 OptionalFileEntryRefDegradesToFileEntryPtr( 299 const OptionalFileEntryRefDegradesToFileEntryPtr &) = default; 300 OptionalFileEntryRefDegradesToFileEntryPtr & 301 operator=(OptionalFileEntryRefDegradesToFileEntryPtr &&) = default; 302 OptionalFileEntryRefDegradesToFileEntryPtr & 303 operator=(const OptionalFileEntryRefDegradesToFileEntryPtr &) = default; 304 305 OptionalFileEntryRefDegradesToFileEntryPtr(std::nullopt_t) {} 306 OptionalFileEntryRefDegradesToFileEntryPtr(FileEntryRef Ref) 307 : OptionalFileEntryRef(Ref) {} 308 OptionalFileEntryRefDegradesToFileEntryPtr(OptionalFileEntryRef MaybeRef) 309 : OptionalFileEntryRef(MaybeRef) {} 310 311 OptionalFileEntryRefDegradesToFileEntryPtr &operator=(std::nullopt_t) { 312 OptionalFileEntryRef::operator=(std::nullopt); 313 return *this; 314 } 315 OptionalFileEntryRefDegradesToFileEntryPtr &operator=(FileEntryRef Ref) { 316 OptionalFileEntryRef::operator=(Ref); 317 return *this; 318 } 319 OptionalFileEntryRefDegradesToFileEntryPtr & 320 operator=(OptionalFileEntryRef MaybeRef) { 321 OptionalFileEntryRef::operator=(MaybeRef); 322 return *this; 323 } 324 325 /// Degrade to 'const FileEntry *' to allow FileEntry::LastRef and 326 /// FileEntry::getName have been deleted, delete this class and replace 327 /// instances with OptionalFileEntryRef 328 operator const FileEntry *() const { 329 return has_value() ? &(*this)->getFileEntry() : nullptr; 330 } 331}; 332 333static_assert( 334 std::is_trivially_copyable< 335 OptionalFileEntryRefDegradesToFileEntryPtr>::value, 336 "OptionalFileEntryRefDegradesToFileEntryPtr should be trivially copyable"); 337 338inline bool operator==(const FileEntry *LHS, const OptionalFileEntryRef &RHS) { 339 return LHS == (RHS ? &RHS->getFileEntry() : nullptr); 340} 341inline bool operator==(const OptionalFileEntryRef &LHS, const FileEntry *RHS) { 342 return (LHS ? &LHS->getFileEntry() : nullptr) == RHS; 343} 344inline bool operator!=(const FileEntry *LHS, const OptionalFileEntryRef &RHS) { 345 return !(LHS == RHS); 346} 347inline bool operator!=(const OptionalFileEntryRef &LHS, const FileEntry *RHS) { 348 return !(LHS == RHS); 349} 350 351/// Cached information about one file (either on disk 352/// or in the virtual file system). 353/// 354/// If the 'File' member is valid, then this FileEntry has an open file 355/// descriptor for the file. 356class FileEntry { 357 friend class FileManager; 358 friend class FileEntryTestHelper; 359 FileEntry(); 360 FileEntry(const FileEntry &) = delete; 361 FileEntry &operator=(const FileEntry &) = delete; 362 363 std::string RealPathName; // Real path to the file; could be empty. 364 off_t Size = 0; // File size in bytes. 365 time_t ModTime = 0; // Modification time of file. 366 const DirectoryEntry *Dir = nullptr; // Directory file lives in. 367 llvm::sys::fs::UniqueID UniqueID; 368 unsigned UID = 0; // A unique (small) ID for the file. 369 bool IsNamedPipe = false; 370 371 /// The open file, if it is owned by the \p FileEntry. 372 mutable std::unique_ptr<llvm::vfs::File> File; 373 374 /// The file content, if it is owned by the \p FileEntry. 375 std::unique_ptr<llvm::MemoryBuffer> Content; 376 377 // First access name for this FileEntry. 378 // 379 // This is Optional only to allow delayed construction (FileEntryRef has no 380 // default constructor). It should always have a value in practice. 381 // 382 // TODO: remove this once everyone that needs a name uses FileEntryRef. 383 OptionalFileEntryRef LastRef; 384 385public: 386 ~FileEntry(); 387 StringRef getName() const { return LastRef->getName(); } 388 FileEntryRef getLastRef() const { return *LastRef; } 389 390 StringRef tryGetRealPathName() const { return RealPathName; } 391 off_t getSize() const { return Size; } 392 unsigned getUID() const { return UID; } 393 const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; } 394 time_t getModificationTime() const { return ModTime; } 395 396 /// Return the directory the file lives in. 397 const DirectoryEntry *getDir() const { return Dir; } 398 399 /// Check whether the file is a named pipe (and thus can't be opened by 400 /// the native FileManager methods). 401 bool isNamedPipe() const { return IsNamedPipe; } 402 403 void closeFile() const; 404}; 405 406off_t FileEntryRef::getSize() const { return getFileEntry().getSize(); } 407 408unsigned FileEntryRef::getUID() const { return getFileEntry().getUID(); } 409 410const llvm::sys::fs::UniqueID &FileEntryRef::getUniqueID() const { 411 return getFileEntry().getUniqueID(); 412} 413 414time_t FileEntryRef::getModificationTime() const { 415 return getFileEntry().getModificationTime(); 416} 417 418bool FileEntryRef::isNamedPipe() const { return getFileEntry().isNamedPipe(); } 419 420void FileEntryRef::closeFile() const { getFileEntry().closeFile(); } 421 422} // end namespace clang 423 424#endif // LLVM_CLANG_BASIC_FILEENTRY_H 425