aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/include/clang/Basic/FileEntry.h
blob: bb4d51107a90750301e231010b7228d43e12d6ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
#pragma once

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif

//===- clang/Basic/FileEntry.h - File references ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Defines interfaces for clang::FileEntry and clang::FileEntryRef.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_BASIC_FILEENTRY_H
#define LLVM_CLANG_BASIC_FILEENTRY_H

#include "clang/Basic/CustomizableOptional.h"
#include "clang/Basic/DirectoryEntry.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem/UniqueID.h"

#include <optional>
#include <utility>

namespace llvm {

class MemoryBuffer;

namespace vfs {

class File;

} // namespace vfs
} // namespace llvm

namespace clang {

class FileEntryRef;

namespace optional_detail {

/// Forward declare a template specialization for OptionalStorage.
template <> class OptionalStorage<clang::FileEntryRef>;

} // namespace optional_detail

class FileEntry;

/// A reference to a \c FileEntry that includes the name of the file as it was
/// accessed by the FileManager's client.
class FileEntryRef {
public:
  /// The name of this FileEntry. If a VFS uses 'use-external-name', this is
  /// the redirected name. See getRequestedName().
  StringRef getName() const { return getBaseMapEntry().first(); }

  /// The name of this FileEntry, as originally requested without applying any
  /// remappings for VFS 'use-external-name'.
  ///
  /// FIXME: this should be the semantics of getName(). See comment in
  /// FileManager::getFileRef().
  StringRef getNameAsRequested() const { return ME->first(); }

  const FileEntry &getFileEntry() const {
    return *getBaseMapEntry().second->V.get<FileEntry *>();
  }
  DirectoryEntryRef getDir() const { return *getBaseMapEntry().second->Dir; }

  inline off_t getSize() const;
  inline unsigned getUID() const;
  inline const llvm::sys::fs::UniqueID &getUniqueID() const;
  inline time_t getModificationTime() const;
  inline bool isNamedPipe() const;
  inline void closeFile() const;

  /// Check if the underlying FileEntry is the same, intentially ignoring
  /// whether the file was referenced with the same spelling of the filename.
  friend bool operator==(const FileEntryRef &LHS, const FileEntryRef &RHS) {
    return &LHS.getFileEntry() == &RHS.getFileEntry();
  }
  friend bool operator==(const FileEntry *LHS, const FileEntryRef &RHS) {
    return LHS == &RHS.getFileEntry();
  }
  friend bool operator==(const FileEntryRef &LHS, const FileEntry *RHS) {
    return &LHS.getFileEntry() == RHS;
  }
  friend bool operator!=(const FileEntryRef &LHS, const FileEntryRef &RHS) {
    return !(LHS == RHS);
  }
  friend bool operator!=(const FileEntry *LHS, const FileEntryRef &RHS) {
    return !(LHS == RHS);
  }
  friend bool operator!=(const FileEntryRef &LHS, const FileEntry *RHS) {
    return !(LHS == RHS);
  }

  /// Hash code is based on the FileEntry, not the specific named reference,
  /// just like operator==.
  friend llvm::hash_code hash_value(FileEntryRef Ref) {
    return llvm::hash_value(&Ref.getFileEntry());
  }

  struct MapValue;

  /// Type used in the StringMap.
  using MapEntry = llvm::StringMapEntry<llvm::ErrorOr<MapValue>>;

  /// Type stored in the StringMap.
  struct MapValue {
    /// The pointer at another MapEntry is used when the FileManager should
    /// silently forward from one name to another, which occurs in Redirecting
    /// VFSs that use external names. In that case, the \c FileEntryRef
    /// returned by the \c FileManager will have the external name, and not the
    /// name that was used to lookup the file.
    ///
    /// The second type is really a `const MapEntry *`, but that confuses
    /// gcc5.3.  Once that's no longer supported, change this back.
    llvm::PointerUnion<FileEntry *, const void *> V;

    /// Directory the file was found in. Set if and only if V is a FileEntry.
    OptionalDirectoryEntryRef Dir;

