Implement tooling::Replacements as a class.

Summary:
- Implement clang::tooling::Replacements as a class to provide interfaces to
  control how replacements for a single file are combined and provide guarantee
  on the order of replacements being applied.
- tooling::Replacements only contains replacements for the same file now.
  Use std::map<std::string, tooling::Replacements> to represent multi-file
  replacements.
- Error handling for the interface change will be improved in followup patches.

Reviewers: djasper, klimek

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D21748

llvm-svn: 277335
diff --git a/clang/unittests/Tooling/RefactoringTest.cpp b/clang/unittests/Tooling/RefactoringTest.cpp
index 78fab8c..6fbb328 100644
--- a/clang/unittests/Tooling/RefactoringTest.cpp
+++ b/clang/unittests/Tooling/RefactoringTest.cpp
@@ -7,6 +7,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "ReplacementTest.h"
 #include "RewriterTestContext.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
@@ -31,16 +32,6 @@
 namespace clang {
 namespace tooling {
 
-class ReplacementTest : public ::testing::Test {
- protected:
-  Replacement createReplacement(SourceLocation Start, unsigned Length,
-                                llvm::StringRef ReplacementText) {
-    return Replacement(Context.Sources, Start, Length, ReplacementText);
-  }
-
-  RewriterTestContext Context;
-};
-
 TEST_F(ReplacementTest, CanDeleteAllText) {
   FileID ID = Context.createInMemoryFile("input.cpp", "text");
   SourceLocation Location = Context.getLocation(ID, 1, 1);
@@ -108,29 +99,30 @@
   EXPECT_TRUE(Replace2.getFilePath().empty());
 }
 
+TEST_F(ReplacementTest, FailAddReplacements) {
+  Replacements Replaces;
+  auto Err = Replaces.add(Replacement("x.cc", 0, 10, "3"));
+  EXPECT_TRUE(!Err);
+  llvm::consumeError(std::move(Err));
+  Err = Replaces.add(Replacement("x.cc", 0, 2, ""));
+  EXPECT_TRUE((bool)Err);
+  llvm::consumeError(std::move(Err));
+  Err = Replaces.add(Replacement("x.cc", 2, 2, ""));
+  EXPECT_TRUE((bool)Err);
+  llvm::consumeError(std::move(Err));
+  Err = Replaces.add(Replacement("y.cc", 20, 2, ""));
+  EXPECT_TRUE((bool)Err);
+  llvm::consumeError(std::move(Err));
+}
+
 TEST_F(ReplacementTest, CanApplyReplacements) {
   FileID ID = Context.createInMemoryFile("input.cpp",
                                          "line1\nline2\nline3\nline4");
-  Replacements Replaces;
-  Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
-                              5, "replaced"));
-  Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 3, 1),
-                              5, "other"));
-  EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
-  EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
-}
-
-// FIXME: Remove this test case when Replacements is implemented as std::vector
-// instead of std::set. The other ReplacementTest tests will need to be updated
-// at that point as well.
-TEST_F(ReplacementTest, VectorCanApplyReplacements) {
-  FileID ID = Context.createInMemoryFile("input.cpp",
-                                         "line1\nline2\nline3\nline4");
-  std::vector<Replacement> Replaces;
-  Replaces.push_back(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
-                                 5, "replaced"));
-  Replaces.push_back(
-      Replacement(Context.Sources, Context.getLocation(ID, 3, 1), 5, "other"));
+  Replacements Replaces =
+      toReplacements({Replacement(Context.Sources,
+                                  Context.getLocation(ID, 2, 1), 5, "replaced"),
+                      Replacement(Context.Sources,
+                                  Context.getLocation(ID, 3, 1), 5, "other")});
   EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
   EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
 }
@@ -138,32 +130,28 @@
 TEST_F(ReplacementTest, SkipsDuplicateReplacements) {
   FileID ID = Context.createInMemoryFile("input.cpp",
                                          "line1\nline2\nline3\nline4");
-  Replacements Replaces;
-  Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
-                              5, "replaced"));
-  Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
-                              5, "replaced"));
-  Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
-                              5, "replaced"));
+  auto Replaces = toReplacements({Replacement(
+      Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")});
+
+  auto Err = Replaces.add(Replacement(
+      Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced"));
+  EXPECT_TRUE((bool)Err);
+  llvm::consumeError(std::move(Err));
+
+  Err = Replaces.add(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+                                 5, "replaced"));
+  EXPECT_TRUE((bool)Err);
+  llvm::consumeError(std::move(Err));
+
   EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
   EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID));
 }
 
-TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) {
-  // This test depends on the value of the file name of an invalid source
-  // location being in the range ]a, z[.
-  FileID IDa = Context.createInMemoryFile("a.cpp", "text");
-  FileID IDz = Context.createInMemoryFile("z.cpp", "text");
-  Replacements Replaces;
-  Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDa, 1, 1),
-                              4, "a"));
-  Replaces.insert(Replacement(Context.Sources, SourceLocation(),
-                              5, "2"));
-  Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDz, 1, 1),
-                              4, "z"));
+TEST_F(ReplacementTest, InvalidSourceLocationFailsApplyAll) {
+  Replacements Replaces =
+      toReplacements({Replacement(Context.Sources, SourceLocation(), 5, "2")});
+
   EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite));
-  EXPECT_EQ("a", Context.getRewrittenText(IDa));
-  EXPECT_EQ("z", Context.getRewrittenText(IDz));
 }
 
 TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
@@ -179,76 +167,66 @@
   std::string Expected2 = "int x =\n"
                           "    1234567890123;\n"
                           "int y = 10;";
-  FileID ID1 = Context.createInMemoryFile("format_1.cpp", Code1);
-  FileID ID2 = Context.createInMemoryFile("format_2.cpp", Code2);
+  StringRef File1 = "format_1.cpp";
+  StringRef File2 = "format_2.cpp";
+  FileID ID1 = Context.createInMemoryFile(File1, Code1);
+  FileID ID2 = Context.createInMemoryFile(File2, Code2);
 
-  tooling::Replacements Replaces;
   // Scrambled the order of replacements.
-  Replaces.insert(tooling::Replacement(
-      Context.Sources, Context.getLocation(ID2, 1, 12), 0, "4567890123"));
-  Replaces.insert(tooling::Replacement(
-      Context.Sources, Context.getLocation(ID1, 1, 1), 6, "auto "));
-  Replaces.insert(tooling::Replacement(
-      Context.Sources, Context.getLocation(ID2, 2, 9), 1, "10"));
-  Replaces.insert(tooling::Replacement(
-      Context.Sources, Context.getLocation(ID1, 3, 10), 1, "12345678901"));
-
-  EXPECT_TRUE(formatAndApplyAllReplacements(
-      Replaces, Context.Rewrite, "{BasedOnStyle: LLVM, ColumnLimit: 20}"));
+  std::map<std::string, Replacements> FileToReplaces;
+  FileToReplaces[File1] = toReplacements(
+      {tooling::Replacement(Context.Sources, Context.getLocation(ID1, 1, 1), 6,
+                            "auto "),
+       tooling::Replacement(Context.Sources, Context.getLocation(ID1, 3, 10), 1,
+                            "12345678901")});
+  FileToReplaces[File2] = toReplacements(
+      {tooling::Replacement(Context.Sources, Context.getLocation(ID2, 1, 12), 0,
+                            "4567890123"),
+       tooling::Replacement(Context.Sources, Context.getLocation(ID2, 2, 9), 1,
+                            "10")});
+  EXPECT_TRUE(
+      formatAndApplyAllReplacements(FileToReplaces, Context.Rewrite,
+                                    "{BasedOnStyle: LLVM, ColumnLimit: 20}"));
   EXPECT_EQ(Expected1, Context.getRewrittenText(ID1));
   EXPECT_EQ(Expected2, Context.getRewrittenText(ID2));
 }
 
 TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
-  Replacements Replaces;
-  Replaces.insert(Replacement("", 0, 1, ""));
-  Replaces.insert(Replacement("", 4, 3, " "));
+  Replacements Replaces =
+      toReplacements({Replacement("", 0, 1, ""), Replacement("", 4, 3, " ")});
   // Assume ' int   i;' is turned into 'int i;' and cursor is located at '|'.
-  EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int   i;
-  EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); //  |nt   i;
-  EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); //  i|t   i;
-  EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); //  in|   i;
-  EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); //  int|  i;
-  EXPECT_EQ(3u, shiftedCodePosition(Replaces, 5)); //  int | i;
-  EXPECT_EQ(3u, shiftedCodePosition(Replaces, 6)); //  int  |i;
-  EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); //  int   |;
-  EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); //  int   i|
-}
-
-// FIXME: Remove this test case when Replacements is implemented as std::vector
-// instead of std::set. The other ReplacementTest tests will need to be updated
-// at that point as well.
-TEST(ShiftedCodePositionTest, VectorFindsNewCodePositionWithInserts) {
-  std::vector<Replacement> Replaces;
-  Replaces.push_back(Replacement("", 0, 1, ""));
-  Replaces.push_back(Replacement("", 4, 3, " "));
-  // Assume ' int   i;' is turned into 'int i;' and cursor is located at '|'.
-  EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int   i;
-  EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); //  |nt   i;
-  EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); //  i|t   i;
-  EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); //  in|   i;
-  EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); //  int|  i;
-  EXPECT_EQ(3u, shiftedCodePosition(Replaces, 5)); //  int | i;
-  EXPECT_EQ(3u, shiftedCodePosition(Replaces, 6)); //  int  |i;
-  EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); //  int   |;
-  EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); //  int   i|
+  EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0)); // |int   i;
+  EXPECT_EQ(0u, Replaces.getShiftedCodePosition(1)); //  |nt   i;
+  EXPECT_EQ(1u, Replaces.getShiftedCodePosition(2)); //  i|t   i;
+  EXPECT_EQ(2u, Replaces.getShiftedCodePosition(3)); //  in|   i;
+  EXPECT_EQ(3u, Replaces.getShiftedCodePosition(4)); //  int|  i;
+  EXPECT_EQ(3u, Replaces.getShiftedCodePosition(5)); //  int | i;
+  EXPECT_EQ(3u, Replaces.getShiftedCodePosition(6)); //  int  |i;
+  EXPECT_EQ(4u, Replaces.getShiftedCodePosition(7)); //  int   |;
+  EXPECT_EQ(5u, Replaces.getShiftedCodePosition(8)); //  int   i|
 }
 
 TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) {
-  Replacements Replaces;
-  Replaces.insert(Replacement("", 4, 0, "\"\n\""));
+  Replacements Replaces = toReplacements({Replacement("", 4, 0, "\"\n\"")});
   // Assume '"12345678"' is turned into '"1234"\n"5678"'.
-  EXPECT_EQ(3u, shiftedCodePosition(Replaces, 3)); // "123|5678"
-  EXPECT_EQ(7u, shiftedCodePosition(Replaces, 4)); // "1234|678"
-  EXPECT_EQ(8u, shiftedCodePosition(Replaces, 5)); // "12345|78"
+  EXPECT_EQ(3u, Replaces.getShiftedCodePosition(3)); // "123|5678"
+  EXPECT_EQ(7u, Replaces.getShiftedCodePosition(4)); // "1234|678"
+  EXPECT_EQ(8u, Replaces.getShiftedCodePosition(5)); // "12345|78"
 }
 
 TEST(ShiftedCodePositionTest, FindsNewCodePositionInReplacedText) {
-  Replacements Replaces;
   // Replace the first four characters with "abcd".
-  Replaces.insert(Replacement("", 0, 4, "abcd"));
+  auto Replaces = toReplacements({Replacement("", 0, 4, "abcd")});
   for (unsigned i = 0; i < 3; ++i)
-    EXPECT_EQ(i, shiftedCodePosition(Replaces, i));
+    EXPECT_EQ(i, Replaces.getShiftedCodePosition(i));
+}
+
+TEST(ShiftedCodePositionTest, NoReplacementText) {
+  Replacements Replaces = toReplacements({Replacement("", 0, 42, "")});
+  EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0));
+  EXPECT_EQ(0u, Replaces.getShiftedCodePosition(39));
+  EXPECT_EQ(3u, Replaces.getShiftedCodePosition(45));
+  EXPECT_EQ(0u, Replaces.getShiftedCodePosition(42));
 }
 
 class FlushRewrittenFilesTest : public ::testing::Test {
@@ -304,9 +282,8 @@
 
 TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) {
   FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4");
-  Replacements Replaces;
-  Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
-                              5, "replaced"));
+  Replacements Replaces = toReplacements({Replacement(
+      Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")});
   EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
   EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
   EXPECT_EQ("line1\nreplaced\nline3\nline4",
@@ -454,12 +431,11 @@
 TEST(Range, CalculateRangesOfReplacements) {
   // Before: aaaabbbbbbz
   // After : bbbbbbzzzzzzoooooooooooooooo
-  Replacements Replaces;
-  Replaces.insert(Replacement("foo", 0, 4, ""));
-  Replaces.insert(Replacement("foo", 10, 1, "zzzzzz"));
-  Replaces.insert(Replacement("foo", 11, 0, "oooooooooooooooo"));
+  Replacements Replaces = toReplacements(
+      {Replacement("foo", 0, 4, ""), Replacement("foo", 10, 1, "zzzzzz"),
+       Replacement("foo", 11, 0, "oooooooooooooooo")});
 
-  std::vector<Range> Ranges = calculateChangedRanges(Replaces);
+  std::vector<Range> Ranges = Replaces.getAffectedRanges();
 
   EXPECT_EQ(2ul, Ranges.size());
   EXPECT_TRUE(Ranges[0].getOffset() == 0);
@@ -470,23 +446,23 @@
 
 TEST(Range, RangesAfterReplacements) {
   std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
-  Replacements Replaces = {Replacement("foo", 0, 2, "1234")};
+  Replacements Replaces = toReplacements({Replacement("foo", 0, 2, "1234")});
   std::vector<Range> Expected = {Range(0, 4), Range(7, 2), Range(12, 5)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
 }
 
 TEST(Range, RangesBeforeReplacements) {
   std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
-  Replacements Replaces = {Replacement("foo", 20, 2, "1234")};
+  Replacements Replaces = toReplacements({Replacement("foo", 20, 2, "1234")});
   std::vector<Range> Expected = {Range(5, 2), Range(10, 5), Range(20, 4)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
 }
 
 TEST(Range, NotAffectedByReplacements) {
   std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
-  Replacements Replaces = {Replacement("foo", 3, 2, "12"),
-                           Replacement("foo", 12, 2, "12"),
-                           Replacement("foo", 20, 5, "")};
+  Replacements Replaces = toReplacements({Replacement("foo", 3, 2, "12"),
+                                          Replacement("foo", 12, 2, "12"),
+                                          Replacement("foo", 20, 5, "")});
   std::vector<Range> Expected = {Range(0, 2), Range(3, 4), Range(10, 5),
                                  Range(20, 0)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
@@ -494,9 +470,9 @@
 
 TEST(Range, RangesWithNonOverlappingReplacements) {
   std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
-  Replacements Replaces = {Replacement("foo", 3, 1, ""),
-                           Replacement("foo", 6, 1, "123"),
-                           Replacement("foo", 20, 2, "12345")};
+  Replacements Replaces = toReplacements({Replacement("foo", 3, 1, ""),
+                                          Replacement("foo", 6, 1, "123"),
+                                          Replacement("foo", 20, 2, "12345")});
   std::vector<Range> Expected = {Range(0, 2), Range(3, 0), Range(4, 4),
                                  Range(11, 5), Range(21, 5)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
@@ -505,9 +481,9 @@
 TEST(Range, RangesWithOverlappingReplacements) {
   std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5),
                                Range(30, 5)};
-  Replacements Replaces = {
-      Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
-      Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")};
+  Replacements Replaces = toReplacements(
+      {Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
+       Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")});
   std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(12, 5),
                                  Range(22, 0)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
@@ -515,122 +491,52 @@
 
 TEST(Range, MergeIntoOneRange) {
   std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
-  Replacements Replaces = {Replacement("foo", 1, 15, "1234567890")};
+  Replacements Replaces =
+      toReplacements({Replacement("foo", 1, 15, "1234567890")});
   std::vector<Range> Expected = {Range(0, 15)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
 }
 
 TEST(Range, ReplacementsStartingAtRangeOffsets) {
   std::vector<Range> Ranges = {Range(0, 2), Range(5, 5), Range(15, 5)};
-  Replacements Replaces = {
-      Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
-      Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")};
+  Replacements Replaces = toReplacements(
+      {Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
+       Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")});
   std::vector<Range> Expected = {Range(0, 2), Range(5, 9), Range(18, 2)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
 }
 
 TEST(Range, ReplacementsEndingAtRangeEnds) {
   std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
-  Replacements Replaces = {Replacement("foo", 6, 1, "123"),
-                           Replacement("foo", 17, 3, "12")};
+  Replacements Replaces = toReplacements(
+      {Replacement("foo", 6, 1, "123"), Replacement("foo", 17, 3, "12")});
   std::vector<Range> Expected = {Range(0, 2), Range(5, 4), Range(17, 4)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
 }
 
 TEST(Range, AjacentReplacements) {
   std::vector<Range> Ranges = {Range(0, 0), Range(15, 5)};
-  Replacements Replaces = {Replacement("foo", 1, 2, "123"),
-                           Replacement("foo", 12, 3, "1234")};
+  Replacements Replaces = toReplacements(
+      {Replacement("foo", 1, 2, "123"), Replacement("foo", 12, 3, "1234")});
   std::vector<Range> Expected = {Range(0, 0), Range(1, 3), Range(13, 9)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
 }
 
 TEST(Range, MergeRangesAfterReplacements) {
   std::vector<Range> Ranges = {Range(8, 0), Range(5, 2), Range(9, 0), Range(0, 1)};
-  Replacements Replaces = {Replacement("foo", 1, 3, ""),
-                           Replacement("foo", 7, 0, "12"), Replacement("foo", 9, 2, "")};
-  std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0), Range(8, 0)};
+  Replacements Replaces = toReplacements({Replacement("foo", 1, 3, ""),
+                                          Replacement("foo", 7, 0, "12"),
+                                          Replacement("foo", 9, 2, "")});
+  std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0),
+                                 Range(8, 0)};
   EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
 }
 
-TEST(DeduplicateTest, removesDuplicates) {
-  std::vector<Replacement> Input;
-  Input.push_back(Replacement("fileA", 50, 0, " foo "));
-  Input.push_back(Replacement("fileA", 10, 3, " bar "));
-  Input.push_back(Replacement("fileA", 10, 2, " bar ")); // Length differs
-  Input.push_back(Replacement("fileA", 9,  3, " bar ")); // Offset differs
-  Input.push_back(Replacement("fileA", 50, 0, " foo ")); // Duplicate
-  Input.push_back(Replacement("fileA", 51, 3, " bar "));
-  Input.push_back(Replacement("fileB", 51, 3, " bar ")); // Filename differs!
-  Input.push_back(Replacement("fileB", 60, 1, " bar "));
-  Input.push_back(Replacement("fileA", 60, 2, " bar "));
-  Input.push_back(Replacement("fileA", 51, 3, " moo ")); // Replacement text
-                                                         // differs!
-
-  std::vector<Replacement> Expected;
-  Expected.push_back(Replacement("fileA", 9,  3, " bar "));
-  Expected.push_back(Replacement("fileA", 10, 2, " bar "));
-  Expected.push_back(Replacement("fileA", 10, 3, " bar "));
-  Expected.push_back(Replacement("fileA", 50, 0, " foo "));
-  Expected.push_back(Replacement("fileA", 51, 3, " bar "));
-  Expected.push_back(Replacement("fileA", 51, 3, " moo "));
-  Expected.push_back(Replacement("fileB", 60, 1, " bar "));
-  Expected.push_back(Replacement("fileA", 60, 2, " bar "));
-
-  std::vector<Range> Conflicts; // Ignored for this test
-  deduplicate(Input, Conflicts);
-
-  EXPECT_EQ(3U, Conflicts.size());
-  EXPECT_EQ(Expected, Input);
-}
-
-TEST(DeduplicateTest, detectsConflicts) {
-  {
-    std::vector<Replacement> Input;
-    Input.push_back(Replacement("fileA", 0, 5, " foo "));
-    Input.push_back(Replacement("fileA", 0, 5, " foo ")); // Duplicate not a
-                                                          // conflict.
-    Input.push_back(Replacement("fileA", 2, 6, " bar "));
-    Input.push_back(Replacement("fileA", 7, 3, " moo "));
-
-    std::vector<Range> Conflicts;
-    deduplicate(Input, Conflicts);
-
-    // One duplicate is removed and the remaining three items form one
-    // conflicted range.
-    ASSERT_EQ(3u, Input.size());
-    ASSERT_EQ(1u, Conflicts.size());
-    ASSERT_EQ(0u, Conflicts.front().getOffset());
-    ASSERT_EQ(3u, Conflicts.front().getLength());
-  }
-  {
-    std::vector<Replacement> Input;
-
-    // Expected sorted order is shown. It is the sorted order to which the
-    // returned conflict info refers to.
-    Input.push_back(Replacement("fileA", 0,  5, " foo "));  // 0
-    Input.push_back(Replacement("fileA", 5,  5, " bar "));  // 1
-    Input.push_back(Replacement("fileA", 6,  0, " bar "));  // 3
-    Input.push_back(Replacement("fileA", 5,  5, " moo "));  // 2
-    Input.push_back(Replacement("fileA", 7,  2, " bar "));  // 4
-    Input.push_back(Replacement("fileA", 15, 5, " golf ")); // 5
-    Input.push_back(Replacement("fileA", 16, 5, " bag "));  // 6
-    Input.push_back(Replacement("fileA", 10, 3, " club ")); // 7
-
-    // #3 is special in that it is completely contained by another conflicting
-    // Replacement. #4 ensures #3 hasn't messed up the conflicting range size.
-
-    std::vector<Range> Conflicts;
-    deduplicate(Input, Conflicts);
-
-    // No duplicates
-    ASSERT_EQ(8u, Input.size());
-    ASSERT_EQ(2u, Conflicts.size());
-    ASSERT_EQ(1u, Conflicts[0].getOffset());
-    ASSERT_EQ(4u, Conflicts[0].getLength());
-    ASSERT_EQ(6u, Conflicts[1].getOffset());
-    ASSERT_EQ(2u, Conflicts[1].getLength());
-  }
+TEST(Range, ConflictingRangesBeforeReplacements) {
+  std::vector<Range> Ranges = {Range(8, 3), Range(5, 4), Range(9, 1)};
+  Replacements Replaces = toReplacements({Replacement("foo", 1, 3, "")});
+  std::vector<Range> Expected = {Range(1, 0), Range(2, 6)};
+  EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
 }
 
 class MergeReplacementsTest : public ::testing::Test {
@@ -646,7 +552,7 @@
     EXPECT_EQ(Intermediate, *AfterFirst);
     EXPECT_EQ(Result, *InSequenceRewrite);
 
-    tooling::Replacements Merged = mergeReplacements(First, Second);
+    tooling::Replacements Merged = First.merge(Second);
     auto MergedRewrite = applyAllReplacements(Code, Merged);
     EXPECT_TRUE(static_cast<bool>(MergedRewrite));
     EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
@@ -660,7 +566,7 @@
     auto AfterFirst = applyAllReplacements(Code, First);
     EXPECT_TRUE(static_cast<bool>(AfterFirst));
     auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second);
-    tooling::Replacements Merged = mergeReplacements(First, Second);
+    tooling::Replacements Merged = First.merge(Second);
     auto MergedRewrite = applyAllReplacements(Code, Merged);
     EXPECT_TRUE(static_cast<bool>(MergedRewrite));
     EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
@@ -673,62 +579,82 @@
 
 TEST_F(MergeReplacementsTest, Offsets) {
   mergeAndTestRewrite("aaa", "aabab", "cacabab",
-                      {{"", 2, 0, "b"}, {"", 3, 0, "b"}},
-                      {{"", 0, 0, "c"}, {"", 1, 0, "c"}});
+                      toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
+                      toReplacements({{"", 0, 0, "c"}, {"", 1, 0, "c"}}));
   mergeAndTestRewrite("aaa", "babaa", "babacac",
-                      {{"", 0, 0, "b"}, {"", 1, 0, "b"}},
-                      {{"", 4, 0, "c"}, {"", 5, 0, "c"}});
-  mergeAndTestRewrite("aaaa", "aaa", "aac", {{"", 1, 1, ""}},
-                      {{"", 2, 1, "c"}});
+                      toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "b"}}),
+                      toReplacements({{"", 4, 0, "c"}, {"", 5, 0, "c"}}));
+  mergeAndTestRewrite("aaaa", "aaa", "aac", toReplacements({{"", 1, 1, ""}}),
+                      toReplacements({{"", 2, 1, "c"}}));
 
   mergeAndTestRewrite("aa", "bbabba", "bbabcba",
-                      {{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}, {{"", 4, 0, "c"}});
+                      toReplacements({{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}),
+                      toReplacements({{"", 4, 0, "c"}}));
 }
 
 TEST_F(MergeReplacementsTest, Concatenations) {
   // Basic concatenations. It is important to merge these into a single
   // replacement to ensure the correct order.
-  EXPECT_EQ((Replacements{{"", 0, 0, "ab"}}),
-            mergeReplacements({{"", 0, 0, "a"}}, {{"", 1, 0, "b"}}));
-  EXPECT_EQ((Replacements{{"", 0, 0, "ba"}}),
-            mergeReplacements({{"", 0, 0, "a"}}, {{"", 0, 0, "b"}}));
-  mergeAndTestRewrite("", "a", "ab", {{"", 0, 0, "a"}}, {{"", 1, 0, "b"}});
-  mergeAndTestRewrite("", "a", "ba", {{"", 0, 0, "a"}}, {{"", 0, 0, "b"}});
+  {
+    auto First = toReplacements({{"", 0, 0, "a"}});
+    auto Second = toReplacements({{"", 1, 0, "b"}});
+    EXPECT_EQ(toReplacements({{"", 0, 0, "ab"}}), First.merge(Second));
+  }
+  {
+    auto First = toReplacements({{"", 0, 0, "a"}});
+    auto Second = toReplacements({{"", 0, 0, "b"}});
+    EXPECT_EQ(toReplacements({{"", 0, 0, "ba"}}), First.merge(Second));
+  }
+  mergeAndTestRewrite("", "a", "ab", toReplacements({{"", 0, 0, "a"}}),
+                      toReplacements({{"", 1, 0, "b"}}));
+  mergeAndTestRewrite("", "a", "ba", toReplacements({{"", 0, 0, "a"}}),
+                      toReplacements({{"", 0, 0, "b"}}));
 }
 
 TEST_F(MergeReplacementsTest, NotChangingLengths) {
-  mergeAndTestRewrite("aaaa", "abba", "acca", {{"", 1, 2, "bb"}},
-                      {{"", 1, 2, "cc"}});
-  mergeAndTestRewrite("aaaa", "abba", "abcc", {{"", 1, 2, "bb"}},
-                      {{"", 2, 2, "cc"}});
-  mergeAndTestRewrite("aaaa", "abba", "ccba", {{"", 1, 2, "bb"}},
-                      {{"", 0, 2, "cc"}});
+  mergeAndTestRewrite("aaaa", "abba", "acca",
+                      toReplacements({{"", 1, 2, "bb"}}),
+                      toReplacements({{"", 1, 2, "cc"}}));
+  mergeAndTestRewrite("aaaa", "abba", "abcc",
+                      toReplacements({{"", 1, 2, "bb"}}),
+                      toReplacements({{"", 2, 2, "cc"}}));
+  mergeAndTestRewrite("aaaa", "abba", "ccba",
+                      toReplacements({{"", 1, 2, "bb"}}),
+                      toReplacements({{"", 0, 2, "cc"}}));
   mergeAndTestRewrite("aaaaaa", "abbdda", "abccda",
-                      {{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}, {{"", 2, 2, "cc"}});
+                      toReplacements({{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}),
+                      toReplacements({{"", 2, 2, "cc"}}));
 }
 
 TEST_F(MergeReplacementsTest, OverlappingRanges) {
   mergeAndTestRewrite("aaa", "bbd", "bcbcd",
-                      {{"", 0, 1, "bb"}, {"", 1, 2, "d"}},
-                      {{"", 1, 0, "c"}, {"", 2, 0, "c"}});
+                      toReplacements({{"", 0, 1, "bb"}, {"", 1, 2, "d"}}),
+                      toReplacements({{"", 1, 0, "c"}, {"", 2, 0, "c"}}));
 
-  mergeAndTestRewrite("aaaa", "aabbaa", "acccca", {{"", 2, 0, "bb"}},
-                      {{"", 1, 4, "cccc"}});
+  mergeAndTestRewrite("aaaa", "aabbaa", "acccca",
+                      toReplacements({{"", 2, 0, "bb"}}),
+                      toReplacements({{"", 1, 4, "cccc"}}));
   mergeAndTestRewrite("aaaa", "aababa", "acccca",
-                      {{"", 2, 0, "b"}, {"", 3, 0, "b"}}, {{"", 1, 4, "cccc"}});
-  mergeAndTestRewrite("aaaaaa", "abbbba", "abba", {{"", 1, 4, "bbbb"}},
-                      {{"", 2, 2, ""}});
-  mergeAndTestRewrite("aaaa", "aa", "cc", {{"", 1, 1, ""}, {"", 2, 1, ""}},
-                      {{"", 0, 2, "cc"}});
-  mergeAndTestRewrite("aa", "abbba", "abcbcba", {{"", 1, 0, "bbb"}},
-                      {{"", 2, 0, "c"}, {"", 3, 0, "c"}});
+                      toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
+                      toReplacements({{"", 1, 4, "cccc"}}));
+  mergeAndTestRewrite("aaaaaa", "abbbba", "abba",
+                      toReplacements({{"", 1, 4, "bbbb"}}),
+                      toReplacements({{"", 2, 2, ""}}));
+  mergeAndTestRewrite("aaaa", "aa", "cc",
+                      toReplacements({{"", 1, 1, ""}, {"", 2, 1, ""}}),
+                      toReplacements({{"", 0, 2, "cc"}}));
+  mergeAndTestRewrite("aa", "abbba", "abcbcba",
+                      toReplacements({{"", 1, 0, "bbb"}}),
+                      toReplacements({{"", 2, 0, "c"}, {"", 3, 0, "c"}}));
 
-  mergeAndTestRewrite("aaa", "abbab", "ccdd",
-                      {{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}},
-                      {{"", 0, 2, "cc"}, {"", 2, 3, "dd"}});
-  mergeAndTestRewrite("aa", "babbab", "ccdd",
-                      {{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}},
-                      {{"", 0, 3, "cc"}, {"", 3, 3, "dd"}});
+  mergeAndTestRewrite(
+      "aaa", "abbab", "ccdd",
+      toReplacements({{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}}),
+      toReplacements({{"", 0, 2, "cc"}, {"", 2, 3, "dd"}}));
+  mergeAndTestRewrite(
+      "aa", "babbab", "ccdd",
+      toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}}),
+      toReplacements({{"", 0, 3, "cc"}, {"", 3, 3, "dd"}}));
 }
 
 } // end namespace tooling
diff --git a/clang/unittests/Tooling/ReplacementTest.h b/clang/unittests/Tooling/ReplacementTest.h
new file mode 100644
index 0000000..91530f0
--- /dev/null
+++ b/clang/unittests/Tooling/ReplacementTest.h
@@ -0,0 +1,56 @@
+//===- unittest/Tooling/ReplacementTest.h - Replacements related test------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines utility class and function for Replacement related tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H
+#define LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H
+
+#include "RewriterTestContext.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+
+/// \brief Converts a set of replacements to Replacements class.
+/// \return A Replacements class containing \p Replaces on success; otherwise,
+/// an empty Replacements is returned.
+static tooling::Replacements
+toReplacements(const std::set<tooling::Replacement> &Replaces) {
+  tooling::Replacements Result;
+  for (const auto &R : Replaces) {
+    auto Err = Result.add(R);
+    EXPECT_TRUE(!Err);
+    if (Err) {
+      llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+      return tooling::Replacements();
+    }
+  }
+  return Result;
+}
+
+/// \brief A utility class for replacement related tests.
+class ReplacementTest : public ::testing::Test {
+protected:
+  tooling::Replacement createReplacement(SourceLocation Start, unsigned Length,
+                                         llvm::StringRef ReplacementText) {
+    return tooling::Replacement(Context.Sources, Start, Length,
+                                ReplacementText);
+  }
+
+  RewriterTestContext Context;
+};
+
+} // namespace tooling
+} // namespace clang
+
+#endif // LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H
diff --git a/clang/unittests/Tooling/RewriterTest.cpp b/clang/unittests/Tooling/RewriterTest.cpp
index e8afedb..4305d42 100644
--- a/clang/unittests/Tooling/RewriterTest.cpp
+++ b/clang/unittests/Tooling/RewriterTest.cpp
@@ -39,8 +39,11 @@
 
 TEST(Rewriter, AdjacentInsertAndDelete) {
   Replacements Replaces;
-  Replaces.insert(Replacement("<file>", 6, 6, ""));
-  Replaces.insert(Replacement("<file>", 6, 0, "replaced\n"));
+  auto Err = Replaces.add(Replacement("<file>", 6, 6, ""));
+  EXPECT_TRUE(!Err);
+  Replaces =
+      Replaces.merge(Replacements(Replacement("<file>", 6, 0, "replaced\n")));
+
   auto Rewritten = applyAllReplacements("line1\nline2\nline3\nline4", Replaces);
   EXPECT_TRUE(static_cast<bool>(Rewritten));
   EXPECT_EQ("line1\nreplaced\nline3\nline4", *Rewritten);