174 changes: 1 addition & 173 deletions clang/include/clang/Tooling/Refactoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,180 +19,16 @@
#ifndef LLVM_CLANG_TOOLING_REFACTORING_H
#define LLVM_CLANG_TOOLING_REFACTORING_H

#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/Core/Replacement.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include <set>
#include <string>

namespace clang {

class Rewriter;
class SourceLocation;

namespace tooling {

/// \brief A source range independent of the \c SourceManager.
class Range {
public:
Range() : Offset(0), Length(0) {}
Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {}

/// \brief Accessors.
/// @{
unsigned getOffset() const { return Offset; }
unsigned getLength() const { return Length; }
/// @}

/// \name Range Predicates
/// @{
/// \brief Whether this range overlaps with \p RHS or not.
bool overlapsWith(Range RHS) const {
return Offset + Length > RHS.Offset && Offset < RHS.Offset + RHS.Length;
}

/// \brief Whether this range contains \p RHS or not.
bool contains(Range RHS) const {
return RHS.Offset >= Offset &&
(RHS.Offset + RHS.Length) <= (Offset + Length);
}
/// @}

private:
unsigned Offset;
unsigned Length;
};

/// \brief A text replacement.
///
/// Represents a SourceManager independent replacement of a range of text in a
/// specific file.
class Replacement {
public:
/// \brief Creates an invalid (not applicable) replacement.
Replacement();

/// \brief Creates a replacement of the range [Offset, Offset+Length) in
/// FilePath with ReplacementText.
///
/// \param FilePath A source file accessible via a SourceManager.
/// \param Offset The byte offset of the start of the range in the file.
/// \param Length The length of the range in bytes.
Replacement(StringRef FilePath, unsigned Offset,
unsigned Length, StringRef ReplacementText);

/// \brief Creates a Replacement of the range [Start, Start+Length) with
/// ReplacementText.
Replacement(const SourceManager &Sources, SourceLocation Start, unsigned Length,
StringRef ReplacementText);

/// \brief Creates a Replacement of the given range with ReplacementText.
Replacement(const SourceManager &Sources, const CharSourceRange &Range,
StringRef ReplacementText);

/// \brief Creates a Replacement of the node with ReplacementText.
template <typename Node>
Replacement(const SourceManager &Sources, const Node &NodeToReplace,
StringRef ReplacementText);

/// \brief Returns whether this replacement can be applied to a file.
///
/// Only replacements that are in a valid file can be applied.
bool isApplicable() const;

/// \brief Accessors.
/// @{
StringRef getFilePath() const { return FilePath; }
unsigned getOffset() const { return ReplacementRange.getOffset(); }
unsigned getLength() const { return ReplacementRange.getLength(); }
StringRef getReplacementText() const { return ReplacementText; }
/// @}

/// \brief Applies the replacement on the Rewriter.
bool apply(Rewriter &Rewrite) const;

/// \brief Returns a human readable string representation.
std::string toString() const;

private:
void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start,
unsigned Length, StringRef ReplacementText);
void setFromSourceRange(const SourceManager &Sources,
const CharSourceRange &Range,
StringRef ReplacementText);

std::string FilePath;
Range ReplacementRange;
std::string ReplacementText;
};

/// \brief Less-than operator between two Replacements.
bool operator<(const Replacement &LHS, const Replacement &RHS);

/// \brief Equal-to operator between two Replacements.
bool operator==(const Replacement &LHS, const Replacement &RHS);

/// \brief A set of Replacements.
/// FIXME: Change to a vector and deduplicate in the RefactoringTool.
typedef std::set<Replacement> Replacements;

/// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
///
/// Replacement applications happen independently of the success of
/// other applications.
///
/// \returns true if all replacements apply. false otherwise.
bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite);

/// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
///
/// Replacement applications happen independently of the success of
/// other applications.
///
/// \returns true if all replacements apply. false otherwise.
bool applyAllReplacements(const std::vector<Replacement> &Replaces,
Rewriter &Rewrite);

