Coverage Report

Created: 2018-10-10 11:36

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/include/clang/Basic/VirtualFileSystem.h
Line
Count
Source (jump to first uncovered line)
1
//===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===//
2
//
3
//                     The LLVM Compiler Infrastructure
4
//
5
// This file is distributed under the University of Illinois Open Source
6
// License. See LICENSE.TXT for details.
7
//
8
//===----------------------------------------------------------------------===//
9
//
10
/// \file
11
/// Defines the virtual file system interface vfs::FileSystem.
12
//
13
//===----------------------------------------------------------------------===//
14
15
#ifndef LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
16
#define LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
17
18
#include "clang/Basic/LLVM.h"
19
#include "llvm/ADT/IntrusiveRefCntPtr.h"
20
#include "llvm/ADT/None.h"
21
#include "llvm/ADT/Optional.h"
22
#include "llvm/ADT/SmallVector.h"
23
#include "llvm/ADT/StringRef.h"
24
#include "llvm/ADT/Twine.h"
25
#include "llvm/Support/Chrono.h"
26
#include "llvm/Support/ErrorOr.h"
27
#include "llvm/Support/FileSystem.h"
28
#include "llvm/Support/SourceMgr.h"
29
#include <cassert>
30
#include <cstdint>
31
#include <ctime>
32
#include <memory>
33
#include <stack>
34
#include <string>
35
#include <system_error>
36
#include <utility>
37
#include <vector>
38
39
namespace llvm {
40
41
class MemoryBuffer;
42
43
} // namespace llvm
44
45
namespace clang {
46
namespace vfs {
47
48
/// The result of a \p status operation.
49
class Status {
50
  std::string Name;
51
  llvm::sys::fs::UniqueID UID;
52
  llvm::sys::TimePoint<> MTime;
53
  uint32_t User;
54
  uint32_t Group;
55
  uint64_t Size;
56
  llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::status_error;
57
  llvm::sys::fs::perms Perms;
58
59
public:
60
   // FIXME: remove when files support multiple names
61
  bool IsVFSMapped = false;
62
63
2.69k
  Status() = default;
64
  Status(const llvm::sys::fs::file_status &Status);
65
  Status(StringRef Name, llvm::sys::fs::UniqueID UID,
66
         llvm::sys::TimePoint<> MTime, uint32_t User, uint32_t Group,
67
         uint64_t Size, llvm::sys::fs::file_type Type,
68
         llvm::sys::fs::perms Perms);
69
70
  /// Get a copy of a Status with a different name.
71
  static Status copyWithNewName(const Status &In, StringRef NewName);
72
  static Status copyWithNewName(const llvm::sys::fs::file_status &In,
73
                                StringRef NewName);
74
75
  /// Returns the name that should be used for this file or directory.
76
1.50M
  StringRef getName() const { return Name; }
77
78
  /// @name Status interface from llvm::sys::fs
79
  /// @{
80
948k
  llvm::sys::fs::file_type getType() const { return Type; }
81
97.7k
  llvm::sys::fs::perms getPermissions() const { return Perms; }
82
948k
  llvm::sys::TimePoint<> getLastModificationTime() const { return MTime; }
83
949k
  llvm::sys::fs::UniqueID getUniqueID() const { return UID; }
84
97.7k
  uint32_t getUser() const { return User; }
85
97.7k
  uint32_t getGroup() const { return Group; }
86
948k
  uint64_t getSize() const { return Size; }
87
  /// @}
88
  /// @name Status queries
89
  /// These are static queries in llvm::sys::fs.
90
  /// @{
91
  bool equivalent(const Status &Other) const;
92
  bool isDirectory() const;
93
  bool isRegularFile() const;
94
  bool isOther() const;
95
  bool isSymlink() const;
96
  bool isStatusKnown() const;
97
  bool exists() const;
98
  /// @}
99
};
100
101
/// Represents an open file.
102
class File {
103
public:
104
  /// Destroy the file after closing it (if open).
105
  /// Sub-classes should generally call close() inside their destructors.  We
106
  /// cannot do that from the base class, since close is virtual.
107
  virtual ~File();
108
109
  /// Get the status of the file.
110
  virtual llvm::ErrorOr<Status> status() = 0;
111
112
  /// Get the name of the file
113
5.04k
  virtual llvm::ErrorOr<std::string> getName() {
114
5.04k
    if (auto Status = status())
115
5.04k
      return Status->getName().str();
116
0
    else
117
0
      return Status.getError();
118
5.04k
  }
119
120
  /// Get the contents of the file as a \p MemoryBuffer.
121
  virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
122
  getBuffer(const Twine &Name, int64_t FileSize = -1,
123
            bool RequiresNullTerminator = true, bool IsVolatile = false) = 0;
124
125
  /// Closes the file.
126
  virtual std::error_code close() = 0;
127
};
128
129
/// A member of a directory, yielded by a directory_iterator.
130
/// Only information available on most platforms is included.
131
class directory_entry {
132
  std::string Path;
133
  llvm::sys::fs::file_type Type;
134
135
public:
136
283k
  directory_entry() = default;
137
  directory_entry(std::string Path, llvm::sys::fs::file_type Type)
138
8.83k
      : Path(std::move(Path)), Type(Type) {}
139
140
307k
  llvm::StringRef path() const { return Path; }
141
3.25k
  llvm::sys::fs::file_type type() const { return Type; }
142
};
143
144
namespace detail {
145
146
/// An interface for virtual file systems to provide an iterator over the
147
/// (non-recursive) contents of a directory.
148
struct DirIterImpl {
149
  virtual ~DirIterImpl();
150
151
  /// Sets \c CurrentEntry to the next entry in the directory on success,
152
  /// to directory_entry() at end,  or returns a system-defined \c error_code.
153
  virtual std::error_code increment() = 0;
154
155
  directory_entry CurrentEntry;
156
};
157
158
} // namespace detail
159
160
/// An input iterator over the entries in a virtual path, similar to
161
/// llvm::sys::fs::directory_iterator.
162
class directory_iterator {
163
  std::shared_ptr<detail::DirIterImpl> Impl; // Input iterator semantics on copy
164
165
public:
166
  directory_iterator(std::shared_ptr<detail::DirIterImpl> I)
167
281k
      : Impl(std::move(I)) {
168
281k
    assert(Impl.get() != nullptr && "requires non-null implementation");
169
281k
    if (Impl->CurrentEntry.path().empty())
170
280k
      Impl.reset(); // Normalize the end iterator to Impl == nullptr.
171
281k
  }
172
173
  /// Construct an 'end' iterator.
174
285k
  directory_iterator() = default;
175
176
  /// Equivalent to operator++, with an error code.
177
8.90k
  directory_iterator &increment(std::error_code &EC) {
178
8.90k
    assert(Impl && "attempting to increment past end");
179
8.90k
    EC = Impl->increment();
180
8.90k
    if (Impl->CurrentEntry.path().empty())
181
1.74k
      Impl.reset(); // Normalize the end iterator to Impl == nullptr.
182
8.90k
    return *this;
183
8.90k
  }
184
185
2.29k
  const directory_entry &operator*() const { return Impl->CurrentEntry; }
186
17.2k
  const directory_entry *operator->() const { return &Impl->CurrentEntry; }
187
188
11.0k
  bool operator==(const directory_iterator &RHS) const {
189
11.0k
    if (Impl && 
RHS.Impl9.00k
)
190
0
      return Impl->CurrentEntry.path() == RHS.Impl->CurrentEntry.path();
191
11.0k
    return !Impl && 
!RHS.Impl2.07k
;
192
11.0k
  }
193
9.64k
  bool operator!=(const directory_iterator &RHS) const {
194
9.64k
    return !(*this == RHS);
195
9.64k
  }
196
};
197
198
class FileSystem;
199
200
/// An input iterator over the recursive contents of a virtual path,
201
/// similar to llvm::sys::fs::recursive_directory_iterator.
202
class recursive_directory_iterator {
203
  using IterState =
204
      std::stack<directory_iterator, std::vector<directory_iterator>>;
205
206
  FileSystem *FS;
207
  std::shared_ptr<IterState> State; // Input iterator semantics on copy.
208
209
public:
210
  recursive_directory_iterator(FileSystem &FS, const Twine &Path,
211
                               std::error_code &EC);
212
213
  /// Construct an 'end' iterator.
214
286
  recursive_directory_iterator() = default;
215
216
  /// Equivalent to operator++, with an error code.
217
  recursive_directory_iterator &increment(std::error_code &EC);
218
219
0
  const directory_entry &operator*() const { return *State->top(); }
220
2.19k
  const directory_entry *operator->() const { return &*State->top(); }
221
222
1.42k
  bool operator==(const recursive_directory_iterator &Other) const {
223
1.42k
    return State == Other.State; // identity
224
1.42k
  }
225
1.42k
  bool operator!=(const recursive_directory_iterator &RHS) const {
226
1.42k
    return !(*this == RHS);
227
1.42k
  }
228
229
  /// Gets the current level. Starting path is at level 0.
230
89
  int level() const {
231
89
    assert(!State->empty() && "Cannot get level without any iteration state");
232
89
    return State->size()-1;
233
89
  }
234
};
235
236
/// The virtual file system interface.
237
class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> {
238
public:
239
  virtual ~FileSystem();
240
241
  /// Get the status of the entry at \p Path, if one exists.
242
  virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0;
243
244
  /// Get a \p File object for the file at \p Path, if one exists.
245
  virtual llvm::ErrorOr<std::unique_ptr<File>>
246
  openFileForRead(const Twine &Path) = 0;
247
248
  /// This is a convenience method that opens a file, gets its content and then
249
  /// closes the file.
250
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
251
  getBufferForFile(const Twine &Name, int64_t FileSize = -1,
252
                   bool RequiresNullTerminator = true, bool IsVolatile = false);
253
254
  /// Get a directory_iterator for \p Dir.
255
  /// \note The 'end' iterator is directory_iterator().
256
  virtual directory_iterator dir_begin(const Twine &Dir,
257
                                       std::error_code &EC) = 0;
258
259
  /// Set the working directory. This will affect all following operations on
260
  /// this file system and may propagate down for nested file systems.
261
  virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0;
262
263
  /// Get the working directory of this file system.
264
  virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0;
265
266
  /// Gets real path of \p Path e.g. collapse all . and .. patterns, resolve
267
  /// symlinks. For real file system, this uses `llvm::sys::fs::real_path`.
268
  /// This returns errc::operation_not_permitted if not implemented by subclass.
269
  virtual std::error_code getRealPath(const Twine &Path,
270
                                      SmallVectorImpl<char> &Output) const;
271
272
  /// Check whether a file exists. Provided for convenience.
273
  bool exists(const Twine &Path);
274
275
  /// Make \a Path an absolute path.
276
  ///
277
  /// Makes \a Path absolute using the current directory if it is not already.
278
  /// An empty \a Path will result in the current directory.
279
  ///
280
  /// /absolute/path   => /absolute/path
281
  /// relative/../path => <current-directory>/relative/../path
282
  ///
283
  /// \param Path A path that is modified to be an absolute path.
284
  /// \returns success if \a path has been made absolute, otherwise a
285
  ///          platform-specific error_code.
286
  std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const;
287
};
288
289
/// Gets an \p vfs::FileSystem for the 'real' file system, as seen by
290
/// the operating system.
291
IntrusiveRefCntPtr<FileSystem> getRealFileSystem();
292
293
/// A file system that allows overlaying one \p AbstractFileSystem on top
294
/// of another.
295
///
296
/// Consists of a stack of >=1 \p FileSystem objects, which are treated as being
297
/// one merged file system. When there is a directory that exists in more than
298
/// one file system, the \p OverlayFileSystem contains a directory containing
299
/// the union of their contents.  The attributes (permissions, etc.) of the
300
/// top-most (most recently added) directory are used.  When there is a file
301
/// that exists in more than one file system, the file in the top-most file
302
/// system overrides the other(s).
303
class OverlayFileSystem : public FileSystem {
304
  using FileSystemList = SmallVector<IntrusiveRefCntPtr<FileSystem>, 1>;
305
306
  /// The stack of file systems, implemented as a list in order of
307
  /// their addition.
308
  FileSystemList FSList;
309
310
public:
311
  OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base);
312
313
  /// Pushes a file system on top of the stack.
314
  void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS);
315
316
  llvm::ErrorOr<Status> status(const Twine &Path) override;
317
  llvm::ErrorOr<std::unique_ptr<File>>
318
  openFileForRead(const Twine &Path) override;
319
  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
320
  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
321
  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
322
  std::error_code getRealPath(const Twine &Path,
323
                              SmallVectorImpl<char> &Output) const override;
324
325
  using iterator = FileSystemList::reverse_iterator;
326
  using const_iterator = FileSystemList::const_reverse_iterator;
327
328
  /// Get an iterator pointing to the most recently added file system.
329
118k
  iterator overlays_begin() { return FSList.rbegin(); }
330
0
  const_iterator overlays_begin() const { return FSList.rbegin(); }
331
332
  /// Get an iterator pointing one-past the least recently added file
333
  /// system.
334
117k
  iterator overlays_end() { return FSList.rend(); }
335
0
  const_iterator overlays_end() const { return FSList.rend(); }
336
};
337
338
/// By default, this delegates all calls to the underlying file system. This
339
/// is useful when derived file systems want to override some calls and still
340
/// proxy other calls.
341
class ProxyFileSystem : public FileSystem {
342
public:
343
  explicit ProxyFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
344
      : FS(std::move(FS)) {}
345
346
  llvm::ErrorOr<Status> status(const Twine &Path) override {
347
    return FS->status(Path);
348
  }
349
  llvm::ErrorOr<std::unique_ptr<File>>
350
  openFileForRead(const Twine &Path) override {
351
    return FS->openFileForRead(Path);
352
  }
353
  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override {
354
    return FS->dir_begin(Dir, EC);
355
  }
356
  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
357
    return FS->getCurrentWorkingDirectory();
358
  }
