[VFS] Let the user decide if they want path normalization.

This is a more principled version of what I did earlier. Path
normalization is generally a good thing, but may break users in strange
environments, e. g. using lots of symlinks. Let the user choose and
default it to on.

This also changes adding a duplicated file into returning an error if
the file contents are different instead of an assertion failure.

Differential Revision: http://reviews.llvm.org/D13658

llvm-svn: 250060
diff --git a/clang/lib/Basic/VirtualFileSystem.cpp b/clang/lib/Basic/VirtualFileSystem.cpp
index 1b75fbe..a20132b 100644
--- a/clang/lib/Basic/VirtualFileSystem.cpp
+++ b/clang/lib/Basic/VirtualFileSystem.cpp
@@ -10,6 +10,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Basic/FileManager.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
@@ -474,11 +475,12 @@
 };
 }
 
-InMemoryFileSystem::InMemoryFileSystem()
+InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
     : Root(new detail::InMemoryDirectory(
           Status("", getNextVirtualUniqueID(), llvm::sys::TimeValue::MinTime(),
                  0, 0, 0, llvm::sys::fs::file_type::directory_file,
-                 llvm::sys::fs::perms::all_all))) {}
+                 llvm::sys::fs::perms::all_all))),
+      UseNormalizedPaths(UseNormalizedPaths) {}
 
 InMemoryFileSystem::~InMemoryFileSystem() {}
 
@@ -486,7 +488,7 @@
   return Root->toString(/*Indent=*/0);
 }
 
-void InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
+bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
                                  std::unique_ptr<llvm::MemoryBuffer> Buffer) {
   SmallString<128> Path;
   P.toVector(Path);
@@ -496,14 +498,14 @@
   assert(!EC);
   (void)EC;
 
+  if (useNormalizedPaths())
+    FileManager::removeDotPaths(Path, /*RemoveDotDot=*/true);
+
+  if (Path.empty())
+    return false;
+
   detail::InMemoryDirectory *Dir = Root.get();
   auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
-  if (*I == ".")
-    ++I;
-
-  if (I == E)
-    return;
-
   while (true) {
     StringRef Name = *I;
     detail::InMemoryNode *Node = Dir->getChild(Name);
@@ -519,7 +521,7 @@
                     llvm::sys::fs::all_all);
         Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
                                 std::move(Stat), std::move(Buffer)));
-        return;
+        return true;
       }
 
       // Create a new directory. Use the path up to here.
@@ -534,12 +536,24 @@
       continue;
     }
 
-    if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node))
+    if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
       Dir = NewDir;
+    } else {
+      assert(isa<detail::InMemoryFile>(Node) &&
+             "Must be either file or directory!");
+
+      // Trying to insert a directory in place of a file.
+      if (I != E)
+        return false;
+
+      // Return false only if the new file is different from the existing one.
+      return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
+             Buffer->getBuffer();
+    }
   }
 }
 
-void InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
+bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
                                       llvm::MemoryBuffer *Buffer) {
   return addFile(P, ModificationTime,
                  llvm::MemoryBuffer::getMemBuffer(
@@ -557,13 +571,13 @@
   assert(!EC);
   (void)EC;
 
-  auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
-  if (*I == ".")
-    ++I;
+  if (FS.useNormalizedPaths())
+    FileManager::removeDotPaths(Path, /*RemoveDotDot=*/true);
 
-  if (I == E)
+  if (Path.empty())
     return Dir;
 
+  auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
   while (true) {
     detail::InMemoryNode *Node = Dir->getChild(*I);
     ++I;