Index: include/clang/Tooling/Core/Replacement.h =================================================================== --- include/clang/Tooling/Core/Replacement.h +++ include/clang/Tooling/Core/Replacement.h @@ -175,6 +175,12 @@ /// refers to code after applying the current replacements. Replacements merge(const Replacements &Replaces) const; + /// \brief Adds `R` to the current set of replacements. If this fails, + /// calculates the range of R in the code after applying all existing + /// replacements and sets the the range of `R` to be the new range. `R` with + /// new range is then merged into the current replacements. + void addOrMerge(const Replacement &R); + // Returns the affected ranges in the changed code. std::vector getAffectedRanges() const; @@ -204,6 +210,8 @@ Replacements mergeReplacements(const ReplacementsImpl &Second) const; + Replacement getReplacementInChangedCode(const Replacement &R); + ReplacementsImpl Replaces; }; Index: lib/Tooling/Core/Replacement.cpp =================================================================== --- lib/Tooling/Core/Replacement.cpp +++ lib/Tooling/Core/Replacement.cpp @@ -190,6 +190,27 @@ llvm::inconvertibleErrorCode()); } +// Returns `R` with new range that refers to code after `Replaces` being +// applied. +Replacement +Replacements::getReplacementInChangedCode(const Replacement &R) { + unsigned NewStart = getShiftedCodePosition(R.getOffset()); + unsigned NewEnd = getShiftedCodePosition(R.getOffset() + R.getLength()); + return Replacement(R.getFilePath(), NewStart, NewEnd - NewStart, + R.getReplacementText()); +} + +void Replacements::addOrMerge(const Replacement &R) { + auto Err = add(R); + if (!Err) + return; + llvm::consumeError(std::move(Err)); + // Failed to add R. Use `merge`. + Replacements NewReplaces = + merge(Replacements(getReplacementInChangedCode(R))); + Replaces = NewReplaces.Replaces; +} + namespace { // Represents a merged replacement, i.e. a replacement consisting of multiple Index: unittests/Tooling/RefactoringTest.cpp =================================================================== --- unittests/Tooling/RefactoringTest.cpp +++ unittests/Tooling/RefactoringTest.cpp @@ -177,6 +177,57 @@ llvm::consumeError(std::move(Err)); } +TEST_F(ReplacementTest, AddOrMergeTwoInsertions) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 10, 0, "a")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Replacement R("x.cc", 10, 0, "b"); + Err = Replaces.add(R); + EXPECT_TRUE((bool)Err); + llvm::consumeError(std::move(Err)); + + Replaces.addOrMerge(R); + + Replacements Expected(Replacement("x.cc", 10, 0, "ab")); + EXPECT_EQ(Expected, Replaces); +} + +TEST_F(ReplacementTest, AddOrMergeInsertionAndReplace) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 10, 0, "aaa")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Replacement R("x.cc", 10, 2, "bbb"); + Err = Replaces.add(R); + EXPECT_TRUE((bool)Err); + llvm::consumeError(std::move(Err)); + + Replaces.addOrMerge(R); + + Replacements Expected(Replacement("x.cc", 10, 2, "aaabbb")); + EXPECT_EQ(Expected, Replaces); +} + +TEST_F(ReplacementTest, AddOrMergeReplaceAndInsertion) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 10, 2, "bbb")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Replacement R("x.cc", 10, 0, "aaa"); + Err = Replaces.add(R); + EXPECT_TRUE((bool)Err); + llvm::consumeError(std::move(Err)); + + Replaces.addOrMerge(R); + + Replacements Expected(Replacement("x.cc", 10, 2, "aaabbb")); + EXPECT_EQ(Expected, Replaces); +} + TEST_F(ReplacementTest, CanApplyReplacements) { FileID ID = Context.createInMemoryFile("input.cpp", "line1\nline2\nline3\nline4");