    MapValue() = delete;
    MapValue(FileEntry &FE, DirectoryEntryRef Dir) : V(&FE), Dir(Dir) {}
    MapValue(MapEntry &ME) : V(&ME) {}
  };

  /// Check if RHS referenced the file in exactly the same way.
  bool isSameRef(const FileEntryRef &RHS) const { return ME == RHS.ME; }

  /// Allow FileEntryRef to degrade into 'const FileEntry*' to facilitate
  /// incremental adoption.
  ///
  /// The goal is to avoid code churn due to dances like the following:
  /// \code
  /// // Old code.
  /// lvalue = rvalue;
  ///
  /// // Temporary code from an incremental patch.
  /// lvalue = &rvalue.getFileEntry();
  ///
  /// // Final code.
  /// lvalue = rvalue;
  /// \endcode
  ///
  /// FIXME: Once FileEntryRef is "everywhere" and FileEntry::LastRef and
  /// FileEntry::getName have been deleted, delete this implicit conversion.
  operator const FileEntry *() const { return &getFileEntry(); }

  FileEntryRef() = delete;
  explicit FileEntryRef(const MapEntry &ME) : ME(&ME) {
    assert(ME.second && "Expected payload");
    assert(ME.second->V && "Expected non-null");
  }

  /// Expose the underlying MapEntry to simplify packing in a PointerIntPair or
  /// PointerUnion and allow construction in Optional.
  const clang::FileEntryRef::MapEntry &getMapEntry() const { return *ME; }

  /// Retrieve the base MapEntry after redirects.
  const MapEntry &getBaseMapEntry() const {
    const MapEntry *ME = this->ME;
    while (const void *Next = ME->second->V.dyn_cast<const void *>())
      ME = static_cast<const MapEntry *>(Next);
    return *ME;
  }

private:
  friend class FileMgr::MapEntryOptionalStorage<FileEntryRef>;
  struct optional_none_tag {};

  // Private constructor for use by OptionalStorage.
  FileEntryRef(optional_none_tag) : ME(nullptr) {}
  bool hasOptionalValue() const { return ME; }

  friend struct llvm::DenseMapInfo<FileEntryRef>;
  struct dense_map_empty_tag {};
  struct dense_map_tombstone_tag {};

  // Private constructors for use by DenseMapInfo.
  FileEntryRef(dense_map_empty_tag)
      : ME(llvm::DenseMapInfo<const MapEntry *>::getEmptyKey()) {}
  FileEntryRef(dense_map_tombstone_tag)
      : ME(llvm::DenseMapInfo<const MapEntry *>::getTombstoneKey()) {}
  bool isSpecialDenseMapKey() const {
    return isSameRef(FileEntryRef(dense_map_empty_tag())) ||
           isSameRef(FileEntryRef(dense_map_tombstone_tag()));
  }

  const MapEntry *ME;
};

static_assert(sizeof(FileEntryRef) == sizeof(const FileEntry *),
              "FileEntryRef must avoid size overhead");

static_assert(std::is_trivially_copyable<FileEntryRef>::value,
              "FileEntryRef must be trivially copyable");

using OptionalFileEntryRef = CustomizableOptional<FileEntryRef>;

namespace optional_detail {

/// Customize OptionalStorage<FileEntryRef> to use FileEntryRef and its
/// optional_none_tag to keep it the size of a single pointer.
template <>
class OptionalStorage<clang::FileEntryRef>
    : public clang::FileMgr::MapEntryOptionalStorage<clang::FileEntryRef> {
  using StorageImpl =
      clang::FileMgr::MapEntryOptionalStorage<clang::FileEntryRef>;

public:
  OptionalStorage() = default;

  template <class... ArgTypes>
  explicit OptionalStorage(std::in_place_t, ArgTypes &&...Args)
      : StorageImpl(std::in_place_t{}, std::forward<ArgTypes>(Args)...) {}

  OptionalStorage &operator=(clang::FileEntryRef Ref) {
    StorageImpl::operator=(Ref);
    return *this;
  }
};

static_assert(sizeof(OptionalFileEntryRef) == sizeof(FileEntryRef),
              "OptionalFileEntryRef must avoid size overhead");

static_assert(std::is_trivially_copyable<OptionalFileEntryRef>::value,
              "OptionalFileEntryRef should be trivially copyable");

} // end namespace optional_detail
} // namespace clang

