- clang-replace: Separate out replacement file finding
    - clang-replace: Update LIT test for windows

Hi klimek, djasper, silvas, tareqsiraj, arielbernal, Sarcasm,

http://llvm-reviews.chandlerc.com/D1424

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D1424?vs=3526&id=3577#toc

Files:
  CMakeLists.txt
  Makefile
  clang-replace/ApplyReplacements.cpp
  clang-replace/ApplyReplacements.h
  clang-replace/CMakeLists.txt
  clang-replace/Makefile
  clang-replace/tool/CMakeLists.txt
  clang-replace/tool/ClangReplaceMain.cpp
  clang-replace/tool/Makefile
  cpp11-migrate/Core/FileOverrides.cpp
  cpp11-migrate/Core/FileOverrides.h
  cpp11-migrate/Core/Reformatting.cpp
  cpp11-migrate/Core/ReplacementsYaml.h
  cpp11-migrate/tool/Cpp11Migrate.cpp
  test/CMakeLists.txt
  test/clang-replace/conflict.cpp
  test/clang-replace/conflict/common.h
  test/clang-replace/conflict/expected.txt
  test/clang-replace/conflict/file1.yaml
  test/clang-replace/conflict/file2.yaml
  test/clang-replace/conflict/file3.yaml
  test/cpp11-migrate/HeaderReplacements/common.h.yaml
  unittests/cpp11-migrate/CMakeLists.txt
  unittests/cpp11-migrate/ReplacementsYamlTest.cpp
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -1,6 +1,7 @@
 add_subdirectory(remove-cstr-calls)
 add_subdirectory(tool-template)
 add_subdirectory(cpp11-migrate)
+add_subdirectory(clang-replace)
 add_subdirectory(modularize)
 add_subdirectory(clang-tidy)
 
Index: Makefile
===================================================================
--- Makefile
+++ Makefile
@@ -12,7 +12,7 @@
 include $(CLANG_LEVEL)/../../Makefile.config
 
 PARALLEL_DIRS := remove-cstr-calls tool-template modularize
-DIRS := cpp11-migrate clang-tidy unittests
+DIRS := cpp11-migrate clang-tidy clang-replace unittests
 
 include $(CLANG_LEVEL)/Makefile
 