359
  std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
360
    return FS->setCurrentWorkingDirectory(Path);
361
  }
362
  std::error_code getRealPath(const Twine &Path,
363
                              SmallVectorImpl<char> &Output) const override {
364
    return FS->getRealPath(Path, Output);
365
  }
366
367
protected:
368
  FileSystem &getUnderlyingFS() { return *FS; }
369
370
private:
371
  IntrusiveRefCntPtr<FileSystem> FS;
372
};
373
374
namespace detail {
375
376
class InMemoryDirectory;
377
class InMemoryFile;
378
379
} // namespace detail
380
381
/// An in-memory file system.
382
class InMemoryFileSystem : public FileSystem {
383
  std::unique_ptr<detail::InMemoryDirectory> Root;
384
  std::string WorkingDirectory;
385
  bool UseNormalizedPaths = true;
386
387
  /// If HardLinkTarget is non-null, a hardlink is created to the To path which
388
  /// must be a file. If it is null then it adds the file as the public addFile.
389
  bool addFile(const Twine &Path, time_t ModificationTime,
390
               std::unique_ptr<llvm::MemoryBuffer> Buffer,
391
               Optional<uint32_t> User, Optional<uint32_t> Group,
392
               Optional<llvm::sys::fs::file_type> Type,
393
               Optional<llvm::sys::fs::perms> Perms,
394
               const detail::InMemoryFile *HardLinkTarget);
395
396
public:
397
  explicit InMemoryFileSystem(bool UseNormalizedPaths = true);
398
  ~InMemoryFileSystem() override;
399
400
  /// Add a file containing a buffer or a directory to the VFS with a
401
  /// path. The VFS owns the buffer.  If present, User, Group, Type
402
  /// and Perms apply to the newly-created file or directory.
403
  /// \return true if the file or directory was successfully added,
404
  /// false if the file or directory already exists in the file system with
405
  /// different contents.
406
  bool addFile(const Twine &Path, time_t ModificationTime,
407
               std::unique_ptr<llvm::MemoryBuffer> Buffer,
408
               Optional<uint32_t> User = None, Optional<uint32_t> Group = None,
409
               Optional<llvm::sys::fs::file_type> Type = None,
410
               Optional<llvm::sys::fs::perms> Perms = None);
411
412
  /// Add a hard link to a file.
413
  /// Here hard links are not intended to be fully equivalent to the classical
414
  /// filesystem. Both the hard link and the file share the same buffer and
415
  /// status (and thus have the same UniqueID). Because of this there is no way
416
  /// to distinguish between the link and the file after the link has been
417
  /// added.
418
  ///
419
  /// The To path must be an existing file or a hardlink. The From file must not
420
  /// have been added before. The To Path must not be a directory. The From Node
421
  /// is added as a hard link which points to the resolved file of To Node.
422
  /// \return true if the above condition is satisfied and hardlink was
423
  /// successfully created, false otherwise.
424
  bool addHardLink(const Twine &From, const Twine &To);
425
426
  /// Add a buffer to the VFS with a path. The VFS does not own the buffer.
427
  /// If present, User, Group, Type and Perms apply to the newly-created file
428
  /// or directory.
429
  /// \return true if the file or directory was successfully added,
430
  /// false if the file or directory already exists in the file system with
431
  /// different contents.
432
  bool addFileNoOwn(const Twine &Path, time_t ModificationTime,
433
                    llvm::MemoryBuffer *Buffer,
434
                    Optional<uint32_t> User = None,
435
                    Optional<uint32_t> Group = None,
436
                    Optional<llvm::sys::fs::file_type> Type = None,
437
                    Optional<llvm::sys::fs::perms> Perms = None);
438
439
  std::string toString() const;
440
441
  /// Return true if this file system normalizes . and .. in paths.
442
299k
  bool useNormalizedPaths() const { return UseNormalizedPaths; }
443
444
  llvm::ErrorOr<Status> status(const Twine &Path) override;
445
  llvm::ErrorOr<std::unique_ptr<File>>
446
  openFileForRead(const Twine &Path) override;
447
  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
448
449
215k
  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
450
215k
    return WorkingDirectory;
451
215k
  }
