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



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@187979 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Tooling/Refactoring.cpp b/lib/Tooling/Refactoring.cpp
index a61bf9a..8599f97 100644
--- a/lib/Tooling/Refactoring.cpp
+++ b/lib/Tooling/Refactoring.cpp
@@ -90,6 +90,12 @@
   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 @@
   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) {}