/// \brief Applies all replacements in \p Replaces to \p Code.
///
/// This completely ignores the path stored in each replacement. If one or more
/// replacements cannot be applied, this returns an empty \c string.
std::string applyAllReplacements(StringRef Code, const Replacements &Replaces);

/// \brief Calculates how a code \p Position is shifted when \p Replaces are
/// applied.
unsigned shiftedCodePosition(const Replacements& Replaces, unsigned Position);

/// \brief Calculates how a code \p Position is shifted when \p Replaces are
/// applied.
///
/// \pre Replaces[i].getOffset() <= Replaces[i+1].getOffset().
unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
unsigned Position);

/// \brief Removes duplicate Replacements and reports if Replacements conflict
/// with one another. All Replacements are assumed to be in the same file.
///
/// \post Replaces[i].getOffset() <= Replaces[i+1].getOffset().
///
/// This function sorts \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 Collection of Replacements generated from a single translation unit.
struct TranslationUnitReplacements {
/// Name of the main source for the translation unit.
std::string MainSourceFile;

/// A freeform chunk of text to describe the context of the replacements.
/// Will be printed, for example, when detecting conflicts during replacement
/// deduplication.
std::string Context;

std::vector<Replacement> Replacements;
};

/// \brief A tool to run refactorings.
///
/// This is a refactoring specific version of \see ClangTool. FrontendActions
Expand Down Expand Up @@ -230,14 +66,6 @@ class RefactoringTool : public ClangTool {
Replacements Replace;
};

template <typename Node>
Replacement::Replacement(const SourceManager &Sources,
const Node &NodeToReplace, StringRef ReplacementText) {
const CharSourceRange Range =
CharSourceRange::getTokenRange(NodeToReplace->getSourceRange());
setFromSourceRange(Sources, Range, ReplacementText);
}

} // end namespace tooling
} // end namespace clang

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ add_clang_library(clangFormat
LINK_LIBS
clangBasic
clangLex
clangTooling
clangToolingCore
)
3 changes: 3 additions & 0 deletions clang/lib/Tooling/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
set(LLVM_LINK_COMPONENTS support)

add_subdirectory(Core)