452
  /// Canonicalizes \p Path by combining with the current working
453
  /// directory and normalizing the path (e.g. remove dots). If the current
454
  /// working directory is not set, this returns errc::operation_not_permitted.
455
  ///
456
  /// This doesn't resolve symlinks as they are not supported in in-memory file
457
  /// system.
458
  std::error_code getRealPath(const Twine &Path,
459
                              SmallVectorImpl<char> &Output) const override;
460
461
  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
462
};
463
464
/// Get a globally unique ID for a virtual file or directory.
465
llvm::sys::fs::UniqueID getNextVirtualUniqueID();
466
467
/// Gets a \p FileSystem for a virtual file system described in YAML
468
/// format.
469
IntrusiveRefCntPtr<FileSystem>
470
getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer,
471
               llvm::SourceMgr::DiagHandlerTy DiagHandler,
472
               StringRef YAMLFilePath,
473
               void *DiagContext = nullptr,
474
               IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
475
476
struct YAMLVFSEntry {
477
  template <typename T1, typename T2> YAMLVFSEntry(T1 &&VPath, T2 &&RPath)
478
539
      : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)) {}
clang::vfs::YAMLVFSEntry::YAMLVFSEntry<char const*, llvm::StringRef>(char const*&&, llvm::StringRef&&)
Line
Count
Source
478
2
      : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)) {}
