r187979 - Introduce Replacement deduplication and conflict detection function

Edwin Vane edwin.vane at intel.com
Thu Aug 8 06:31:14 PDT 2013


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





More information about the cfe-commits mailing list