[CrashReproducer] Add support for merging -ivfsoverlay

Merge all VFS mapped files inside -ivfsoverlay inputs into the vfs
overlay provided by the crash reproducer. This is the last missing piece
to allow crash reproducers to fully work with user frameworks; when
combined with headermaps, it allows clang to find additional frameworks.

rdar://problem/27913709

llvm-svn: 290326
diff --git a/clang/lib/Basic/VirtualFileSystem.cpp b/clang/lib/Basic/VirtualFileSystem.cpp
index 88d08b4..50fcb22 100644
--- a/clang/lib/Basic/VirtualFileSystem.cpp
+++ b/clang/lib/Basic/VirtualFileSystem.cpp
@@ -887,9 +887,6 @@
   RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
       : ExternalFS(std::move(ExternalFS)) {}
 
-  /// \brief Looks up \p Path in \c Roots.
-  ErrorOr<Entry *> lookupPath(const Twine &Path);
-
   /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
   /// recursing into the contents of \p From if it is a directory.
   ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
@@ -899,6 +896,9 @@
   ErrorOr<Status> status(const Twine &Path, Entry *E);
 
 public:
+  /// \brief Looks up \p Path in \c Roots.
+  ErrorOr<Entry *> lookupPath(const Twine &Path);
+
   /// \brief Parses \p Buffer, which is expected to be in YAML format and
   /// returns a virtual file system representing its contents.
   static RedirectingFileSystem *
@@ -1606,6 +1606,47 @@
                                        std::move(ExternalFS));
 }
 
+static void getVFSEntries(Entry *SrcE, SmallVectorImpl<StringRef> &Path,
+                          SmallVectorImpl<YAMLVFSEntry> &Entries) {
+  auto Kind = SrcE->getKind();
+  if (Kind == EK_Directory) {
+    auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE);
+    assert(DE && "Must be a directory");
+    for (std::unique_ptr<Entry> &SubEntry :
+         llvm::make_range(DE->contents_begin(), DE->contents_end())) {
+      Path.push_back(SubEntry->getName());
+      getVFSEntries(SubEntry.get(), Path, Entries);
+      Path.pop_back();
+    }
+    return;
+  }
+
+  assert(Kind == EK_File && "Must be a EK_File");
+  auto *FE = dyn_cast<RedirectingFileEntry>(SrcE);
+  assert(FE && "Must be a file");
+  SmallString<128> VPath;
+  for (auto &Comp : Path)
+    llvm::sys::path::append(VPath, Comp);
+  Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
+}
+
+void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
+                             SourceMgr::DiagHandlerTy DiagHandler,
+                             StringRef YAMLFilePath,
+                             SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
+                             void *DiagContext,
+                             IntrusiveRefCntPtr<FileSystem> ExternalFS) {
+  RedirectingFileSystem *VFS = RedirectingFileSystem::create(
+      std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
+      std::move(ExternalFS));
+  ErrorOr<Entry *> RootE = VFS->lookupPath("/");
+  if (!RootE)
+    return;
+  SmallVector<StringRef, 8> Components;
+  Components.push_back("/");
+  getVFSEntries(*RootE, Components, CollectedEntries);
+}
+
 UniqueID vfs::getNextVirtualUniqueID() {
   static std::atomic<unsigned> UID;
   unsigned ID = ++UID;
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 5c78f81..ccddd14 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -181,6 +181,26 @@
   }
 }
 