Index: clang-replace/ApplyReplacements.cpp
===================================================================
--- /dev/null
+++ clang-replace/ApplyReplacements.cpp
@@ -0,0 +1,195 @@
+//===-- Core/ApplyChangeDescriptions.cpp ----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the implementation for finding and applying change
+/// description files.
+///
+//===----------------------------------------------------------------------===//
+#include "ApplyReplacements.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace clang;
+
+typedef std::set<StringRef> UniqueFiles;
+typedef StringMap<std::vector<tooling::Replacement> > FileToReplacementsMap;
+
+static void eatDiagnostics(const SMDiagnostic &, void *) {}
+
+error_code collectReplacementsDocs(const StringRef Directory,
+                                   ReplacementsDocs &Docs,
+                                   DiagnosticsEngine &Diagnostics) {
+  using namespace llvm::sys::fs;
+  using namespace llvm::sys::path;
+
+  error_code ErrorCode;
+
+  for (recursive_directory_iterator I(Directory, ErrorCode), E;
+       I != E && !ErrorCode; I.increment(ErrorCode)) {
+    if (filename(I->path())[0] == '.') {
+      // Indicate not to descend into directories beginning with '.'
+      I.no_push();
+      continue;
+    }
+
+    if (extension(I->path()) != ".yaml")
+      continue;
+
+    OwningPtr<MemoryBuffer> Out;
+    error_code BufferError = MemoryBuffer::getFile(I->path(), Out);
+    if (BufferError) {
+      errs() << "Error reading " << I->path() << ": " << BufferError.message()
+             << "\n";
+      continue;
+    }
+
+    yaml::Input YIn(Out->getBuffer());
+    YIn.setDiagHandler(&eatDiagnostics);
+    tooling::ReplacementsDocument Doc;
+    YIn >> Doc;
+    if (YIn.error()) {
+      // File doesn't appear to be a header change description. Ignore it.
+      continue;
+    }
+
+    // Only keep files that properly parse.
+    Docs.push_back(Doc);
+  }
+
+  return ErrorCode;
+}
+
+/// \brief Dumps information for a sequence of conflicting Replacements.
+///
+/// \param[in] File FileEntry for the file the conflicting Replacements are
+/// for.
+/// \param[in] Replacements Full list of Replacements.
+/// \param[in] firstConflictIdx Offset into \p Replacements of the first
+/// conflicting Replacement.
+/// \param[in] numConflicting Number of consecutive Replacements in
+/// \p Replacements that conflict with one another.
+/// \param[in] SM SourceManager used for reporting.
+/// \param[in] Diagnostics DiagnosticsEngine used for reporting.
+static void
+reportConflict(const FileEntry *File,
+               const FileToReplacementsMap::mapped_type &Replacements,
+               unsigned firstConflictIdx, unsigned numConflicting,
+               SourceManager &SM, DiagnosticsEngine &Diagnostics) {
+  FileID FID = SM.translateFile(File);
+  if (FID.isInvalid())
+    FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
+
+  // FIXME: Output something a little more user-friendly (e.g. unified diff?)
+  errs() << "The following changes conflict:\n";
+  for (unsigned i = firstConflictIdx, e = firstConflictIdx + numConflicting;
+       i < e; ++i) {
+    const tooling::Replacement &R = Replacements[i];
+    if (R.getLength() == 0) {
+      errs() << "  Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
+             << SM.getColumnNumber(FID, R.getOffset()) << " "
+             << R.getReplacementText() << "\n";
+    } else {
+      if (R.getReplacementText().empty())
+        errs() << "  Remove ";
+      else
+        errs() << "  Replace ";
+
+      errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
+             << SM.getColumnNumber(FID, R.getOffset()) << "-"
+             << SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
+             << SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
+
+      if (R.getReplacementText().empty())
+        errs() << "\n";
+      else
+        errs() << " with \"" << R.getReplacementText() << "\"\n";
+    }
+  }
+}
+
+/// \brief Deduplicates and tests for conflicts among the replacements for each
+/// file in \c Replacements. Any conflicts found are reported.
+///
+/// \param[in,out] Replacements Container of all replacements grouped by file
+/// to be deduplicated and checked for conflicts.
+/// \param[in] SM SourceManager required for conflict reporting
+/// \param[in] Diagnostics DiagnosticsEngine used for output.
+///
+/// \returns \li true if conflicts were detected
+///          \li false if no conflicts were detected
+static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
+                                          SourceManager &SM,
+                                          DiagnosticsEngine &Diagnostics) {
+  bool conflictsFound = false;
+
+  for (FileToReplacementsMap::iterator I = Replacements.begin(),
+                                       E = Replacements.end();
+       I != E; ++I) {
+
+    const FileEntry *Entry = SM.getFileManager().getFile(I->getKey());
+    if (!Entry) {
+      errs() << "Described file '" << I->getKey()
+             << "' doesn't exist. Ignoring...\n";
+      continue;
+    }
+
+    std::vector<tooling::Range> Conflicts;
+    tooling::deduplicate(I->getValue(), Conflicts);
+
+    if (Conflicts.empty())
+      continue;
+
+    conflictsFound = true;
+
+    errs() << "There are conflicting changes to " << I->getKey() << ":\n";
+
+    for (std::vector<tooling::Range>::const_iterator
+             ConflictI = Conflicts.begin(),
+             ConflictE = Conflicts.end();
+         ConflictI != ConflictE; ++ConflictI)
+      reportConflict(Entry, I->getValue(), ConflictI->getOffset(),
+                     ConflictI->getLength(), SM, Diagnostics);
+  }
+
+  return conflictsFound;
+}
+
+bool applyReplacements(const ReplacementsDocs &Docs,
+                       DiagnosticsEngine &Diagnostics) {
+
+  // FIXME: Use Diagnostics for output
+
+  FileToReplacementsMap GroupedReplacements;
+
+  // Group all replacements by target file.
+  for (ReplacementsDocs::const_iterator DocI = Docs.begin(), DocE = Docs.end();
+       DocI != DocE; ++DocI)
+    for (std::vector<tooling::Replacement>::const_iterator
+             RI = DocI->Replacements.begin(),
+             RE = DocI->Replacements.end();
+         RI != RE; ++RI)
+      GroupedReplacements[RI->getFilePath()].push_back(*RI);
+
+  FileManager Files((FileSystemOptions()));
+  SourceManager SM(Diagnostics, Files);
+
+  // Ask clang to deduplicate and report conflicts.
+  if (deduplicateAndDetectConflicts(GroupedReplacements, SM, Diagnostics))
+    return false;
+
+  // FIXME: Time to apply
+
+  return true;
+}
Index: clang-replace/ApplyReplacements.h
===================================================================
--- /dev/null
+++ clang-replace/ApplyReplacements.h
@@ -0,0 +1,60 @@
+//===-- Core/ApplyChangeDescriptions.h --------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the interface for finding and applying change
+/// description files.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef CPP11_MIGRATE_APPLYCHANGEDESCRIPTIONS_H
+#define CPP11_MIGRATE_APPLYCHANGEDESCRIPTIONS_H
+
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/system_error.h"
+#include <vector>
+
+// Forward declarations
+
+namespace clang {
+class DiagnosticsEngine;
+} // namespace clang
+
+typedef std::vector<clang::tooling::ReplacementsDocument> ReplacementsDocs;
+
+/// \brief Recursively descends through a directory structure rooted at \p
+/// Directory and attempts to parse *.yaml files as ReplacementsDocs. All
+/// docs that successfully parse are added to \p Docs.
+///
+/// Directories starting with '.' are ignored during traversal.
+///
+/// \param[in] Directory Directory to begin search for ReplacementsDocs.
+/// \param[out] Docs Collection of all found and parsed ReplacementsDocs.
+/// \param[in] Diagnostics DiagnosticsEngine used for error output.
+///
+/// \returns An error_code indicating success or failure in navigating the
+/// directory structure.
+llvm::error_code collectReplacementsDocs(const llvm::StringRef Directory,
+                                         ReplacementsDocs &Docs,
+                                         clang::DiagnosticsEngine &Diagnostics);
+
+/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
+/// in \c Docs. If conflicts occur, no Replacements are applied.
+///
+/// \param[in] Docs Collection of ReplacementsDocs to deduplicate, test for
+/// conflicts, and apply.
+/// \param[in] Diagnostics DiagnosticsEngine used for error/warning output.
+///
+/// \returns \li true If all changes were applied successfully.
+///          \li false If there were conflicts.
+bool applyReplacements(const ReplacementsDocs &Docs,
+                       clang::DiagnosticsEngine &Diagnostics);
+
+#endif // CPP11_MIGRATE_APPLYCHANGEDESCRIPTIONS_H
Index: clang-replace/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-replace/CMakeLists.txt
@@ -0,0 +1,19 @@
+set(LLVM_LINK_COMPONENTS
+  ${LLVM_TARGETS_TO_BUILD}
+  asmparser
+  bitreader
+  support
+  mc
+  )
+
+add_clang_library(clangReplace
+  ApplyReplacements.cpp
+  )
+target_link_libraries(clangReplace
+  clangTooling
+  clangBasic
+  clangRewriteFrontend
+  )
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+add_subdirectory(tool)
Index: clang-replace/Makefile
===================================================================
--- /dev/null
+++ clang-replace/Makefile
@@ -0,0 +1,16 @@
+##===- clang-replace/Makefile ------------------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../../..
+LIBRARYNAME := clangReplace
+include $(CLANG_LEVEL)/../../Makefile.config
+
+DIRS = tool
+
+include $(CLANG_LEVEL)/Makefile
Index: clang-replace/tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-replace/tool/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS
+  ${LLVM_TARGETS_TO_BUILD}
+  asmparser
+  bitreader
+  support
+  mc
+  )
+
+add_clang_executable(clang-replace
+  ClangReplaceMain.cpp
+  )
+target_link_libraries(clang-replace
+  clangReplace
+  )
+
+install(TARGETS clang-replace
+  RUNTIME DESTINATION bin)
Index: clang-replace/tool/ClangReplaceMain.cpp
===================================================================
--- /dev/null
+++ clang-replace/tool/ClangReplaceMain.cpp
@@ -0,0 +1,47 @@
+//===-- ClangReplaceMain.cpp - Main file for clang-replace tool -----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the main function for the clang-replace tool.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ApplyReplacements.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+using namespace clang;
+
+static cl::opt<std::string> Directory(cl::Positional, cl::Required,
+                                      cl::desc("<Search Root Directory>"));
+
+int main(int argc, char **argv) {
+  cl::ParseCommandLineOptions(argc, argv);
+
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
+      DiagOpts.getPtr());
+
+  ReplacementsDocs Docs;
+
+  error_code ErrorCode = collectReplacementsDocs(Directory, Docs, Diagnostics);
+
+  if (ErrorCode) {
+    errs() << "Trouble iterating over directory '" << Directory
+           << "': " << ErrorCode.message() << "\n";
+    return false;
+  }
+
+  if (applyReplacements(Docs, Diagnostics))
+    return 0;
+  return 1;
+}
Index: clang-replace/tool/Makefile
===================================================================
--- /dev/null
+++ clang-replace/tool/Makefile
@@ -0,0 +1,28 @@
+##===- clang-replace/tool/Makefile -------------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../../../..
+include $(CLANG_LEVEL)/../../Makefile.config
+
+TOOLNAME = clang-replace
+
+# No plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+SOURCES = ClangReplaceMain.cpp
+
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
+USEDLIBS = clangReplace.a clangFormat.a clangTooling.a clangFrontend.a \
+	   clangSerialization.a clangDriver.a clangRewriteFrontend.a \
+	   clangRewriteCore.a clangParse.a clangSema.a clangAnalysis.a \
+	   clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/Makefile
+
+CPP.Flags += -I$(PROJ_SRC_DIR)/..
Index: cpp11-migrate/Core/FileOverrides.cpp
===================================================================
--- cpp11-migrate/Core/FileOverrides.cpp
+++ cpp11-migrate/Core/FileOverrides.cpp
@@ -19,6 +19,7 @@
 #include "clang/Basic/SourceManager.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Tooling/Tooling.h"