clang::vfs::YAMLVFSEntry::YAMLVFSEntry<llvm::StringRef&, llvm::StringRef&>(llvm::StringRef&&&, llvm::StringRef&&&)
Line
Count
Source
478
537
      : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)) {}
479
  std::string VPath;
480
  std::string RPath;
481
};
482
483
/// Collect all pairs of <virtual path, real path> entries from the
484
/// \p YAMLFilePath. This is used by the module dependency collector to forward
485
/// the entries into the reproducer output VFS YAML file.
486
void collectVFSFromYAML(
487
    std::unique_ptr<llvm::MemoryBuffer> Buffer,
488
    llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
489
    SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
490
    void *DiagContext = nullptr,
491
    IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
492
493
class YAMLVFSWriter {
494
  std::vector<YAMLVFSEntry> Mappings;
495
  Optional<bool> IsCaseSensitive;
496
  Optional<bool> IsOverlayRelative;
497
  Optional<bool> UseExternalNames;
498
  Optional<bool> IgnoreNonExistentContents;
499
  std::string OverlayDir;
500
501
public:
502
25
  YAMLVFSWriter() = default;
503
504
  void addFileMapping(StringRef VirtualPath, StringRef RealPath);
505
506
17
  void setCaseSensitivity(bool CaseSensitive) {
507
17
    IsCaseSensitive = CaseSensitive;
508
17
  }
509
510
16
  void setUseExternalNames(bool UseExtNames) {
511
16
    UseExternalNames = UseExtNames;
512
16
  }
513
514
16
  void setIgnoreNonExistentContents(bool IgnoreContents) {
515
16
    IgnoreNonExistentContents = IgnoreContents;
516
16
  }
517
518
16
  void setOverlayDir(StringRef OverlayDirectory) {
519
16
    IsOverlayRelative = true;
520
16
    OverlayDir.assign(OverlayDirectory.str());
521
16
  }
522
523
  void write(llvm::raw_ostream &OS);
524
};
525
526
} // namespace vfs
527
} // namespace clang
528
529
#endif // LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H