+static void collectVFSEntries(CompilerInstance &CI,
+                              std::shared_ptr<ModuleDependencyCollector> MDC) {
+  if (CI.getHeaderSearchOpts().VFSOverlayFiles.empty())
+    return;
+
+  // Collect all VFS found.
+  SmallVector<vfs::YAMLVFSEntry, 16> VFSEntries;
+  for (const std::string &VFSFile : CI.getHeaderSearchOpts().VFSOverlayFiles) {
+    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
+        llvm::MemoryBuffer::getFile(VFSFile);
+    if (!Buffer)
+      return;
+    vfs::collectVFSFromYAML(std::move(Buffer.get()), /*DiagHandler*/ nullptr,
+                            VFSFile, VFSEntries);
+  }
+
+  for (auto &E : VFSEntries)
+    MDC->addFile(E.VPath, E.RPath);
+}
+
 // Diagnostics
 static void SetUpDiagnosticLog(DiagnosticOptions *DiagOpts,
                                const CodeGenOptions *CodeGenOpts,
@@ -412,6 +432,7 @@
     addDependencyCollector(ModuleDepCollector);
     collectHeaderMaps(PP->getHeaderSearchInfo(), ModuleDepCollector);
     collectIncludePCH(*this, ModuleDepCollector);
+    collectVFSEntries(*this, ModuleDepCollector);
   }
 
   for (auto &Listener : DependencyCollectors)
diff --git a/clang/lib/Frontend/ModuleDependencyCollector.cpp b/clang/lib/Frontend/ModuleDependencyCollector.cpp
index 578910a..9b34d42 100644
--- a/clang/lib/Frontend/ModuleDependencyCollector.cpp
+++ b/clang/lib/Frontend/ModuleDependencyCollector.cpp
@@ -201,7 +201,8 @@
   return true;
 }
 
-std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src) {
+std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src,
+                                                      StringRef Dst) {
   using namespace llvm::sys;
 
   // We need an absolute src path to append to the root.
@@ -213,23 +214,35 @@
   AbsoluteSrc = path::remove_leading_dotslash(AbsoluteSrc);
 
   // Canonicalize the source path by removing "..", "." components.
-  SmallString<256> CanonicalPath = AbsoluteSrc;
-  path::remove_dots(CanonicalPath, /*remove_dot_dot=*/true);
+  SmallString<256> VirtualPath = AbsoluteSrc;
+  path::remove_dots(VirtualPath, /*remove_dot_dot=*/true);
 
   // If a ".." component is present after a symlink component, remove_dots may
   // lead to the wrong real destination path. Let the source be canonicalized
   // like that but make sure we always use the real path for the destination.
-  SmallString<256> RealPath;
-  if (!getRealPath(AbsoluteSrc, RealPath))
-    RealPath = CanonicalPath;
-  SmallString<256> Dest = getDest();
-  path::append(Dest, path::relative_path(RealPath));
+  SmallString<256> CopyFrom;
+  if (!getRealPath(AbsoluteSrc, CopyFrom))
+    CopyFrom = VirtualPath;
+  SmallString<256> CacheDst = getDest();
+
+  if (Dst.empty()) {
+    // The common case is to map the virtual path to the same path inside the
+    // cache.
+    path::append(CacheDst, path::relative_path(CopyFrom));
+  } else {
+    // When collecting entries from input vfsoverlays, copy the external
+    // contents into the cache but still map from the source.
+    if (!fs::exists(Dst))
+      return std::error_code();
+    path::append(CacheDst, Dst);
+    CopyFrom = Dst;
+  }
 
   // Copy the file into place.
-  if (std::error_code EC = fs::create_directories(path::parent_path(Dest),
-                                                   /*IgnoreExisting=*/true))
+  if (std::error_code EC = fs::create_directories(path::parent_path(CacheDst),
+                                                  /*IgnoreExisting=*/true))
     return EC;
-  if (std::error_code EC = fs::copy_file(RealPath, Dest))
+  if (std::error_code EC = fs::copy_file(CopyFrom, CacheDst))
     return EC;
 
   // Always map a canonical src path to its real path into the YAML, by doing
@@ -237,12 +250,12 @@
   // overlay, which is a way to emulate symlink inside the VFS; this is also
   // needed for correctness, not doing that can lead to module redifinition
   // errors.
-  addFileMapping(CanonicalPath, Dest);
+  addFileMapping(VirtualPath, CacheDst);
   return std::error_code();
 }
 
-void ModuleDependencyCollector::addFile(StringRef Filename) {
+void ModuleDependencyCollector::addFile(StringRef Filename, StringRef FileDst) {
   if (insertSeen(Filename))
-    if (copyToRoot(Filename))
+    if (copyToRoot(Filename, FileDst))
       HasErrors = true;
 }