namespace llvm {
/// Specialisation of DenseMapInfo for FileEntryRef.
template <> struct DenseMapInfo<clang::FileEntryRef> {
  static inline clang::FileEntryRef getEmptyKey() {
    return clang::FileEntryRef(clang::FileEntryRef::dense_map_empty_tag());
  }

  static inline clang::FileEntryRef getTombstoneKey() {
    return clang::FileEntryRef(clang::FileEntryRef::dense_map_tombstone_tag());
  }

  static unsigned getHashValue(clang::FileEntryRef Val) {
    return hash_value(Val);
  }

  static bool isEqual(clang::FileEntryRef LHS, clang::FileEntryRef RHS) {
    // Catch the easy cases: both empty, both tombstone, or the same ref.
    if (LHS.isSameRef(RHS))
      return true;

    // Confirm LHS and RHS are valid.
    if (LHS.isSpecialDenseMapKey() || RHS.isSpecialDenseMapKey())
      return false;

    // It's safe to use operator==.
    return LHS == RHS;
  }
};

} // end namespace llvm

namespace clang {

/// Wrapper around OptionalFileEntryRef that degrades to 'const FileEntry*',
/// facilitating incremental patches to propagate FileEntryRef.
///
/// This class can be used as return value or field where it's convenient for
/// an OptionalFileEntryRef to degrade to a 'const FileEntry*'. The purpose
/// is to avoid code churn due to dances like the following:
/// \code
/// // Old code.
/// lvalue = rvalue;
///
/// // Temporary code from an incremental patch.
/// OptionalFileEntryRef MaybeF = rvalue;
/// lvalue = MaybeF ? &MaybeF.getFileEntry() : nullptr;
///
/// // Final code.
/// lvalue = rvalue;
/// \endcode
///
/// FIXME: Once FileEntryRef is "everywhere" and FileEntry::LastRef and
/// FileEntry::getName have been deleted, delete this class and replace
/// instances with OptionalFileEntryRef.
class OptionalFileEntryRefDegradesToFileEntryPtr : public OptionalFileEntryRef {
public:
  OptionalFileEntryRefDegradesToFileEntryPtr() = default;
  OptionalFileEntryRefDegradesToFileEntryPtr(
      OptionalFileEntryRefDegradesToFileEntryPtr &&) = default;
  OptionalFileEntryRefDegradesToFileEntryPtr(
      const OptionalFileEntryRefDegradesToFileEntryPtr &) = default;
  OptionalFileEntryRefDegradesToFileEntryPtr &
  operator=(OptionalFileEntryRefDegradesToFileEntryPtr &&) = default;
  OptionalFileEntryRefDegradesToFileEntryPtr &
  operator=(const OptionalFileEntryRefDegradesToFileEntryPtr &) = default;

  OptionalFileEntryRefDegradesToFileEntryPtr(std::nullopt_t) {}
  OptionalFileEntryRefDegradesToFileEntryPtr(FileEntryRef Ref)
      : OptionalFileEntryRef(Ref) {}
  OptionalFileEntryRefDegradesToFileEntryPtr(OptionalFileEntryRef MaybeRef)
      : OptionalFileEntryRef(MaybeRef) {}

  OptionalFileEntryRefDegradesToFileEntryPtr &operator=(std::nullopt_t) {
    OptionalFileEntryRef::operator=(std::nullopt);
    return *this;
  }
  OptionalFileEntryRefDegradesToFileEntryPtr &operator=(FileEntryRef Ref) {
    OptionalFileEntryRef::operator=(Ref);
    return *this;
  }
  OptionalFileEntryRefDegradesToFileEntryPtr &
  operator=(OptionalFileEntryRef MaybeRef) {
    OptionalFileEntryRef::operator=(MaybeRef);
    return *this;
  }

