summaryrefslogtreecommitdiff
path: root/include/clang/Basic/FileManager.h
blob: 217736b698b85bde371f5dcb25bad30b8eb7c1b7 (plain)
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
//===--- FileManager.h - File System Probing and Caching --------*- 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 the clang::FileManager interface and associated types.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_BASIC_FILEMANAGER_H
#define LLVM_CLANG_BASIC_FILEMANAGER_H

#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <ctime>
#include <map>
#include <memory>
#include <string>

namespace llvm {

class MemoryBuffer;

} // end namespace llvm

namespace clang {

class FileSystemStatCache;

/// Cached information about one directory (either on disk or in
/// the virtual file system).
class DirectoryEntry {
  friend class FileManager;

  // FIXME: We should not be storing a directory entry name here.
  StringRef Name; // Name of the directory.

public:
  StringRef getName() const { return Name; }
};

/// A reference to a \c DirectoryEntry  that includes the name of the directory
/// as it was accessed by the FileManager's client.
class DirectoryEntryRef {
public:
  const DirectoryEntry &getDirEntry() const { return *Entry->getValue(); }

  StringRef getName() const { return Entry->getKey(); }

private:
  friend class FileManager;

  DirectoryEntryRef(
      llvm::StringMapEntry<llvm::ErrorOr<DirectoryEntry &>> *Entry)
      : Entry(Entry) {}

  const llvm::StringMapEntry<llvm::ErrorOr<DirectoryEntry &>> *Entry;
};

/// 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;

  StringRef Name;             // Name of the file.
  std::string RealPathName;   // Real path to the file; could be empty.
  off_t Size;                 // File size in bytes.
  time_t ModTime;             // Modification time of file.
  const DirectoryEntry *Dir;  // Directory file lives in.
  llvm::sys::fs::UniqueID UniqueID;
  unsigned UID;               // A unique (small) ID for the file.
  bool IsNamedPipe;
  bool IsValid;               // Is this \c FileEntry initialized and valid?

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

public:
  FileEntry()
      : UniqueID(0, 0), IsNamedPipe(false), IsValid(false)
  {}

  FileEntry(const FileEntry &) = delete;
  FileEntry &operator=(const FileEntry &) = delete;

  StringRef getName() const { return Name; }
  StringRef tryGetRealPathName() const { return RealPathName; }
  bool isValid() const { return IsValid; }
  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; }

  bool operator<(const FileEntry &RHS) const { return UniqueID < RHS.UniqueID; }

  /// 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 {
    File.reset(); // rely on destructor to close File
  }

  // Only for use in tests to see if deferred opens are happening, rather than
  // relying on RealPathName being empty.
  bool isOpenForTests() const { return File != nullptr; }
};

/// 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:
  FileEntryRef() = delete;
  FileEntryRef(StringRef Name, const FileEntry &Entry)
      : Name(Name), Entry(&Entry) {}

  const StringRef getName() const { return Name; }

  bool isValid() const { return Entry->isValid(); }

  const FileEntry &getFileEntry() const { return *Entry; }

  off_t getSize() const { return Entry->getSize(); }

  unsigned getUID() const { return Entry->getUID(); }

  const llvm::sys::fs::UniqueID &getUniqueID() const {
    return Entry->getUniqueID();
  }

  time_t getModificationTime() const { return Entry->getModificationTime(); }

  friend bool operator==(const FileEntryRef &LHS, const FileEntryRef &RHS) {
    return LHS.Entry == RHS.Entry && LHS.Name == RHS.Name;
  }
  friend bool operator!=(const FileEntryRef &LHS, const FileEntryRef &RHS) {
    return !(LHS == RHS);
  }

private:
  StringRef Name;
  const FileEntry *Entry;
};

/// Implements support for file system lookup, file system caching,
/// and directory search management.
///
/// This also handles more advanced properties, such as uniquing files based
/// on "inode", so that a file with two names (e.g. symlinked) will be treated
/// as a single file.
///
class FileManager : public RefCountedBase<FileManager> {
  IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
  FileSystemOptions FileSystemOpts;

  /// Cache for existing real directories.
  std::map<llvm::sys::fs::UniqueID, DirectoryEntry> UniqueRealDirs;

  /// Cache for existing real files.
  std::map<llvm::sys::fs::UniqueID, FileEntry> UniqueRealFiles;

  /// The virtual directories that we have allocated.
  ///
  /// For each virtual file (e.g. foo/bar/baz.cpp), we add all of its parent
  /// directories (foo/ and foo/bar/) here.
  SmallVector<std::unique_ptr<DirectoryEntry>, 4> VirtualDirectoryEntries;
  /// The virtual files that we have allocated.
  SmallVector<std::unique_ptr<FileEntry>, 4> VirtualFileEntries;

  /// A set of files that bypass the maps and uniquing.  They can have
  /// conflicting filenames.
  SmallVector<std::unique_ptr<FileEntry>, 0> BypassFileEntries;

  /// A cache that maps paths to directory entries (either real or
  /// virtual) we have looked up, or an error that occurred when we looked up
  /// the directory.
  ///
  /// The actual Entries for real directories/files are
  /// owned by UniqueRealDirs/UniqueRealFiles above, while the Entries
  /// for virtual directories/files are owned by
  /// VirtualDirectoryEntries/VirtualFileEntries above.
  ///
  llvm::StringMap<llvm::ErrorOr<DirectoryEntry &>, llvm::BumpPtrAllocator>
  SeenDirEntries;

  /// A reference to the file entry that is associated with a particular
  /// filename, or a reference to another filename that should be looked up
  /// instead of the accessed filename.
  ///
  /// The reference to another filename is specifically useful for 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.
  using SeenFileEntryOrRedirect =
      llvm::PointerUnion<FileEntry *, const StringRef *>;

  /// A cache that maps paths to file entries (either real or
  /// virtual) we have looked up, or an error that occurred when we looked up
  /// the file.
  ///
  /// \see SeenDirEntries
  llvm::StringMap<llvm::ErrorOr<SeenFileEntryOrRedirect>,
                  llvm::BumpPtrAllocator>
      SeenFileEntries;

  /// The canonical names of directories.
  llvm::DenseMap<const DirectoryEntry *, llvm::StringRef> CanonicalDirNames;

  /// Storage for canonical names that we have computed.
  llvm::BumpPtrAllocator CanonicalNameStorage;

  /// Each FileEntry we create is assigned a unique ID #.
  ///
  unsigned NextFileUID;

  // Statistics.
  unsigned NumDirLookups, NumFileLookups;
  unsigned NumDirCacheMisses, NumFileCacheMisses;

  // Caching.
  std::unique_ptr<FileSystemStatCache> StatCache;

  std::error_code getStatValue(StringRef Path, llvm::vfs::Status &Status,
                               bool isFile,
                               std::unique_ptr<llvm::vfs::File> *F);

  /// Add all ancestors of the given path (pointing to either a file
  /// or a directory) as virtual directories.
  void addAncestorsAsVirtualDirs(StringRef Path);

  /// Fills the RealPathName in file entry.
  void fillRealPathName(FileEntry *UFE, llvm::StringRef FileName);

public:
  /// Construct a file manager, optionally with a custom VFS.
  ///
  /// \param FS if non-null, the VFS to use.  Otherwise uses
  /// llvm::vfs::getRealFileSystem().
  FileManager(const FileSystemOptions &FileSystemOpts,
              IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = nullptr);
  ~FileManager();

  /// Installs the provided FileSystemStatCache object within
  /// the FileManager.
  ///
  /// Ownership of this object is transferred to the FileManager.
  ///
  /// \param statCache the new stat cache to install. Ownership of this
  /// object is transferred to the FileManager.
  void setStatCache(std::unique_ptr<FileSystemStatCache> statCache);

  /// Removes the FileSystemStatCache object from the manager.
  void clearStatCache();

  /// Returns the number of unique real file entries cached by the file manager.
  size_t getNumUniqueRealFiles() const { return UniqueRealFiles.size(); }

  /// Lookup, cache, and verify the specified directory (real or
  /// virtual).
  ///
  /// This returns a \c std::error_code if there was an error reading the
  /// directory. On success, returns the reference to the directory entry
  /// together with the exact path that was used to access a file by a
  /// particular call to getDirectoryRef.
  ///
  /// \param CacheFailure If true and the file does not exist, we'll cache
  /// the failure to find this file.
  llvm::Expected<DirectoryEntryRef> getDirectoryRef(StringRef DirName,
                                                    bool CacheFailure = true);

  /// Get a \c DirectoryEntryRef if it exists, without doing anything on error.
  llvm::Optional<DirectoryEntryRef>
  getOptionalDirectoryRef(StringRef DirName, bool CacheFailure = true) {
    return llvm::expectedToOptional(getDirectoryRef(DirName, CacheFailure));
  }

  /// Lookup, cache, and verify the specified directory (real or
  /// virtual).
  ///
  /// This function is deprecated and will be removed at some point in the
  /// future, new clients should use
  ///  \c getDirectoryRef.
  ///
  /// This returns a \c std::error_code if there was an error reading the
  /// directory. If there is no error, the DirectoryEntry is guaranteed to be
  /// non-NULL.
  ///
  /// \param CacheFailure If true and the file does not exist, we'll cache
  /// the failure to find this file.
  llvm::ErrorOr<const DirectoryEntry *>
  getDirectory(StringRef DirName, bool CacheFailure = true);