+#include "clang/Tooling/ReplacementsYaml.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
@@ -30,9 +31,9 @@
 
 void HeaderOverride::recordReplacements(
     const clang::tooling::Replacements &Replaces) {
-  MigratorDoc.Replacements.resize(Replaces.size());
+  Replacements.Replacements.resize(Replaces.size());
   std::copy(Replaces.begin(), Replaces.end(),
-            MigratorDoc.Replacements.begin());
+            Replacements.Replacements.begin());
 }
 
 SourceOverrides::SourceOverrides(llvm::StringRef MainFileName,
@@ -93,7 +94,7 @@
     // pipeline.
     HeaderOverride &HeaderOv = Headers[FileName];
     // "Create" HeaderOverride if not already existing
-    if (HeaderOv.getFileName().empty())
+    if (HeaderOv.getHeaderPath().empty())
       HeaderOv = HeaderOverride(FileName, MainFileName);
 
     HeaderOv.swapContentOverride(ResultBuf);
@@ -150,7 +151,7 @@
     assert(!I->second.getContentOverride().empty() &&
            "Header override should not be empty!");
     SM.overrideFileContents(
-        FM.getFile(I->second.getFileName()),
+        FM.getFile(I->second.getHeaderPath()),
         llvm::MemoryBuffer::getMemBuffer(I->second.getContentOverride()));
   }
 }
Index: cpp11-migrate/Core/FileOverrides.h
===================================================================
--- cpp11-migrate/Core/FileOverrides.h
+++ cpp11-migrate/Core/FileOverrides.h
@@ -16,8 +16,8 @@
 #ifndef CPP11_MIGRATE_FILE_OVERRIDES_H
 #define CPP11_MIGRATE_FILE_OVERRIDES_H
 
-#include "Core/ReplacementsYaml.h"
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/ReplacementsYaml.h"
 #include "llvm/ADT/StringMap.h"
 
 // Forward Declarations
@@ -64,16 +64,15 @@
   /// \brief Constructors.
   /// @{
   HeaderOverride() {}
-  HeaderOverride(llvm::StringRef TargetFile,
-                 llvm::StringRef MainSourceFile) {
-    MigratorDoc.TargetFile = TargetFile;
-    MigratorDoc.MainSourceFile= MainSourceFile;
+  HeaderOverride(llvm::StringRef HeaderPath,
+                 llvm::StringRef MainSourceFile) : HeaderPath(HeaderPath) {
+    Replacements.MainSourceFile = MainSourceFile;
   }
   /// @}
 
-  /// \brief Getter for FileName.
-  llvm::StringRef getFileName() const {
-    return MigratorDoc.TargetFile;
+  /// \brief Getter for HeaderPath.
+  llvm::StringRef getHeaderPath() const {
+    return HeaderPath;
   }
 
   /// \brief Accessor for ContentOverride.
@@ -88,9 +87,9 @@
   /// \brief Swaps the content of ContentOverride with \p S.
   void swapContentOverride(std::string &S) { ContentOverride.swap(S); }
 
-  /// \brief Getter for MigratorDoc.
-  const MigratorDocument &getMigratorDoc() const {
-    return MigratorDoc;
+  /// \brief Getter for Replacements.
+  const clang::tooling::TranslationUnitReplacements &getReplacements() const {
+    return Replacements;
   }
 
   /// \brief Stores the replacements made by a transform to the header this
@@ -105,7 +104,8 @@
 private:
   std::string ContentOverride;
   ChangedRanges Changes;
-  MigratorDocument MigratorDoc;
+  std::string HeaderPath;
+  clang::tooling::TranslationUnitReplacements Replacements;
 };
 
 /// \brief Container mapping header file names to override information.
Index: cpp11-migrate/Core/Reformatting.cpp
===================================================================
--- cpp11-migrate/Core/Reformatting.cpp
+++ cpp11-migrate/Core/Reformatting.cpp
@@ -47,7 +47,7 @@
        I != E; ++I) {
     const HeaderOverride &Header = I->getValue();
     const tooling::Replacements &HeaderReplaces =
-        reformatSingleFile(Header.getFileName(), Header.getChanges(), SM);
+        reformatSingleFile(Header.getHeaderPath(), Header.getChanges(), SM);
     Replaces.insert(HeaderReplaces.begin(), HeaderReplaces.end());
   }
   Overrides.applyReplacements(Replaces, SM);
Index: cpp11-migrate/Core/ReplacementsYaml.h
===================================================================
--- cpp11-migrate/Core/ReplacementsYaml.h
+++ /dev/null
@@ -1,105 +0,0 @@
-//===-- Core/ReplacementsYaml.h ---------------------------------*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// \brief This file provides functionality to serialize replacements for a
-/// single file. It is used by the C++11 Migrator to store all the changes made
-/// by a single transform to a particular file resulting from migrating a
-/// translation unit of a particular main source file.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef CPP11_MIGRATE_REPLACEMENTS_YAML_H
-#define CPP11_MIGRATE_REPLACEMENTS_YAML_H
-
-#include "clang/Tooling/Refactoring.h"
-#include "llvm/Support/YAMLTraits.h"
-#include <vector>
-#include <string>
-
-/// \brief The top-level YAML document that contains the details for the
-/// replacement.
-struct MigratorDocument {
-  std::vector<clang::tooling::Replacement> Replacements;
-  std::string TargetFile;
-  std::string MainSourceFile;
-};
-
-// FIXME: Put the YAML support for Replacement into clang::tooling. NOTE: The
-// implementation below doesn't serialize the filename for Replacements.
-
-LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::Replacement)
-
-namespace llvm {
-namespace yaml {
-
-/// \brief ScalarTraits to read/write std::string objects.
-template <>
-struct ScalarTraits<std::string> {
-  static void output(const std::string &Val, void *, llvm::raw_ostream &Out) {
-    // We need to put quotes around the string to make sure special characters
-    // in the string is not treated as YAML tokens.
-    std::string NormalizedVal = std::string("\"") + Val + std::string("\"");
-    Out << NormalizedVal;
-  }
-
-  static StringRef input(StringRef Scalar, void *, std::string &Val) {
-    Val = Scalar;
-    return StringRef();
-  }
-};
-
-/// \brief Specialized MappingTraits for Repleacements to be converted to/from
-/// a YAML File.
-template <>
-struct MappingTraits<clang::tooling::Replacement> {
-  /// \brief Normalize clang::tooling::Replacement to provide direct access to
-  /// its members.
-  struct NormalizedReplacement {
-    NormalizedReplacement(const IO &)
-        : FilePath(""), Offset(0), Length(0), ReplacementText("") {}
-
-    NormalizedReplacement(const IO &, const clang::tooling::Replacement &R)
-        : FilePath(R.getFilePath()), Offset(R.getOffset()),
-          Length(R.getLength()), ReplacementText(R.getReplacementText()) {}
-
-    clang::tooling::Replacement denormalize(const IO &) {
-      return clang::tooling::Replacement(FilePath, Offset, Length,
-                                         ReplacementText);
-    }
-
-    std::string FilePath;
-    unsigned int Offset;
-    unsigned int Length;
-    std::string ReplacementText;
-  };
-
-  static void mapping(IO &Io, clang::tooling::Replacement &R) {
-    MappingNormalization<NormalizedReplacement, clang::tooling::Replacement>
-        Keys(Io, R);
-    Io.mapRequired("Offset", Keys->Offset);
-    Io.mapRequired("Length", Keys->Length);
-    Io.mapRequired("ReplacementText", Keys->ReplacementText);
-  }
-};
-
-/// \brief Specialized MappingTraits for MigratorDocument to be converted
-/// to/from a YAML File.
-template <>
-struct MappingTraits<MigratorDocument> {
-  static void mapping(IO &Io, MigratorDocument &TD) {
-    Io.mapRequired("Replacements", TD.Replacements);
-    Io.mapRequired("TargetFile", TD.TargetFile);
-    Io.mapRequired("MainSourceFile", TD.MainSourceFile);
-  }
-};
-} // end namespace yaml
-} // end namespace llvm
-
-#endif // CPP11_MIGRATE_REPLACEMENTS_YAML_H
Index: cpp11-migrate/tool/Cpp11Migrate.cpp
===================================================================
--- cpp11-migrate/tool/Cpp11Migrate.cpp
+++ cpp11-migrate/tool/Cpp11Migrate.cpp
@@ -379,8 +379,8 @@
           continue;
         }
         llvm::yaml::Output YAML(ReplacementsFile);