add_clang_library(clangTooling
ArgumentsAdjusters.cpp
CommonOptionsParser.cpp
Expand All @@ -18,4 +20,5 @@ add_clang_library(clangTooling
clangFrontend
clangLex
clangRewrite
clangToolingCore
)
10 changes: 10 additions & 0 deletions clang/lib/Tooling/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set(LLVM_LINK_COMPONENTS support)

add_clang_library(clangToolingCore
Replacement.cpp

LINK_LIBS
clangBasic
clangDriver
clangRewrite
)
13 changes: 13 additions & 0 deletions clang/lib/Tooling/Core/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
##===- clang/lib/Tooling/Core/Makefile ---------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##

CLANG_LEVEL := ../../..
LIBRARYNAME := clangToolingCore

include $(CLANG_LEVEL)/Makefile
289 changes: 289 additions & 0 deletions clang/lib/Tooling/Core/Replacement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
//===--- Replacement.cpp - Framework for clang refactoring tools ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implements classes to support/store refactorings.
//
//===----------------------------------------------------------------------===//

#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_os_ostream.h"

namespace clang {
namespace tooling {

static const char * const InvalidLocation = "";

Replacement::Replacement()
: FilePath(InvalidLocation) {}

Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
StringRef ReplacementText)
: FilePath(FilePath), ReplacementRange(Offset, Length),
ReplacementText(ReplacementText) {}

Replacement::Replacement(const SourceManager &Sources, SourceLocation Start,
unsigned Length, StringRef ReplacementText) {
setFromSourceLocation(Sources, Start, Length, ReplacementText);
}

Replacement::Replacement(const SourceManager &Sources,
const CharSourceRange &Range,
StringRef ReplacementText) {
setFromSourceRange(Sources, Range, ReplacementText);
}

bool Replacement::isApplicable() const {
return FilePath != InvalidLocation;
}

bool Replacement::apply(Rewriter &Rewrite) const {
SourceManager &SM = Rewrite.getSourceMgr();
const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
if (!Entry)
return false;
FileID ID;
// FIXME: Use SM.translateFile directly.
SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1);
ID = Location.isValid() ?
SM.getFileID(Location) :
SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
// FIXME: We cannot check whether Offset + Length is in the file, as
// the remapping API is not public in the RewriteBuffer.
const SourceLocation Start =
SM.getLocForStartOfFile(ID).
getLocWithOffset(ReplacementRange.getOffset());
// ReplaceText returns false on success.
// ReplaceText only fails if the source location is not a file location, in
// which case we already returned false earlier.
bool RewriteSucceeded = !Rewrite.ReplaceText(
Start, ReplacementRange.getLength(), ReplacementText);
assert(RewriteSucceeded);
return RewriteSucceeded;
}

std::string Replacement::toString() const {
std::string result;
llvm::raw_string_ostream stream(result);
stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
<< ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
return result;
}

bool operator<(const Replacement &LHS, const Replacement &RHS) {
if (LHS.getOffset() != RHS.getOffset())
return LHS.getOffset() < RHS.getOffset();
if (LHS.getLength() != RHS.getLength())
return LHS.getLength() < RHS.getLength();
if (LHS.getFilePath() != RHS.getFilePath())
return LHS.getFilePath() < RHS.getFilePath();
return LHS.getReplacementText() < RHS.getReplacementText();
}

bool operator==(const Replacement &LHS, const Replacement &RHS) {
return LHS.getOffset() == RHS.getOffset() &&
LHS.getLength() == RHS.getLength() &&
LHS.getFilePath() == RHS.getFilePath() &&
LHS.getReplacementText() == RHS.getReplacementText();
}

void Replacement::setFromSourceLocation(const SourceManager &Sources,
SourceLocation Start, unsigned Length,
StringRef ReplacementText) {
const std::pair<FileID, unsigned> DecomposedLocation =
Sources.getDecomposedLoc(Start);
const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
if (Entry) {
// Make FilePath absolute so replacements can be applied correctly when
// relative paths for files are used.
llvm::SmallString<256> FilePath(Entry->getName());
std::error_code EC = llvm::sys::fs::make_absolute(FilePath);
this->FilePath = EC ? FilePath.c_str() : Entry->getName();
} else {
this->FilePath = InvalidLocation;
}
this->ReplacementRange = Range(DecomposedLocation.second, Length);
this->ReplacementText = ReplacementText;
}

// FIXME: This should go into the Lexer, but we need to figure out how
// to handle ranges for refactoring in general first - there is no obvious
// good way how to integrate this into the Lexer yet.
static int getRangeSize(const SourceManager &Sources,
const CharSourceRange &Range) {
SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
if (Start.first != End.first) return -1;
if (Range.isTokenRange())
End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources,
LangOptions());
return End.second - Start.second;
}

void Replacement::setFromSourceRange(const SourceManager &Sources,
const CharSourceRange &Range,
StringRef ReplacementText) {
setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
getRangeSize(Sources, Range), ReplacementText);
}

unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
unsigned NewPosition = Position;
for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E;
++I) {
if (I->getOffset() >= Position)
break;
if (I->getOffset() + I->getLength() > Position)
NewPosition += I->getOffset() + I->getLength() - Position;
NewPosition += I->getReplacementText().size() - I->getLength();
}
return NewPosition;
}

// FIXME: Remove this function when Replacements is implemented as std::vector
// instead of std::set.
unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
unsigned Position) {
unsigned NewPosition = Position;
for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
E = Replaces.end();
I != E; ++I) {
if (I->getOffset() >= Position)
break;
if (I->getOffset() + I->getLength() > Position)
NewPosition += I->getOffset() + I->getLength() - Position;
NewPosition += I->getReplacementText().size() - I->getLength();
}
return NewPosition;
}