  /// Lookup, cache, and verify the specified file (real or
  /// virtual).
  ///
  /// This function is deprecated and will be removed at some point in the
  /// future, new clients should use
  ///  \c getFileRef.
  ///
  /// This returns a \c std::error_code if there was an error loading the file.
  /// If there is no error, the FileEntry is guaranteed to be non-NULL.
  ///
  /// \param OpenFile if true and the file exists, it will be opened.
  ///
  /// \param CacheFailure If true and the file does not exist, we'll cache
  /// the failure to find this file.
  llvm::ErrorOr<const FileEntry *>
  getFile(StringRef Filename, bool OpenFile = false, bool CacheFailure = true);

  /// Lookup, cache, and verify the specified file (real or virtual). Return the
  /// reference to the file entry together with the exact path that was used to
  /// access a file by a particular call to getFileRef. If the underlying VFS is
  /// a redirecting VFS that uses external file names, the returned FileEntryRef
  /// will use the external name instead of the filename that was passed to this
  /// method.
  ///
  /// This returns a \c std::error_code if there was an error loading the file,
  /// or a \c FileEntryRef otherwise.
  ///
  /// \param OpenFile if true and the file exists, it will be opened.
  ///
  /// \param CacheFailure If true and the file does not exist, we'll cache
  /// the failure to find this file.
  llvm::Expected<FileEntryRef> getFileRef(StringRef Filename,
                                          bool OpenFile = false,
                                          bool CacheFailure = true);

  /// Get a FileEntryRef if it exists, without doing anything on error.
  llvm::Optional<FileEntryRef> getOptionalFileRef(StringRef Filename,
                                                  bool OpenFile = false,
                                                  bool CacheFailure = true) {
    return llvm::expectedToOptional(
        getFileRef(Filename, OpenFile, CacheFailure));
  }

  /// Returns the current file system options
  FileSystemOptions &getFileSystemOpts() { return FileSystemOpts; }
  const FileSystemOptions &getFileSystemOpts() const { return FileSystemOpts; }

  llvm::vfs::FileSystem &getVirtualFileSystem() const { return *FS; }

  void setVirtualFileSystem(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
    this->FS = std::move(FS);
  }

  /// Retrieve a file entry for a "virtual" file that acts as
  /// if there were a file with the given name on disk.
  ///
  /// The file itself is not accessed.
  const FileEntry *getVirtualFile(StringRef Filename, off_t Size,
                                  time_t ModificationTime);

  /// Retrieve a FileEntry that bypasses VFE, which is expected to be a virtual
  /// file entry, to access the real file.  The returned FileEntry will have
  /// the same filename as FE but a different identity and its own stat.
  ///
  /// This should be used only for rare error recovery paths because it
  /// bypasses all mapping and uniquing, blindly creating a new FileEntry.
  /// There is no attempt to deduplicate these; if you bypass the same file
  /// twice, you get two new file entries.
  llvm::Optional<FileEntryRef> getBypassFile(FileEntryRef VFE);

  /// Open the specified file as a MemoryBuffer, returning a new
  /// MemoryBuffer if successful, otherwise returning null.
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
  getBufferForFile(const FileEntry *Entry, bool isVolatile = false);
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
  getBufferForFile(StringRef Filename, bool isVolatile = false) {
    return getBufferForFileImpl(Filename, /*FileSize=*/-1, isVolatile);
  }

private:
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
  getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile);

public:
  /// Get the 'stat' information for the given \p Path.
  ///
  /// If the path is relative, it will be resolved against the WorkingDir of the
  /// FileManager's FileSystemOptions.
  ///
  /// \returns a \c std::error_code describing an error, if there was one
  std::error_code getNoncachedStatValue(StringRef Path,
                                        llvm::vfs::Status &Result);

  /// If path is not absolute and FileSystemOptions set the working
  /// directory, the path is modified to be relative to the given
  /// working directory.
  /// \returns true if \c path changed.
  bool FixupRelativePath(SmallVectorImpl<char> &path) const;

  /// Makes \c Path absolute taking into account FileSystemOptions and the
  /// working directory option.
  /// \returns true if \c Path changed to absolute.
  bool makeAbsolutePath(SmallVectorImpl<char> &Path) const;

  /// Produce an array mapping from the unique IDs assigned to each
  /// file to the corresponding FileEntry pointer.
  void GetUniqueIDMapping(
                    SmallVectorImpl<const FileEntry *> &UIDToFiles) const;

  /// Retrieve the canonical name for a given directory.
  ///
  /// This is a very expensive operation, despite its results being cached,
  /// and should only be used when the physical layout of the file system is
  /// required, which is (almost) never.
  StringRef getCanonicalName(const DirectoryEntry *Dir);

  void PrintStats() const;
};

} // end namespace clang

#endif // LLVM_CLANG_BASIC_FILEMANAGER_H