Author: revane Date: Thu Aug 8 08:31:14 2013 New Revision: 187979 URL: http://llvm.org/viewvc/llvm-project?rev=187979&view=rev Log: Introduce Replacement deduplication and conflict detection function
Summary: This patch adds tooling::deduplicate() which removes duplicates from and looks for conflicts in a vector of Replacements. Differential Revision: http://llvm-reviews.chandlerc.com/D1314 Modified: cfe/trunk/include/clang/Tooling/Refactoring.h cfe/trunk/lib/Tooling/Refactoring.cpp cfe/trunk/unittests/Tooling/RefactoringTest.cpp Modified: cfe/trunk/include/clang/Tooling/Refactoring.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring.h?rev=187979&r1=187978&r2=187979&view=diff ============================================================================== --- cfe/trunk/include/clang/Tooling/Refactoring.h (original) +++ cfe/trunk/include/clang/Tooling/Refactoring.h Thu Aug 8 08:31:14 2013 @@ -122,6 +122,8 @@ public: bool operator()(const Replacement &R1, const Replacement &R2) const; }; + bool operator==(const Replacement &Other) const; + private: void setFromSourceLocation(SourceManager &Sources, SourceLocation Start, unsigned Length, StringRef ReplacementText); @@ -155,6 +157,14 @@ std::string applyAllReplacements(StringR /// applied. unsigned shiftedCodePosition(const Replacements& Replaces, unsigned Position); +/// \brief Removes duplicate Replacements and reports if Replacements conflict +/// with one another. +/// +/// This function will sort \p Replaces so that conflicts can be reported simply +/// by offset into \p Replaces and number of elements in the conflict. +void deduplicate(std::vector<Replacement> &Replaces, + std::vector<Range> &Conflicts); + /// \brief A tool to run refactorings. /// /// This is a refactoring specific version of \see ClangTool. FrontendActions Modified: cfe/trunk/lib/Tooling/Refactoring.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring.cpp?rev=187979&r1=187978&r2=187979&view=diff ============================================================================== --- cfe/trunk/lib/Tooling/Refactoring.cpp (original) +++ cfe/trunk/lib/Tooling/Refactoring.cpp Thu Aug 8 08:31:14 2013 @@ -90,6 +90,12 @@ bool Replacement::Less::operator()(const return R1.ReplacementText < R2.ReplacementText; } +bool Replacement::operator==(const Replacement &Other) const { + return ReplacementRange.getOffset() == Other.ReplacementRange.getOffset() && + ReplacementRange.getLength() == Other.ReplacementRange.getLength() && + FilePath == Other.FilePath && ReplacementText == Other.ReplacementText; +} + void Replacement::setFromSourceLocation(SourceManager &Sources, SourceLocation Start, unsigned Length, StringRef ReplacementText) { @@ -179,6 +185,44 @@ unsigned shiftedCodePosition(const Repla return NewPosition; } +void deduplicate(std::vector<Replacement> &Replaces, + std::vector<Range> &Conflicts) { + if (Replaces.empty()) + return; + + // Deduplicate + std::sort(Replaces.begin(), Replaces.end(), Replacement::Less()); + std::vector<Replacement>::iterator End = + std::unique(Replaces.begin(), Replaces.end()); + Replaces.erase(End, Replaces.end()); + + // Detect conflicts + Range ConflictRange(Replaces.front().getOffset(), + Replaces.front().getLength()); + unsigned ConflictStart = 0; + unsigned ConflictLength = 1; + for (unsigned i = 1; i < Replaces.size(); ++i) { + Range Current(Replaces[i].getOffset(), Replaces[i].getLength()); + if (ConflictRange.overlapsWith(Current)) { + // Extend conflicted range + ConflictRange = Range(ConflictRange.getOffset(), + Current.getOffset() + Current.getLength() - + ConflictRange.getOffset()); + ++ConflictLength; + } else { + if (ConflictLength > 1) + Conflicts.push_back(Range(ConflictStart, ConflictLength)); + ConflictRange = Current; + ConflictStart = i; + ConflictLength = 1; + } + } + + if (ConflictLength > 1) + Conflicts.push_back(Range(ConflictStart, ConflictLength)); +} + + RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths) : ClangTool(Compilations, SourcePaths) {} Modified: cfe/trunk/unittests/Tooling/RefactoringTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RefactoringTest.cpp?rev=187979&r1=187978&r2=187979&view=diff ============================================================================== --- cfe/trunk/unittests/Tooling/RefactoringTest.cpp (original) +++ cfe/trunk/unittests/Tooling/RefactoringTest.cpp Thu Aug 8 08:31:14 2013 @@ -347,5 +347,76 @@ TEST(Range, contains) { EXPECT_FALSE(Range(0, 10).contains(Range(0, 11))); } +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("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", 51, 3, " bar ")); + + std::vector<Range> Conflicts; // Ignored for this test + deduplicate(Input, Conflicts); + + ASSERT_TRUE(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", 5, 5, " moo ")); // 2 + Input.push_back(Replacement("fileA", 15, 5, " golf ")); // 4 + Input.push_back(Replacement("fileA", 16, 5, " bag ")); // 5 + Input.push_back(Replacement("fileA", 10, 3, " club ")); // 6 + + std::vector<Range> Conflicts; + deduplicate(Input, Conflicts); + + // No duplicates + ASSERT_EQ(6u, Input.size()); + ASSERT_EQ(2u, Conflicts.size()); + ASSERT_EQ(1u, Conflicts[0].getOffset()); + ASSERT_EQ(2u, Conflicts[0].getLength()); + ASSERT_EQ(4u, Conflicts[1].getOffset()); + ASSERT_EQ(2u, Conflicts[1].getLength()); + } +} + } // end namespace tooling } // end namespace clang _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