void deduplicate(std::vector<Replacement> &Replaces,
std::vector<Range> &Conflicts) {
if (Replaces.empty())
return;

auto LessNoPath = [](const Replacement &LHS, const Replacement &RHS) {
if (LHS.getOffset() != RHS.getOffset())
return LHS.getOffset() < RHS.getOffset();
if (LHS.getLength() != RHS.getLength())
return LHS.getLength() < RHS.getLength();
return LHS.getReplacementText() < RHS.getReplacementText();
};

auto EqualNoPath = [](const Replacement &LHS, const Replacement &RHS) {
return LHS.getOffset() == RHS.getOffset() &&
LHS.getLength() == RHS.getLength() &&
LHS.getReplacementText() == RHS.getReplacementText();
};

// Deduplicate. We don't want to deduplicate based on the path as we assume
// that all replacements refer to the same file (or are symlinks).
std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
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(),
std::max(ConflictRange.getLength(),
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));
}

bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
bool Result = true;
for (Replacements::const_iterator I = Replaces.begin(),
E = Replaces.end();
I != E; ++I) {
if (I->isApplicable()) {
Result = I->apply(Rewrite) && Result;
} else {
Result = false;
}
}
return Result;
}

// FIXME: Remove this function when Replacements is implemented as std::vector
// instead of std::set.
bool applyAllReplacements(const std::vector<Replacement> &Replaces,
Rewriter &Rewrite) {
bool Result = true;
for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
E = Replaces.end();
I != E; ++I) {
if (I->isApplicable()) {
Result = I->apply(Rewrite) && Result;
} else {
Result = false;
}
}
return Result;
}

std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) {
FileManager Files((FileSystemOptions()));
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
new DiagnosticOptions);
SourceManager SourceMgr(Diagnostics, Files);
Rewriter Rewrite(SourceMgr, LangOptions());
std::unique_ptr<llvm::MemoryBuffer> Buf =
llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>");
const clang::FileEntry *Entry =
Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0);
SourceMgr.overrideFileContents(Entry, std::move(Buf));
FileID ID =
SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User);
for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end();
I != E; ++I) {
Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
I->getReplacementText());
if (!Replace.apply(Rewrite))
return "";
}
std::string Result;
llvm::raw_string_ostream OS(Result);
Rewrite.getEditBuffer(ID).write(OS);
OS.flush();
return Result;
}

} // end namespace tooling
} // end namespace clang

1 change: 1 addition & 0 deletions clang/lib/Tooling/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@

CLANG_LEVEL := ../..
LIBRARYNAME := clangTooling
PARALLEL_DIRS := Core

include $(CLANG_LEVEL)/Makefile
261 changes: 0 additions & 261 deletions clang/lib/Tooling/Refactoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,267 +25,6 @@
namespace clang {
namespace tooling {

static const char * const InvalidLocation = "";

Replacement::Replacement()
: FilePath(InvalidLocation) {}

Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
StringRef ReplacementText)
: FilePath(FilePath), ReplacementRange(Offset, Length),
ReplacementText(ReplacementText) {}

Replacement::Replacement(const SourceManager &Sources, SourceLocation Start,
unsigned Length, StringRef ReplacementText) {
setFromSourceLocation(Sources, Start, Length, ReplacementText);
}

Replacement::Replacement(const SourceManager &Sources,
const CharSourceRange &Range,
StringRef ReplacementText) {
setFromSourceRange(Sources, Range, ReplacementText);
}

bool Replacement::isApplicable() const {
return FilePath != InvalidLocation;
}

bool Replacement::apply(Rewriter &Rewrite) const {
SourceManager &SM = Rewrite.getSourceMgr();
const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
if (!Entry)
return false;
FileID ID;
// FIXME: Use SM.translateFile directly.
SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1);
ID = Location.isValid() ?
SM.getFileID(Location) :
SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
// FIXME: We cannot check whether Offset + Length is in the file, as
// the remapping API is not public in the RewriteBuffer.
const SourceLocation Start =
SM.getLocForStartOfFile(ID).
getLocWithOffset(ReplacementRange.getOffset());
// ReplaceText returns false on success.
// ReplaceText only fails if the source location is not a file location, in
// which case we already returned false earlier.
bool RewriteSucceeded = !Rewrite.ReplaceText(
Start, ReplacementRange.getLength(), ReplacementText);
assert(RewriteSucceeded);
return RewriteSucceeded;
}