  /// Degrade to 'const FileEntry *' to allow  FileEntry::LastRef and
  /// FileEntry::getName have been deleted, delete this class and replace
  /// instances with OptionalFileEntryRef
  operator const FileEntry *() const {
    return has_value() ? &(*this)->getFileEntry() : nullptr;
  }
};

static_assert(
    std::is_trivially_copyable<
        OptionalFileEntryRefDegradesToFileEntryPtr>::value,
    "OptionalFileEntryRefDegradesToFileEntryPtr should be trivially copyable");

inline bool operator==(const FileEntry *LHS, const OptionalFileEntryRef &RHS) {
  return LHS == (RHS ? &RHS->getFileEntry() : nullptr);
}
inline bool operator==(const OptionalFileEntryRef &LHS, const FileEntry *RHS) {
  return (LHS ? &LHS->getFileEntry() : nullptr) == RHS;
}
inline bool operator!=(const FileEntry *LHS, const OptionalFileEntryRef &RHS) {
  return !(LHS == RHS);
}
inline bool operator!=(const OptionalFileEntryRef &LHS, const FileEntry *RHS) {
  return !(LHS == RHS);
}

/// Cached information about one file (either on disk
/// or in the virtual file system).
///
/// If the 'File' member is valid, then this FileEntry has an open file
/// descriptor for the file.
class FileEntry {
  friend class FileManager;
  friend class FileEntryTestHelper;
  FileEntry();
  FileEntry(const FileEntry &) = delete;
  FileEntry &operator=(const FileEntry &) = delete;

  std::string RealPathName;   // Real path to the file; could be empty.
  off_t Size = 0;             // File size in bytes.
  time_t ModTime = 0;         // Modification time of file.
  const DirectoryEntry *Dir = nullptr; // Directory file lives in.
  llvm::sys::fs::UniqueID UniqueID;
  unsigned UID = 0; // A unique (small) ID for the file.
  bool IsNamedPipe = false;

  /// The open file, if it is owned by the \p FileEntry.
  mutable std::unique_ptr<llvm::vfs::File> File;

  /// The file content, if it is owned by the \p FileEntry.
  std::unique_ptr<llvm::MemoryBuffer> Content;

  // First access name for this FileEntry.
  //
  // This is Optional only to allow delayed construction (FileEntryRef has no
  // default constructor). It should always have a value in practice.
  //
  // TODO: remove this once everyone that needs a name uses FileEntryRef.
  OptionalFileEntryRef LastRef;

public:
  ~FileEntry();
  StringRef getName() const { return LastRef->getName(); }
  FileEntryRef getLastRef() const { return *LastRef; }

  StringRef tryGetRealPathName() const { return RealPathName; }
  off_t getSize() const { return Size; }
  unsigned getUID() const { return UID; }
  const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; }
  time_t getModificationTime() const { return ModTime; }

  /// Return the directory the file lives in.
  const DirectoryEntry *getDir() const { return Dir; }

  /// Check whether the file is a named pipe (and thus can't be opened by
  /// the native FileManager methods).
  bool isNamedPipe() const { return IsNamedPipe; }

  void closeFile() const;
};

off_t FileEntryRef::getSize() const { return getFileEntry().getSize(); }

unsigned FileEntryRef::getUID() const { return getFileEntry().getUID(); }

const llvm::sys::fs::UniqueID &FileEntryRef::getUniqueID() const {
  return getFileEntry().getUniqueID();
}

time_t FileEntryRef::getModificationTime() const {
  return getFileEntry().getModificationTime();
}

bool FileEntryRef::isNamedPipe() const { return getFileEntry().isNamedPipe(); }

void FileEntryRef::closeFile() const { getFileEntry().closeFile(); }

} // end namespace clang

#endif // LLVM_CLANG_BASIC_FILEENTRY_H

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif