diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:18:08 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:18:08 +0000 | 
| commit | bab175ec4b075c8076ba14c762900392533f6ee4 (patch) | |
| tree | 01f4f29419a2cb10abe13c1e63cd2a66068b0137 /unittests/Tooling/RefactoringTest.cpp | |
| parent | 8b7a8012d223fac5d17d16a66bb39168a9a1dfc0 (diff) | |
Notes
Diffstat (limited to 'unittests/Tooling/RefactoringTest.cpp')
| -rw-r--r-- | unittests/Tooling/RefactoringTest.cpp | 855 | 
1 files changed, 606 insertions, 249 deletions
diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp index df96bb159dea7..c29f8d7d71230 100644 --- a/unittests/Tooling/RefactoringTest.cpp +++ b/unittests/Tooling/RefactoringTest.cpp @@ -7,6 +7,7 @@  //  //===----------------------------------------------------------------------===// +#include "ReplacementTest.h"  #include "RewriterTestContext.h"  #include "clang/AST/ASTConsumer.h"  #include "clang/AST/ASTContext.h" @@ -18,6 +19,7 @@  #include "clang/Basic/FileManager.h"  #include "clang/Basic/LangOptions.h"  #include "clang/Basic/SourceManager.h" +#include "clang/Basic/VirtualFileSystem.h"  #include "clang/Format/Format.h"  #include "clang/Frontend/CompilerInstance.h"  #include "clang/Frontend/FrontendAction.h" @@ -26,22 +28,11 @@  #include "clang/Tooling/Refactoring.h"  #include "clang/Tooling/Tooling.h"  #include "llvm/ADT/SmallString.h" -#include "llvm/Support/Path.h"  #include "gtest/gtest.h"  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); @@ -109,62 +100,412 @@ TEST_F(ReplacementTest, ReturnsInvalidPath) {    EXPECT_TRUE(Replace2.getFilePath().empty());  } +// Checks that an llvm::Error instance contains a ReplacementError with expected +// error code, expected new replacement, and expected existing replacement. +static bool checkReplacementError( +    llvm::Error&& Error, replacement_error ExpectedErr, +    llvm::Optional<Replacement> ExpectedExisting, +    llvm::Optional<Replacement> ExpectedNew) { +  if (!Error) { +    llvm::errs() << "Error is a success."; +    return false; +  } +  std::string ErrorMessage; +  llvm::raw_string_ostream OS(ErrorMessage); +  llvm::handleAllErrors(std::move(Error), [&](const ReplacementError &RE) { +    llvm::errs() << "Handling error...\n"; +    if (ExpectedErr != RE.get()) +      OS << "Unexpected error code: " << int(RE.get()) << "\n"; +    if (ExpectedExisting != RE.getExistingReplacement()) { +      OS << "Expected Existing != Actual Existing.\n"; +      if (ExpectedExisting.hasValue()) +        OS << "Expected existing replacement: " << ExpectedExisting->toString() +           << "\n"; +      if (RE.getExistingReplacement().hasValue()) +        OS << "Actual existing replacement: " +           << RE.getExistingReplacement()->toString() << "\n"; +    } +    if (ExpectedNew != RE.getNewReplacement()) { +      OS << "Expected New != Actual New.\n"; +      if (ExpectedNew.hasValue()) +        OS << "Expected new replacement: " << ExpectedNew->toString() << "\n"; +      if (RE.getNewReplacement().hasValue()) +        OS << "Actual new replacement: " << RE.getNewReplacement()->toString() +           << "\n"; +    } +  }); +  OS.flush(); +  if (ErrorMessage.empty()) return true; +  llvm::errs() << ErrorMessage; +  return false; +} + +TEST_F(ReplacementTest, FailAddReplacements) { +  Replacements Replaces; +  Replacement Deletion("x.cc", 0, 10, "3"); +  auto Err = Replaces.add(Deletion); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Replacement OverlappingReplacement("x.cc", 0, 2, "a"); +  Err = Replaces.add(OverlappingReplacement); +  EXPECT_TRUE(checkReplacementError(std::move(Err), +                                    replacement_error::overlap_conflict, +                                    Deletion, OverlappingReplacement)); + +  Replacement ContainedReplacement("x.cc", 2, 2, "a"); +  Err = Replaces.add(Replacement(ContainedReplacement)); +  EXPECT_TRUE(checkReplacementError(std::move(Err), +                                    replacement_error::overlap_conflict, +                                    Deletion, ContainedReplacement)); + +  Replacement WrongPathReplacement("y.cc", 20, 2, ""); +  Err = Replaces.add(WrongPathReplacement); +  EXPECT_TRUE(checkReplacementError(std::move(Err), +                                    replacement_error::wrong_file_path, +                                    Deletion, WrongPathReplacement)); + +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(Deletion, *Replaces.begin()); +} + +TEST_F(ReplacementTest, DeletionInReplacements) { +  Replacements Replaces; +  Replacement R("x.cc", 0, 10, "3"); +  auto Err = Replaces.add(R); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 0, 2, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 2, 2, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(R, *Replaces.begin()); +} + +TEST_F(ReplacementTest, OverlappingReplacements) { +  Replacements Replaces; +  auto Err = Replaces.add(Replacement("x.cc", 0, 3, "345")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 2, 3, "543")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(Replacement("x.cc", 0, 5, "34543"), *Replaces.begin()); + +  Err = Replaces.add(Replacement("x.cc", 2, 1, "5")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(Replacement("x.cc", 0, 5, "34543"), *Replaces.begin()); +} + +TEST_F(ReplacementTest, AddAdjacentInsertionAndReplacement) { +  Replacements Replaces; +  // Test adding an insertion at the offset of an existing replacement. +  auto Err = Replaces.add(Replacement("x.cc", 10, 3, "replace")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "insert")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(Replaces.size(), 2u); + +  Replaces.clear(); +  // Test overlap with an existing insertion. +  Err = Replaces.add(Replacement("x.cc", 10, 0, "insert")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 3, "replace")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(Replaces.size(), 2u); +} + +TEST_F(ReplacementTest, MergeNewDeletions) { +  Replacements Replaces; +  Replacement ContainingReplacement("x.cc", 0, 10, ""); +  auto Err = Replaces.add(ContainingReplacement); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Err = Replaces.add(Replacement("x.cc", 5, 3, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Err = Replaces.add(Replacement("x.cc", 0, 10, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Err = Replaces.add(Replacement("x.cc", 5, 5, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(*Replaces.begin(), ContainingReplacement); +} + +TEST_F(ReplacementTest, MergeOverlappingButNotAdjacentReplacement) { +  Replacements Replaces; +  auto Err = Replaces.add(Replacement("x.cc", 0, 2, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Err = Replaces.add(Replacement("x.cc", 5, 5, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Replacement After = Replacement("x.cc", 10, 5, ""); +  Err = Replaces.add(After); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Replacement ContainingReplacement("x.cc", 0, 10, ""); +  Err = Replaces.add(ContainingReplacement); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  EXPECT_EQ(2u, Replaces.size()); +  EXPECT_EQ(*Replaces.begin(), ContainingReplacement); +  EXPECT_EQ(*(++Replaces.begin()), After); +} + +TEST_F(ReplacementTest, InsertionBeforeMergedDeletions) { +  Replacements Replaces; + +  Replacement Insertion("x.cc", 0, 0, "123"); +  auto Err = Replaces.add(Insertion); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Err = Replaces.add(Replacement("x.cc", 5, 5, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Replacement Deletion("x.cc", 0, 10, ""); +  Err = Replaces.add(Deletion); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  EXPECT_EQ(2u, Replaces.size()); +  EXPECT_EQ(*Replaces.begin(), Insertion); +  EXPECT_EQ(*(++Replaces.begin()), Deletion); +} + +TEST_F(ReplacementTest, MergeOverlappingDeletions) { +  Replacements Replaces; +  auto Err = Replaces.add(Replacement("x.cc", 0, 2, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Err = Replaces.add(Replacement("x.cc", 0, 5, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(Replacement("x.cc", 0, 5, ""), *Replaces.begin()); + +  Err = Replaces.add(Replacement("x.cc", 1, 5, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(Replacement("x.cc", 0, 6, ""), *Replaces.begin()); +} + +TEST_F(ReplacementTest, FailedMergeExistingDeletions) { +  Replacements Replaces; +  Replacement First("x.cc", 0, 2, ""); +  auto Err = Replaces.add(First); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Replacement Second("x.cc", 5, 5, ""); +  Err = Replaces.add(Second); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  Err = Replaces.add(Replacement("x.cc", 1, 10, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(Replacement("x.cc", 0, 11, ""), *Replaces.begin()); +} + +TEST_F(ReplacementTest, FailAddRegression) { +  Replacements Replaces; +  // Create two replacements, where the second one is an insertion of the empty +  // string exactly at the end of the first one. +  auto Err = Replaces.add(Replacement("x.cc", 0, 10, "1")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); + +  // Make sure we find the overlap with the first entry when inserting a +  // replacement that ends exactly at the seam of the existing replacements. +  Replacement OverlappingReplacement("x.cc", 5, 5, "fail"); +  Err = Replaces.add(OverlappingReplacement); +  EXPECT_TRUE(checkReplacementError(std::move(Err), +                                    replacement_error::overlap_conflict, +                                    *Replaces.begin(), OverlappingReplacement)); + +  Err = Replaces.add(Replacement("x.cc", 10, 0, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +} + +TEST_F(ReplacementTest, InsertAtOffsetOfReplacement) { +  Replacements Replaces; +  auto Err = Replaces.add(Replacement("x.cc", 10, 2, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(Replaces.size(), 2u); + +  Replaces.clear(); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 2, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(Replaces.size(), 2u); +} + +TEST_F(ReplacementTest, AddInsertAtOtherInsertWhenOderIndependent) { +  Replacements Replaces; +  auto Err = Replaces.add(Replacement("x.cc", 10, 0, "a")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Replacement ConflictInsertion("x.cc", 10, 0, "b"); +  Err = Replaces.add(ConflictInsertion); +  EXPECT_TRUE(checkReplacementError(std::move(Err), +                                    replacement_error::insert_conflict, +                                    *Replaces.begin(), ConflictInsertion)); + +  Replaces.clear(); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "a")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "aa")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(1u, Replaces.size()); +  EXPECT_EQ(Replacement("x.cc", 10, 0, "aaa"), *Replaces.begin()); + +  Replaces.clear(); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 3, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  EXPECT_EQ(2u, Replaces.size()); +  EXPECT_EQ(Replacement("x.cc", 10, 0, ""), *Replaces.begin()); +  EXPECT_EQ(Replacement("x.cc", 10, 3, ""), *std::next(Replaces.begin())); +} + +TEST_F(ReplacementTest, InsertBetweenAdjacentReplacements) { +  Replacements Replaces; +  auto Err = Replaces.add(Replacement("x.cc", 10, 5, "a")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 8, 2, "a")); +  EXPECT_TRUE(!Err); +  llvm::consumeError(std::move(Err)); +  Err = Replaces.add(Replacement("x.cc", 10, 0, "b")); +  EXPECT_TRUE(!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")); +  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));  } -// 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) { +// Verifies that replacement/deletion is applied before insertion at the same +// offset. +TEST_F(ReplacementTest, InsertAndDelete) {    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), 6, ""), +       Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 0, +                   "other\n")});    EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); -  EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID)); +  EXPECT_EQ("line1\nother\nline3\nline4", Context.getRewrittenText(ID)); +} + +TEST_F(ReplacementTest, AdjacentReplacements) { +  FileID ID = Context.createInMemoryFile("input.cpp", +                                         "ab"); +  Replacements Replaces = toReplacements( +      {Replacement(Context.Sources, Context.getLocation(ID, 1, 1), 1, "x"), +       Replacement(Context.Sources, Context.getLocation(ID, 1, 2), 1, "y")}); +  EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); +  EXPECT_EQ("xy", Context.getRewrittenText(ID));  } -TEST_F(ReplacementTest, SkipsDuplicateReplacements) { +TEST_F(ReplacementTest, AddDuplicateReplacements) {    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(!Err); +  llvm::consumeError(std::move(Err)); + +  Err = Replaces.add(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), +                                 5, "replaced")); +  EXPECT_TRUE(!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, FailOrderDependentReplacements) { +  FileID ID = Context.createInMemoryFile("input.cpp", +                                         "line1\nline2\nline3\nline4"); +  auto Replaces = toReplacements({Replacement( +      Context.Sources, Context.getLocation(ID, 2, 1), 5, "other")}); + +  Replacement ConflictReplacement(Context.Sources, +                                  Context.getLocation(ID, 2, 1), 5, "rehto"); +  auto Err = Replaces.add(ConflictReplacement); +  EXPECT_TRUE(checkReplacementError(std::move(Err), +                                    replacement_error::overlap_conflict, +                                    *Replaces.begin(), ConflictReplacement)); + +  EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); +  EXPECT_EQ("line1\nother\nline3\nline4", Context.getRewrittenText(ID)); +} + +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) { @@ -180,76 +521,66 @@ TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {    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 { @@ -305,9 +636,8 @@ public:  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", @@ -455,12 +785,11 @@ TEST(Range, contains) {  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); @@ -469,25 +798,43 @@ TEST(Range, CalculateRangesOfReplacements) {    EXPECT_TRUE(Ranges[1].getLength() == 22);  } +TEST(Range, CalculateRangesOfInsertionAroundReplacement) { +  Replacements Replaces = toReplacements( +      {Replacement("foo", 0, 2, ""), Replacement("foo", 0, 0, "ba")}); + +  std::vector<Range> Ranges = Replaces.getAffectedRanges(); + +  EXPECT_EQ(1ul, Ranges.size()); +  EXPECT_EQ(0u, Ranges[0].getOffset()); +  EXPECT_EQ(2u, Ranges[0].getLength()); +} + +TEST(Range, RangesAfterEmptyReplacements) { +  std::vector<Range> Ranges = {Range(5, 6), Range(10, 5)}; +  Replacements Replaces; +  std::vector<Range> Expected = {Range(5, 10)}; +  EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); +} +  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)); @@ -495,9 +842,9 @@ TEST(Range, NotAffectedByReplacements) {  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)); @@ -506,9 +853,9 @@ TEST(Range, RangesWithNonOverlappingReplacements) {  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)); @@ -516,122 +863,52 @@ TEST(Range, RangesWithOverlappingReplacements) {  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 { @@ -647,7 +924,7 @@ protected:      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); @@ -661,7 +938,7 @@ protected:      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); @@ -674,62 +951,142 @@ protected:  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"}}); - -  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"}}); +                      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", +      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"}})); +} + +TEST(DeduplicateByFileTest, PathsWithDots) { +  std::map<std::string, Replacements> FileToReplaces; +  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( +      new vfs::InMemoryFileSystem()); +  FileManager FileMgr(FileSystemOptions(), VFS); +#if !defined(LLVM_ON_WIN32) +  StringRef Path1 = "a/b/.././c.h"; +  StringRef Path2 = "a/c.h"; +#else +  StringRef Path1 = "a\\b\\..\\.\\c.h"; +  StringRef Path2 = "a\\c.h"; +#endif +  EXPECT_TRUE(VFS->addFile(Path1, 0, llvm::MemoryBuffer::getMemBuffer(""))); +  EXPECT_TRUE(VFS->addFile(Path2, 0, llvm::MemoryBuffer::getMemBuffer(""))); +  FileToReplaces[Path1] = Replacements(); +  FileToReplaces[Path2] = Replacements(); +  FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces); +  EXPECT_EQ(1u, FileToReplaces.size()); +  EXPECT_EQ(Path1, FileToReplaces.begin()->first); +} + +TEST(DeduplicateByFileTest, PathWithDotSlash) { +  std::map<std::string, Replacements> FileToReplaces; +  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( +      new vfs::InMemoryFileSystem()); +  FileManager FileMgr(FileSystemOptions(), VFS); +#if !defined(LLVM_ON_WIN32) +  StringRef Path1 = "./a/b/c.h"; +  StringRef Path2 = "a/b/c.h"; +#else +  StringRef Path1 = ".\\a\\b\\c.h"; +  StringRef Path2 = "a\\b\\c.h"; +#endif +  EXPECT_TRUE(VFS->addFile(Path1, 0, llvm::MemoryBuffer::getMemBuffer(""))); +  EXPECT_TRUE(VFS->addFile(Path2, 0, llvm::MemoryBuffer::getMemBuffer(""))); +  FileToReplaces[Path1] = Replacements(); +  FileToReplaces[Path2] = Replacements(); +  FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces); +  EXPECT_EQ(1u, FileToReplaces.size()); +  EXPECT_EQ(Path1, FileToReplaces.begin()->first); +} + +TEST(DeduplicateByFileTest, NonExistingFilePath) { +  std::map<std::string, Replacements> FileToReplaces; +  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( +      new vfs::InMemoryFileSystem()); +  FileManager FileMgr(FileSystemOptions(), VFS); +#if !defined(LLVM_ON_WIN32) +  StringRef Path1 = "./a/b/c.h"; +  StringRef Path2 = "a/b/c.h"; +#else +  StringRef Path1 = ".\\a\\b\\c.h"; +  StringRef Path2 = "a\\b\\c.h"; +#endif +  FileToReplaces[Path1] = Replacements(); +  FileToReplaces[Path2] = Replacements(); +  FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces); +  EXPECT_TRUE(FileToReplaces.empty());  }  } // end namespace tooling  | 