std::string Replacement::toString() const {
std::string result;
llvm::raw_string_ostream stream(result);
stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
<< ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
return result;
}

bool operator<(const Replacement &LHS, const Replacement &RHS) {
if (LHS.getOffset() != RHS.getOffset())
return LHS.getOffset() < RHS.getOffset();
if (LHS.getLength() != RHS.getLength())
return LHS.getLength() < RHS.getLength();
if (LHS.getFilePath() != RHS.getFilePath())
return LHS.getFilePath() < RHS.getFilePath();
return LHS.getReplacementText() < RHS.getReplacementText();
}

bool operator==(const Replacement &LHS, const Replacement &RHS) {
return LHS.getOffset() == RHS.getOffset() &&
LHS.getLength() == RHS.getLength() &&
LHS.getFilePath() == RHS.getFilePath() &&
LHS.getReplacementText() == RHS.getReplacementText();
}

void Replacement::setFromSourceLocation(const SourceManager &Sources,
SourceLocation Start, unsigned Length,
StringRef ReplacementText) {
const std::pair<FileID, unsigned> DecomposedLocation =
Sources.getDecomposedLoc(Start);
const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
if (Entry) {
// Make FilePath absolute so replacements can be applied correctly when
// relative paths for files are used.
llvm::SmallString<256> FilePath(Entry->getName());
std::error_code EC = llvm::sys::fs::make_absolute(FilePath);
this->FilePath = EC ? FilePath.c_str() : Entry->getName();
} else {
this->FilePath = InvalidLocation;
}
this->ReplacementRange = Range(DecomposedLocation.second, Length);
this->ReplacementText = ReplacementText;
}

// FIXME: This should go into the Lexer, but we need to figure out how
// to handle ranges for refactoring in general first - there is no obvious
// good way how to integrate this into the Lexer yet.
static int getRangeSize(const SourceManager &Sources,
const CharSourceRange &Range) {
SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
if (Start.first != End.first) return -1;
if (Range.isTokenRange())
End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources,
LangOptions());
return End.second - Start.second;
}

void Replacement::setFromSourceRange(const SourceManager &Sources,
const CharSourceRange &Range,
StringRef ReplacementText) {
setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
getRangeSize(Sources, Range), ReplacementText);
}

bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
bool Result = true;
for (Replacements::const_iterator I = Replaces.begin(),
E = Replaces.end();
I != E; ++I) {
if (I->isApplicable()) {
Result = I->apply(Rewrite) && Result;
} else {
Result = false;
}
}
return Result;
}

// FIXME: Remove this function when Replacements is implemented as std::vector
// instead of std::set.
bool applyAllReplacements(const std::vector<Replacement> &Replaces,
Rewriter &Rewrite) {
bool Result = true;
for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
E = Replaces.end();
I != E; ++I) {
if (I->isApplicable()) {
Result = I->apply(Rewrite) && Result;
} else {
Result = false;
}
}
return Result;
}

std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) {
FileManager Files((FileSystemOptions()));
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
new DiagnosticOptions);
Diagnostics.setClient(new TextDiagnosticPrinter(
llvm::outs(), &Diagnostics.getDiagnosticOptions()));
SourceManager SourceMgr(Diagnostics, Files);
Rewriter Rewrite(SourceMgr, LangOptions());
std::unique_ptr<llvm::MemoryBuffer> Buf =
llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>");
const clang::FileEntry *Entry =
Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0);
SourceMgr.overrideFileContents(Entry, std::move(Buf));
FileID ID =
SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User);
for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end();
I != E; ++I) {
Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
I->getReplacementText());
if (!Replace.apply(Rewrite))
return "";
}
std::string Result;
llvm::raw_string_ostream OS(Result);
Rewrite.getEditBuffer(ID).write(OS);
OS.flush();
return Result;
}

unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
unsigned NewPosition = Position;
for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E;
++I) {
if (I->getOffset() >= Position)
break;
if (I->getOffset() + I->getLength() > Position)
NewPosition += I->getOffset() + I->getLength() - Position;
NewPosition += I->getReplacementText().size() - I->getLength();
}
return NewPosition;
}

// FIXME: Remove this function when Replacements is implemented as std::vector
// instead of std::set.
unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
unsigned Position) {
unsigned NewPosition = Position;
for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
E = Replaces.end();
I != E; ++I) {
if (I->getOffset() >= Position)
break;
if (I->getOffset() + I->getLength() > Position)
NewPosition += I->getOffset() + I->getLength() - Position;
NewPosition += I->getReplacementText().size() - I->getLength();
}
return NewPosition;
}

void deduplicate(std::vector<Replacement> &Replaces,
std::vector<Range> &Conflicts) {
if (Replaces.empty())
return;

auto LessNoPath = [](const Replacement &LHS, const Replacement &RHS) {
if (LHS.getOffset() != RHS.getOffset())
return LHS.getOffset() < RHS.getOffset();
if (LHS.getLength() != RHS.getLength())
return LHS.getLength() < RHS.getLength();
return LHS.getReplacementText() < RHS.getReplacementText();
};

auto EqualNoPath = [](const Replacement &LHS, const Replacement &RHS) {
return LHS.getOffset() == RHS.getOffset() &&
LHS.getLength() == RHS.getLength() &&
LHS.getReplacementText() == RHS.getReplacementText();
};

// Deduplicate. We don't want to deduplicate based on the path as we assume
// that all replacements refer to the same file (or are symlinks).
std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
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(),
std::max(ConflictRange.getLength(),
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) {}
Expand Down
1 change: 0 additions & 1 deletion clang/tools/clang-format/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ target_link_libraries(clang-format
clangFormat
clangLex
clangRewrite
clangTooling
)

install(TARGETS clang-format RUNTIME DESTINATION bin)
Expand Down
4 changes: 1 addition & 3 deletions clang/tools/clang-format/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ TOOL_NO_EXPORTS = 1

include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
USEDLIBS = clangFormat.a clangTooling.a clangFrontend.a clangSerialization.a \
clangDriver.a clangParse.a clangSema.a clangAnalysis.a \
clangRewriteFrontend.a clangRewrite.a clangEdit.a clangAST.a \
USEDLIBS = clangFormat.a clangToolingCore.a clangDriver.a clangRewrite.a \
clangLex.a clangBasic.a

include $(CLANG_LEVEL)/Makefile
2 changes: 1 addition & 1 deletion clang/tools/libclang/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ LINK_COMPONENTS := AsmParser BitReader Core MC MCParser Option Support
USEDLIBS = clangIndex.a clangARCMigrate.a \
clangRewriteFrontend.a \
clangFormat.a \
clangTooling.a \
clangTooling.a clangToolingCore.a \
clangFrontend.a clangDriver.a \
clangSerialization.a \
clangParse.a clangSema.a \
Expand Down
2 changes: 1 addition & 1 deletion clang/unittests/Format/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ add_clang_unittest(FormatTests

target_link_libraries(FormatTests
clangFormat
clangTooling
clangToolingCore
)
4 changes: 2 additions & 2 deletions clang/unittests/Format/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ CLANG_LEVEL = ../..
TESTNAME = Format
include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
USEDLIBS = clangFormat.a clangTooling.a clangFrontend.a clangSerialization.a \
clangDriver.a clangParse.a clangRewrite.a \
USEDLIBS = clangFormat.a clangTooling.a clangToolingCore.a clangFrontend.a \
clangSerialization.a clangDriver.a clangParse.a clangRewrite.a \
clangRewriteFrontend.a clangSema.a clangAnalysis.a clangEdit.a \
clangAST.a clangASTMatchers.a clangLex.a clangBasic.a

Expand Down
3 changes: 2 additions & 1 deletion clang/unittests/Tooling/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ CLANG_LEVEL = ../..
TESTNAME = Tooling
include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
USEDLIBS = clangTooling.a clangToolingCore.a clangFrontend.a \
clangSerialization.a clangDriver.a \
clangParse.a clangRewrite.a clangRewriteFrontend.a \
clangSema.a clangAnalysis.a clangEdit.a \
clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
Expand Down