-        YAML << const_cast<MigratorDocument &>(
-                    HeaderI->getValue().getMigratorDoc());
+        YAML << const_cast<TranslationUnitReplacements &>(
+                    HeaderI->getValue().getReplacements());
       } else {
         // If -yaml-only was not specified, then change headers on disk.
         // FIXME: This is transitional behaviour. Remove this functionality
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -27,7 +27,7 @@
   clang clang-headers FileCheck count not
 
   # Individual tools we test.
-  remove-cstr-calls cpp11-migrate modularize clang-tidy
+  remove-cstr-calls clang-replace cpp11-migrate modularize clang-tidy
 
   # Unit tests
   ExtraToolsUnitTests
Index: test/clang-replace/conflict.cpp
===================================================================
--- /dev/null
+++ test/clang-replace/conflict.cpp
@@ -0,0 +1,7 @@
+// RUN: mkdir -p %T/conflict
+// RUN: sed "s#\$(path)#%/S/conflict#" %S/conflict/file1.yaml > %T/conflict/file1.yaml
+// RUN: sed "s#\$(path)#%/S/conflict#" %S/conflict/file2.yaml > %T/conflict/file2.yaml
+// RUN: sed "s#\$(path)#%/S/conflict#" %S/conflict/file3.yaml > %T/conflict/file3.yaml
+// RUN: sed "s#\$(path)#%/S/conflict#" %S/conflict/expected.txt > %T/conflict/expected.txt
+// RUN: not clang-replace %T/conflict > %T/conflict/output.txt 2>&1
+// RUN: diff -b %T/conflict/output.txt %T/conflict/expected.txt
Index: test/clang-replace/conflict/common.h
===================================================================
--- /dev/null
+++ test/clang-replace/conflict/common.h
@@ -0,0 +1,17 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+extern void ext(int (&)[5]);
+
+void func(int t) {
+  int ints[5];
+  for (unsigned i = 0; i < 5; ++i) {
+    ints[i] = t;
+  }
+
+  int *i = 0;
+
+  ext(ints);
+}
+
+#endif // COMMON_H
Index: test/clang-replace/conflict/expected.txt
===================================================================
--- /dev/null
+++ test/clang-replace/conflict/expected.txt
@@ -0,0 +1,11 @@
+There are conflicting changes to $(path)/common.h:
+The following changes conflict:
+  Replace 8:8-8:33 with "auto & i : ints"
+  Replace 8:8-8:33 with "int & elem : ints"
+The following changes conflict:
+  Replace 9:5-9:11 with "elem"
+  Replace 9:5-9:11 with "i"
+The following changes conflict:
+  Remove 12:3-12:14
+  Insert at 12:12 (int*)
+  Replace 12:12-12:12 with "nullptr"
Index: test/clang-replace/conflict/file1.yaml
===================================================================
--- /dev/null
+++ test/clang-replace/conflict/file1.yaml
@@ -0,0 +1,16 @@
+---
+Context: "Main Source File: source1.cpp"
+Replacements:
+  - FilePath:        "$(path)/common.h"
+    Offset:          106
+    Length:          26
+    ReplacementText: "auto & i : ints"
+  - FilePath:        "$(path)/common.h"
+    Offset:          140
+    Length:          7
+    ReplacementText: "i"
+  - FilePath:        "$(path)/common.h"
+    Offset:          160
+    Length:          12
+    ReplacementText: ""
+...
Index: test/clang-replace/conflict/file2.yaml
===================================================================
--- /dev/null
+++ test/clang-replace/conflict/file2.yaml
@@ -0,0 +1,16 @@
+---
+Context: "Main Source File: source2.cpp"
+Replacements:
+  - FilePath:        "$(path)/common.h"
+    Offset:          106
+    Length:          26
+    ReplacementText: "int & elem : ints"
+  - FilePath:        "$(path)/common.h"
+    Offset:          140
+    Length:          7
+    ReplacementText: "elem"
+  - FilePath:        "$(path)/common.h"
+    Offset:          169
+    Length:          1
+    ReplacementText: "nullptr"
+...
Index: test/clang-replace/conflict/file3.yaml
===================================================================
--- /dev/null
+++ test/clang-replace/conflict/file3.yaml
@@ -0,0 +1,8 @@
+---
+Context: "Main Source File: source1.cpp"
+Replacements:
+  - FilePath:        "$(path)/common.h"
+    Offset:          169
+    Length:          0
+    ReplacementText: "(int*)"
+...
Index: test/cpp11-migrate/HeaderReplacements/common.h.yaml
===================================================================
--- test/cpp11-migrate/HeaderReplacements/common.h.yaml
+++ test/cpp11-migrate/HeaderReplacements/common.h.yaml
@@ -1,11 +1,12 @@
 ---
+MainSourceFile:      "$(path)/main.cpp"
 Replacements:
-  - Offset:          432
+  - FilePath:        "$(path)/common.h"
+    Offset:          432
     Length:          61
     ReplacementText: "(auto & elem : C)"
-  - Offset:          506
+  - FilePath:       "$(path)/common.h"
+    Offset:          506
     Length:          2
     ReplacementText: "elem"
-TargetFile:      "$(path)/common.h"
-MainSourceFile:  "$(path)/main.cpp"
 ...
Index: unittests/cpp11-migrate/CMakeLists.txt
===================================================================
--- unittests/cpp11-migrate/CMakeLists.txt
+++ unittests/cpp11-migrate/CMakeLists.txt
@@ -11,7 +11,6 @@
   ReformattingTest.cpp
   IncludeExcludeTest.cpp
   PerfSupportTest.cpp
-  ReplacementsYamlTest.cpp
   TransformTest.cpp
   UniqueHeaderNameTest.cpp
   )
Index: unittests/cpp11-migrate/ReplacementsYamlTest.cpp
===================================================================
--- unittests/cpp11-migrate/ReplacementsYamlTest.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//===- unittests/cpp11-migrate/ReplacementsYamlTest.cpp -------------------===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Tests that change description files can be written and read.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Utility.h"
-#include "Core/FileOverrides.h"
-#include "gtest/gtest.h"
-
-using namespace llvm;
-
-TEST(ReplacementsYamlTest, writeReadTest) {
-  using clang::tooling::Replacement;
-
-  const std::string TargetFile = "/path/to/common.h";
-  const std::string MainSourceFile = "/path/to/source.cpp";
-  const unsigned int ReplacementOffset1 = 232;
-  const unsigned int ReplacementLength1 = 56;
-  const std::string ReplacementText1 = "(auto & elem : V)";
-  const unsigned int ReplacementOffset2 = 301;
-  const unsigned int ReplacementLength2 = 2;
-  const std::string ReplacementText2 = "elem";
-
-  MigratorDocument Doc;
-  Doc.Replacements.push_back(Replacement(TargetFile, ReplacementOffset1,
-                                        ReplacementLength1, ReplacementText1));
-  Doc.Replacements.push_back(Replacement(TargetFile, ReplacementOffset2,
-                                        ReplacementLength2, ReplacementText2));
-
-  Doc.TargetFile = TargetFile.c_str();
-  Doc.MainSourceFile= MainSourceFile.c_str();
-
-  std::string YamlContent;
-  llvm::raw_string_ostream YamlContentStream(YamlContent);
-
-  // Write to the YAML file.
-  {
-    yaml::Output YAML(YamlContentStream);
-    YAML << Doc;
-    YamlContentStream.str();
-    ASSERT_NE(YamlContent.length(), 0u);
-  }
-
-  // Read from the YAML file and verify that what was written is exactly what
-  // we read back.
-  {
-    MigratorDocument DocActual;
-    yaml::Input YAML(YamlContent);
-    YAML >> DocActual;
-    ASSERT_NO_ERROR(YAML.error());
-    EXPECT_EQ(TargetFile, DocActual.TargetFile);
-    EXPECT_EQ(MainSourceFile, DocActual.MainSourceFile);
-    ASSERT_EQ(2u, DocActual.Replacements.size());
-
-    EXPECT_EQ(ReplacementOffset1, DocActual.Replacements[0].getOffset());
-    EXPECT_EQ(ReplacementLength1, DocActual.Replacements[0].getLength());
-    EXPECT_EQ(ReplacementText1,
-              DocActual.Replacements[0].getReplacementText().str());
-
-    EXPECT_EQ(ReplacementOffset2, DocActual.Replacements[1].getOffset());
-    EXPECT_EQ(ReplacementLength2, DocActual.Replacements[1].getLength());
-    EXPECT_EQ(ReplacementText2,
-              DocActual.Replacements[1].getReplacementText().str());
-  }
-}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to