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 llvm-svn: 187979
This commit is contained in:
parent
233455cba8
commit
938f68816a
|
@ -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(StringRef Code, const Replacements &Replaces);
|
|||
/// 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
|
||||
|
|
|
@ -90,6 +90,12 @@ bool Replacement::Less::operator()(const Replacement &R1,
|
|||
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 Replacements &Replaces, unsigned Position) {
|
|||
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) {}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue