forked from OSchip/llvm-project
Implement tooling::Replacements as a class.
Summary: - Implement clang::tooling::Replacements as a class to provide interfaces to control how replacements for a single file are combined and provide guarantee on the order of replacements being applied. - tooling::Replacements only contains replacements for the same file now. Use std::map<std::string, tooling::Replacements> to represent multi-file replacements. - Error handling for the interface change will be improved in followup patches. Reviewers: djasper, klimek Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D21748 llvm-svn: 277335
This commit is contained in:
parent
5c9583981b
commit
40ef2fb363
|
@ -123,14 +123,13 @@ public:
|
|||
/// \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,
|
||||
const LangOptions &LangOpts);
|
||||
private:
|
||||
void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start,
|
||||
unsigned Length, StringRef ReplacementText);
|
||||
void setFromSourceRange(const SourceManager &Sources,
|
||||
const CharSourceRange &Range,
|
||||
StringRef ReplacementText,
|
||||
const LangOptions &LangOpts);
|
||||
|
||||
std::string FilePath;
|
||||
Range ReplacementRange;
|
||||
|
@ -143,9 +142,70 @@ 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 Maintains a set of replacements that are conflict-free.
|
||||
/// Two replacements are considered conflicts if they overlap or have the same
|
||||
/// offset (i.e. order-dependent).
|
||||
class Replacements {
|
||||
private:
|
||||
typedef std::set<Replacement> ReplacementsImpl;
|
||||
|
||||
public:
|
||||
typedef ReplacementsImpl::const_iterator const_iterator;
|
||||
|
||||
Replacements() = default;
|
||||
|
||||
explicit Replacements(const Replacement &R) { Replaces.insert(R); }
|
||||
|
||||
/// \brief Adds a new replacement \p R to the current set of replacements.
|
||||
/// \p R must have the same file path as all existing replacements.
|
||||
/// Returns true if the replacement is successfully inserted; otherwise,
|
||||
/// it returns an llvm::Error, i.e. there is a conflict between R and the
|
||||
/// existing replacements or R's file path is different from the filepath of
|
||||
/// existing replacements. Callers must explicitly check the Error returned.
|
||||
/// This prevents users from adding order-dependent replacements. To control
|
||||
/// the order in which order-dependent replacements are applied, use
|
||||
/// merge({R}) with R referring to the changed code after applying all
|
||||
/// existing replacements.
|
||||
/// Replacements with offset UINT_MAX are special - we do not detect conflicts
|
||||
/// for such replacements since users may add them intentionally as a special
|
||||
/// category of replacements.
|
||||
llvm::Error add(const Replacement &R);
|
||||
|
||||
/// \brief Merges \p Replaces into the current replacements. \p Replaces
|
||||
/// refers to code after applying the current replacements.
|
||||
Replacements merge(const Replacements &Replaces) const;
|
||||
|
||||
// Returns the affected ranges in the changed code.
|
||||
std::vector<Range> getAffectedRanges() const;
|
||||
|
||||
// Returns the new offset in the code after replacements being applied.
|
||||
// Note that if there is an insertion at Offset in the current replacements,
|
||||
// \p Offset will be shifted to Offset + Length in inserted text.
|
||||
unsigned getShiftedCodePosition(unsigned Position) const;
|
||||
|
||||
unsigned size() const { return Replaces.size(); }
|
||||
|
||||
void clear() { Replaces.clear(); }
|
||||
|
||||
bool empty() const { return Replaces.empty(); }
|
||||
|
||||
const_iterator begin() const { return Replaces.begin(); }
|
||||
|
||||
const_iterator end() const { return Replaces.end(); }
|
||||
|
||||
bool operator==(const Replacements &RHS) const {
|
||||
return Replaces == RHS.Replaces;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Replacements(const_iterator Begin, const_iterator End)
|
||||
: Replaces(Begin, End) {}
|
||||
|
||||
Replacements mergeReplacements(const ReplacementsImpl &Second) const;
|
||||
|
||||
ReplacementsImpl Replaces;
|
||||
};
|
||||
|
||||
/// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
|
||||
///
|
||||
|
@ -155,15 +215,6 @@ typedef std::set<Replacement> Replacements;
|
|||
/// \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 all
|
||||
|
@ -174,27 +225,6 @@ bool applyAllReplacements(const std::vector<Replacement> &Replaces,
|
|||
llvm::Expected<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.
|
||||
|
@ -208,14 +238,6 @@ struct TranslationUnitReplacements {
|
|||
std::vector<Replacement> Replacements;
|
||||
};
|
||||
|
||||
/// \brief Calculates the ranges in a single file that are affected by the
|
||||
/// Replacements. Overlapping ranges will be merged.
|
||||
///
|
||||
/// \pre Replacements must be for the same file.
|
||||
///
|
||||
/// \returns a non-overlapping and sorted ranges.
|
||||
std::vector<Range> calculateChangedRanges(const Replacements &Replaces);
|
||||
|
||||
/// \brief Calculates the new ranges after \p Replaces are applied. These
|
||||
/// include both the original \p Ranges and the affected ranges of \p Replaces
|
||||
/// in the new code.
|
||||
|
@ -233,12 +255,6 @@ calculateRangesAfterReplacements(const Replacements &Replaces,
|
|||
std::map<std::string, Replacements>
|
||||
groupReplacementsByFile(const Replacements &Replaces);
|
||||
|
||||
/// \brief Merges two sets of replacements with the second set referring to the
|
||||
/// code after applying the first set. Within both 'First' and 'Second',
|
||||
/// replacements must not overlap.
|
||||
Replacements mergeReplacements(const Replacements &First,
|
||||
const Replacements &Second);
|
||||
|
||||
template <typename Node>
|
||||
Replacement::Replacement(const SourceManager &Sources,
|
||||
const Node &NodeToReplace, StringRef ReplacementText,
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "clang/Tooling/Core/Replacement.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace clang {
|
||||
|
@ -42,9 +43,9 @@ public:
|
|||
std::shared_ptr<PCHContainerOperations> PCHContainerOps =
|
||||
std::make_shared<PCHContainerOperations>());
|
||||
|
||||
/// \brief Returns the set of replacements to which replacements should
|
||||
/// be added during the run of the tool.
|
||||
Replacements &getReplacements();
|
||||
/// \brief Returns the file path to replacements map to which replacements
|
||||
/// should be added during the run of the tool.
|
||||
std::map<std::string, Replacements> &getReplacements();
|
||||
|
||||
/// \brief Call run(), apply all generated replacements, and immediately save
|
||||
/// the results to disk.
|
||||
|
@ -65,7 +66,7 @@ private:
|
|||
int saveRewrittenFiles(Rewriter &Rewrite);
|
||||
|
||||
private:
|
||||
Replacements Replace;
|
||||
std::map<std::string, Replacements> FileToReplaces;
|
||||
};
|
||||
|
||||
/// \brief Groups \p Replaces by the file path and applies each group of
|
||||
|
@ -77,14 +78,15 @@ private:
|
|||
/// Replacement applications happen independently of the success of other
|
||||
/// applications.
|
||||
///
|
||||
/// \param[in] Replaces Replacements to apply.
|
||||
/// \param[in] FileToReplaces Replacements (grouped by files) to apply.
|
||||
/// \param[in] Rewrite The `Rewritter` to apply replacements on.
|
||||
/// \param[in] Style The style name used for reformatting. See ```getStyle``` in
|
||||
/// "include/clang/Format/Format.h" for all possible style forms.
|
||||
///
|
||||
/// \returns true if all replacements applied and formatted. false otherwise.
|
||||
bool formatAndApplyAllReplacements(const Replacements &Replaces,
|
||||
Rewriter &Rewrite, StringRef Style = "file");
|
||||
bool formatAndApplyAllReplacements(
|
||||
const std::map<std::string, Replacements> &FileToReplaces,
|
||||
Rewriter &Rewrite, StringRef Style = "file");
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
||||
|
|
|
@ -854,8 +854,13 @@ private:
|
|||
SourceLocation Start = FormatTok->Tok.getLocation();
|
||||
auto Replace = [&](SourceLocation Start, unsigned Length,
|
||||
StringRef ReplacementText) {
|
||||
Result.insert(tooling::Replacement(Env.getSourceManager(), Start,
|
||||
Length, ReplacementText));
|
||||
auto Err = Result.add(tooling::Replacement(
|
||||
Env.getSourceManager(), Start, Length, ReplacementText));
|
||||
// FIXME: handle error. For now, print error message and skip the
|
||||
// replacement for release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err);
|
||||
};
|
||||
Replace(Start, 1, IsSingle ? "'" : "\"");
|
||||
Replace(FormatTok->Tok.getEndLoc().getLocWithOffset(-1), 1,
|
||||
|
@ -1163,7 +1168,13 @@ private:
|
|||
}
|
||||
auto SR = CharSourceRange::getCharRange(Tokens[St]->Tok.getLocation(),
|
||||
Tokens[End]->Tok.getEndLoc());
|
||||
Fixes.insert(tooling::Replacement(Env.getSourceManager(), SR, ""));
|
||||
auto Err =
|
||||
Fixes.add(tooling::Replacement(Env.getSourceManager(), SR, ""));
|
||||
// FIXME: better error handling. for now just print error message and skip
|
||||
// for the release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err && "Fixes must not conflict!");
|
||||
Idx = End + 1;
|
||||
}
|
||||
|
||||
|
@ -1256,8 +1267,13 @@ static void sortCppIncludes(const FormatStyle &Style,
|
|||
Includes.back().Offset + Includes.back().Text.size() -
|
||||
Includes.front().Offset);
|
||||
|
||||
Replaces.insert(tooling::Replacement(FileName, Includes.front().Offset,
|
||||
result.size(), result));
|
||||
auto Err = Replaces.add(tooling::Replacement(
|
||||
FileName, Includes.front().Offset, result.size(), result));
|
||||
// FIXME: better error handling. For now, just skip the replacement for the
|
||||
// release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -1402,14 +1418,13 @@ processReplacements(T ProcessFunc, StringRef Code,
|
|||
auto NewCode = applyAllReplacements(Code, Replaces);
|
||||
if (!NewCode)
|
||||
return NewCode.takeError();
|
||||
std::vector<tooling::Range> ChangedRanges =
|
||||
tooling::calculateChangedRanges(Replaces);
|
||||
std::vector<tooling::Range> ChangedRanges = Replaces.getAffectedRanges();
|
||||
StringRef FileName = Replaces.begin()->getFilePath();
|
||||
|
||||
tooling::Replacements FormatReplaces =
|
||||
ProcessFunc(Style, *NewCode, ChangedRanges, FileName);
|
||||
|
||||
return mergeReplacements(Replaces, FormatReplaces);
|
||||
return Replaces.merge(FormatReplaces);
|
||||
}
|
||||
|
||||
llvm::Expected<tooling::Replacements>
|
||||
|
@ -1497,20 +1512,22 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces,
|
|||
return Replaces;
|
||||
|
||||
tooling::Replacements HeaderInsertions;
|
||||
tooling::Replacements Result;
|
||||
for (const auto &R : Replaces) {
|
||||
if (isHeaderInsertion(R))
|
||||
HeaderInsertions.insert(R);
|
||||
else if (R.getOffset() == UINT_MAX)
|
||||
if (isHeaderInsertion(R)) {
|
||||
// Replacements from \p Replaces must be conflict-free already, so we can
|
||||
// simply consume the error.
|
||||
llvm::consumeError(HeaderInsertions.add(R));
|
||||
} else if (R.getOffset() == UINT_MAX) {
|
||||
llvm::errs() << "Insertions other than header #include insertion are "
|
||||
"not supported! "
|
||||
<< R.getReplacementText() << "\n";
|
||||
} else {
|
||||
llvm::consumeError(Result.add(R));
|
||||
}
|
||||
}
|
||||
if (HeaderInsertions.empty())
|
||||
return Replaces;
|
||||
tooling::Replacements Result;
|
||||
std::set_difference(Replaces.begin(), Replaces.end(),
|
||||
HeaderInsertions.begin(), HeaderInsertions.end(),
|
||||
std::inserter(Result, Result.begin()));
|
||||
|
||||
llvm::Regex IncludeRegex(IncludeRegexPattern);
|
||||
llvm::Regex DefineRegex(R"(^[\t\ ]*#[\t\ ]*define[\t\ ]*[^\\]*$)");
|
||||
|
@ -1587,7 +1604,12 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces,
|
|||
std::string NewInclude = !IncludeDirective.endswith("\n")
|
||||
? (IncludeDirective + "\n").str()
|
||||
: IncludeDirective.str();
|
||||
Result.insert(tooling::Replacement(FileName, Offset, 0, NewInclude));
|
||||
auto NewReplace = tooling::Replacement(FileName, Offset, 0, NewInclude);
|
||||
auto Err = Result.add(NewReplace);
|
||||
if (Err) {
|
||||
llvm::consumeError(std::move(Err));
|
||||
Result = Result.merge(tooling::Replacements(NewReplace));
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
|
|
@ -127,7 +127,8 @@ public:
|
|||
tooling::Replacements
|
||||
analyze(TokenAnnotator &Annotator,
|
||||
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
|
||||
FormatTokenLexer &Tokens, tooling::Replacements &Result) override {
|
||||
FormatTokenLexer &Tokens, tooling::Replacements &) override {
|
||||
tooling::Replacements Result;
|
||||
AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(),
|
||||
AnnotatedLines.end());
|
||||
|
||||
|
@ -192,9 +193,14 @@ public:
|
|||
DEBUG(llvm::dbgs() << "Replacing imports:\n"
|
||||
<< getSourceText(InsertionPoint) << "\nwith:\n"
|
||||
<< ReferencesText << "\n");
|
||||
Result.insert(tooling::Replacement(
|
||||
auto Err = Result.add(tooling::Replacement(
|
||||
Env.getSourceManager(), CharSourceRange::getCharRange(InsertionPoint),
|
||||
ReferencesText));
|
||||
// FIXME: better error handling. For now, just print error message and skip
|
||||
// the replacement for the release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
|
|
@ -111,8 +111,8 @@ tooling::Replacements TokenAnalyzer::process() {
|
|||
|
||||
DEBUG({
|
||||
llvm::dbgs() << "Replacements for run " << Run << ":\n";
|
||||
for (tooling::Replacements::iterator I = RunResult.begin(),
|
||||
E = RunResult.end();
|
||||
for (tooling::Replacements::const_iterator I = RunResult.begin(),
|
||||
E = RunResult.end();
|
||||
I != E; ++I) {
|
||||
llvm::dbgs() << I->toString() << "\n";
|
||||
}
|
||||
|
@ -120,7 +120,15 @@ tooling::Replacements TokenAnalyzer::process() {
|
|||
for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
|
||||
delete AnnotatedLines[i];
|
||||
}
|
||||
Result.insert(RunResult.begin(), RunResult.end());
|
||||
for (auto R : RunResult) {
|
||||
auto Err = Result.add(R);
|
||||
// FIXME: better error handling here. For now, simply return an empty
|
||||
// Replacements to indicate failure.
|
||||
if (Err) {
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
return tooling::Replacements();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
|
|
@ -502,8 +502,13 @@ void WhitespaceManager::storeReplacement(SourceRange Range,
|
|||
if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
|
||||
WhitespaceLength) == Text)
|
||||
return;
|
||||
Replaces.insert(tooling::Replacement(
|
||||
auto Err = Replaces.add(tooling::Replacement(
|
||||
SourceMgr, CharSourceRange::getCharRange(Range), Text));
|
||||
// FIXME: better error handling. For now, just print an error message in the
|
||||
// release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err);
|
||||
}
|
||||
|
||||
void WhitespaceManager::appendNewlineText(std::string &Text,
|
||||
|
|
|
@ -137,200 +137,30 @@ void Replacement::setFromSourceRange(const SourceManager &Sources,
|
|||
ReplacementText);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unsigned shiftedCodePositionInternal(const T &Replaces, unsigned Position) {
|
||||
unsigned Offset = 0;
|
||||
for (const auto& R : Replaces) {
|
||||
if (R.getOffset() + R.getLength() <= Position) {
|
||||
Offset += R.getReplacementText().size() - R.getLength();
|
||||
continue;
|
||||
llvm::Error Replacements::add(const Replacement &R) {
|
||||
if (R.getOffset() != UINT_MAX)
|
||||
for (auto Replace : Replaces) {
|
||||
if (R.getFilePath() != Replace.getFilePath())
|
||||
return llvm::make_error<llvm::StringError>(
|
||||
"All replacements must have the same file path. New replacement: " +
|
||||
R.getFilePath() + ", existing replacements: " +
|
||||
Replace.getFilePath() + "\n",
|
||||
llvm::inconvertibleErrorCode());
|
||||
if (R.getOffset() == Replace.getOffset() ||
|
||||
Range(R.getOffset(), R.getLength())
|
||||
.overlapsWith(Range(Replace.getOffset(), Replace.getLength())))
|
||||
return llvm::make_error<llvm::StringError>(
|
||||
"New replacement:\n" + R.toString() +
|
||||
"\nconflicts with existing replacement:\n" + Replace.toString(),
|
||||
llvm::inconvertibleErrorCode());
|
||||
}
|
||||
if (R.getOffset() < Position &&
|
||||
R.getOffset() + R.getReplacementText().size() <= Position) {
|
||||
Position = R.getOffset() + R.getReplacementText().size() - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Position + Offset;
|
||||
}
|
||||
|
||||
unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
|
||||
return shiftedCodePositionInternal(Replaces, Position);
|
||||
}
|
||||
|
||||
// FIXME: Remove this function when Replacements is implemented as std::vector
|
||||
// instead of std::set.
|
||||
unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
|
||||
unsigned Position) {
|
||||
return shiftedCodePositionInternal(Replaces, Position);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
llvm::Expected<std::string> applyAllReplacements(StringRef Code,
|
||||
const Replacements &Replaces) {
|
||||
if (Replaces.empty())
|
||||
return Code.str();
|
||||
|
||||
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
|
||||
new vfs::InMemoryFileSystem);
|
||||
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
|
||||
new DiagnosticOptions);
|
||||
SourceManager SourceMgr(Diagnostics, Files);
|
||||
Rewriter Rewrite(SourceMgr, LangOptions());
|
||||
InMemoryFileSystem->addFile(
|
||||
"<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"));
|
||||
FileID ID = SourceMgr.createFileID(Files.getFile("<stdin>"), 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 llvm::make_error<llvm::StringError>(
|
||||
"Failed to apply replacement: " + Replace.toString(),
|
||||
llvm::inconvertibleErrorCode());
|
||||
}
|
||||
std::string Result;
|
||||
llvm::raw_string_ostream OS(Result);
|
||||
Rewrite.getEditBuffer(ID).write(OS);
|
||||
OS.flush();
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Merge and sort overlapping ranges in \p Ranges.
|
||||
static std::vector<Range> mergeAndSortRanges(std::vector<Range> Ranges) {
|
||||
std::sort(Ranges.begin(), Ranges.end(),
|
||||
[](const Range &LHS, const Range &RHS) {
|
||||
if (LHS.getOffset() != RHS.getOffset())
|
||||
return LHS.getOffset() < RHS.getOffset();
|
||||
return LHS.getLength() < RHS.getLength();
|
||||
});
|
||||
std::vector<Range> Result;
|
||||
for (const auto &R : Ranges) {
|
||||
if (Result.empty() ||
|
||||
Result.back().getOffset() + Result.back().getLength() < R.getOffset()) {
|
||||
Result.push_back(R);
|
||||
} else {
|
||||
unsigned NewEnd =
|
||||
std::max(Result.back().getOffset() + Result.back().getLength(),
|
||||
R.getOffset() + R.getLength());
|
||||
Result[Result.size() - 1] =
|
||||
Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
std::vector<Range> calculateChangedRanges(const Replacements &Replaces) {
|
||||
std::vector<Range> ChangedRanges;
|
||||
int Shift = 0;
|
||||
for (const Replacement &R : Replaces) {
|
||||
unsigned Offset = R.getOffset() + Shift;
|
||||
unsigned Length = R.getReplacementText().size();
|
||||
Shift += Length - R.getLength();
|
||||
ChangedRanges.push_back(Range(Offset, Length));
|
||||
}
|
||||
return mergeAndSortRanges(ChangedRanges);
|
||||
}
|
||||
|
||||
std::vector<Range>
|
||||
calculateRangesAfterReplacements(const Replacements &Replaces,
|
||||
const std::vector<Range> &Ranges) {
|
||||
auto MergedRanges = mergeAndSortRanges(Ranges);
|
||||
tooling::Replacements FakeReplaces;
|
||||
for (const auto &R : MergedRanges)
|
||||
FakeReplaces.insert(Replacement(Replaces.begin()->getFilePath(),
|
||||
R.getOffset(), R.getLength(),
|
||||
std::string(R.getLength(), ' ')));
|
||||
tooling::Replacements NewReplaces = mergeReplacements(FakeReplaces, Replaces);
|
||||
return calculateChangedRanges(NewReplaces);
|
||||
Replaces.insert(R);
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Represents a merged replacement, i.e. a replacement consisting of multiple
|
||||
// overlapping replacements from 'First' and 'Second' in mergeReplacements.
|
||||
//
|
||||
|
@ -424,26 +254,19 @@ private:
|
|||
unsigned Length;
|
||||
std::string Text;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::map<std::string, Replacements>
|
||||
groupReplacementsByFile(const Replacements &Replaces) {
|
||||
std::map<std::string, Replacements> FileToReplaces;
|
||||
for (const auto &Replace : Replaces) {
|
||||
FileToReplaces[Replace.getFilePath()].insert(Replace);
|
||||
}
|
||||
return FileToReplaces;
|
||||
}
|
||||
|
||||
Replacements mergeReplacements(const Replacements &First,
|
||||
const Replacements &Second) {
|
||||
if (First.empty() || Second.empty())
|
||||
return First.empty() ? Second : First;
|
||||
Replacements Replacements::merge(const Replacements &ReplacesToMerge) const {
|
||||
if (empty() || ReplacesToMerge.empty())
|
||||
return empty() ? ReplacesToMerge : *this;
|
||||
|
||||
auto &First = Replaces;
|
||||
auto &Second = ReplacesToMerge.Replaces;
|
||||
// Delta is the amount of characters that replacements from 'Second' need to
|
||||
// be shifted so that their offsets refer to the original text.
|
||||
int Delta = 0;
|
||||
Replacements Result;
|
||||
ReplacementsImpl Result;
|
||||
|
||||
// Iterate over both sets and always add the next element (smallest total
|
||||
// Offset) from either 'First' or 'Second'. Merge that element with
|
||||
|
@ -469,8 +292,141 @@ Replacements mergeReplacements(const Replacements &First,
|
|||
Delta -= Merged.deltaFirst();
|
||||
Result.insert(Merged.asReplacement());
|
||||
}
|
||||
return Replacements(Result.begin(), Result.end());
|
||||
}
|
||||
|
||||
// Combines overlapping ranges in \p Ranges and sorts the combined ranges.
|
||||
// Returns a set of non-overlapping and sorted ranges that is equivalent to
|
||||
// \p Ranges.
|
||||
static std::vector<Range> combineAndSortRanges(std::vector<Range> Ranges) {
|
||||
std::sort(Ranges.begin(), Ranges.end(),
|
||||
[](const Range &LHS, const Range &RHS) {
|
||||
if (LHS.getOffset() != RHS.getOffset())
|
||||
return LHS.getOffset() < RHS.getOffset();
|
||||
return LHS.getLength() < RHS.getLength();
|
||||
});
|
||||
std::vector<Range> Result;
|
||||
for (const auto &R : Ranges) {
|
||||
if (Result.empty() ||
|
||||
Result.back().getOffset() + Result.back().getLength() < R.getOffset()) {
|
||||
Result.push_back(R);
|
||||
} else {
|
||||
unsigned NewEnd =
|
||||
std::max(Result.back().getOffset() + Result.back().getLength(),
|
||||
R.getOffset() + R.getLength());
|
||||
Result[Result.size() - 1] =
|
||||
Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
std::vector<Range>
|
||||
calculateRangesAfterReplacements(const Replacements &Replaces,
|
||||
const std::vector<Range> &Ranges) {
|
||||
// To calculate the new ranges,
|
||||
// - Turn \p Ranges into Replacements at (offset, length) with an empty
|
||||
// (unimportant) replacement text of length "length".
|
||||
// - Merge with \p Replaces.
|
||||
// - The new ranges will be the affected ranges of the merged replacements.
|
||||
auto MergedRanges = combineAndSortRanges(Ranges);
|
||||
tooling::Replacements FakeReplaces;
|
||||
for (const auto &R : MergedRanges) {
|
||||
auto Err = FakeReplaces.add(Replacement(Replaces.begin()->getFilePath(),
|
||||
R.getOffset(), R.getLength(),
|
||||
std::string(R.getLength(), ' ')));
|
||||
assert(!Err &&
|
||||
"Replacements must not conflict since ranges have been merged.");
|
||||
(void)Err;
|
||||
}
|
||||
return FakeReplaces.merge(Replaces).getAffectedRanges();
|
||||
}
|
||||
|
||||
std::vector<Range> Replacements::getAffectedRanges() const {
|
||||
std::vector<Range> ChangedRanges;
|
||||
int Shift = 0;
|
||||
for (const Replacement &R : Replaces) {
|
||||
unsigned Offset = R.getOffset() + Shift;
|
||||
unsigned Length = R.getReplacementText().size();
|
||||
Shift += Length - R.getLength();
|
||||
ChangedRanges.push_back(Range(Offset, Length));
|
||||
}
|
||||
return combineAndSortRanges(ChangedRanges);
|
||||
}
|
||||
|
||||
unsigned Replacements::getShiftedCodePosition(unsigned Position) const {
|
||||
unsigned Offset = 0;
|
||||
for (const auto& R : Replaces) {
|
||||
if (R.getOffset() + R.getLength() <= Position) {
|
||||
Offset += R.getReplacementText().size() - R.getLength();
|
||||
continue;
|
||||
}
|
||||
if (R.getOffset() < Position &&
|
||||
R.getOffset() + R.getReplacementText().size() <= Position) {
|
||||
Position = R.getOffset() + R.getReplacementText().size();
|
||||
if (R.getReplacementText().size() > 0)
|
||||
Position--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Position + Offset;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
llvm::Expected<std::string> applyAllReplacements(StringRef Code,
|
||||
const Replacements &Replaces) {
|
||||
if (Replaces.empty())
|
||||
return Code.str();
|
||||
|
||||
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
|
||||
new vfs::InMemoryFileSystem);
|
||||
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
|
||||
new DiagnosticOptions);
|
||||
SourceManager SourceMgr(Diagnostics, Files);
|
||||
Rewriter Rewrite(SourceMgr, LangOptions());
|
||||
InMemoryFileSystem->addFile(
|
||||
"<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"));
|
||||
FileID ID = SourceMgr.createFileID(Files.getFile("<stdin>"), 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 llvm::make_error<llvm::StringError>(
|
||||
"Failed to apply replacement: " + Replace.toString(),
|
||||
llvm::inconvertibleErrorCode());
|
||||
}
|
||||
std::string Result;
|
||||
llvm::raw_string_ostream OS(Result);
|
||||
Rewrite.getEditBuffer(ID).write(OS);
|
||||
OS.flush();
|
||||
return Result;
|
||||
}
|
||||
|
||||
std::map<std::string, Replacements>
|
||||
groupReplacementsByFile(const Replacements &Replaces) {
|
||||
std::map<std::string, Replacements> FileToReplaces;
|
||||
for (const auto &Replace : Replaces)
|
||||
// We can ignore the Error here since \p Replaces is already conflict-free.
|
||||
FileToReplaces[Replace.getFilePath()].add(Replace);
|
||||
return FileToReplaces;
|
||||
}
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
||||
|
|
|
@ -30,7 +30,9 @@ RefactoringTool::RefactoringTool(
|
|||
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
|
||||
: ClangTool(Compilations, SourcePaths, PCHContainerOps) {}
|
||||
|
||||
Replacements &RefactoringTool::getReplacements() { return Replace; }
|
||||
std::map<std::string, Replacements> &RefactoringTool::getReplacements() {
|
||||
return FileToReplaces;
|
||||
}
|
||||
|
||||
int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
|
||||
if (int Result = run(ActionFactory)) {
|
||||
|
@ -54,20 +56,22 @@ int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
|
|||
}
|
||||
|
||||
bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) {
|
||||
return tooling::applyAllReplacements(Replace, Rewrite);
|
||||
bool Result = true;
|
||||
for (const auto &Entry : FileToReplaces)
|
||||
Result = tooling::applyAllReplacements(Entry.second, Rewrite) && Result;
|
||||
return Result;
|
||||
}
|
||||
|
||||
int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) {
|
||||
return Rewrite.overwriteChangedFiles() ? 1 : 0;
|
||||
}
|
||||
|
||||
bool formatAndApplyAllReplacements(const Replacements &Replaces,
|
||||
Rewriter &Rewrite, StringRef Style) {
|
||||
bool formatAndApplyAllReplacements(
|
||||
const std::map<std::string, Replacements> &FileToReplaces, Rewriter &Rewrite,
|
||||
StringRef Style) {
|
||||
SourceManager &SM = Rewrite.getSourceMgr();
|
||||
FileManager &Files = SM.getFileManager();
|
||||
|
||||
auto FileToReplaces = groupReplacementsByFile(Replaces);
|
||||
|
||||
bool Result = true;
|
||||
for (const auto &FileAndReplaces : FileToReplaces) {
|
||||
const std::string &FilePath = FileAndReplaces.first;
|
||||
|
|
|
@ -40,10 +40,14 @@ ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
|
|||
void ReplaceStmtWithText::run(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) {
|
||||
Replace.insert(tooling::Replacement(
|
||||
auto Err = Replace.add(tooling::Replacement(
|
||||
*Result.SourceManager,
|
||||
CharSourceRange::getTokenRange(FromMatch->getSourceRange()),
|
||||
ToText));
|
||||
CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
|
||||
// FIXME: better error handling. For now, just print error message in the
|
||||
// release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,9 +58,15 @@ void ReplaceStmtWithStmt::run(
|
|||
const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId);
|
||||
const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId);
|
||||
if (FromMatch && ToMatch)
|
||||
Replace.insert(replaceStmtWithStmt(
|
||||
*Result.SourceManager, *FromMatch, *ToMatch));
|
||||
if (FromMatch && ToMatch) {
|
||||
auto Err = Replace.add(
|
||||
replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
|
||||
// FIXME: better error handling. For now, just print error message in the
|
||||
// release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err);
|
||||
}
|
||||
}
|
||||
|
||||
ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
|
||||
|
@ -68,11 +78,23 @@ void ReplaceIfStmtWithItsBody::run(
|
|||
if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) {
|
||||
const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
|
||||
if (Body) {
|
||||
Replace.insert(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
|
||||
auto Err =
|
||||
Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
|
||||
// FIXME: better error handling. For now, just print error message in the
|
||||
// release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err);
|
||||
} else if (!PickTrueBranch) {
|
||||
// If we want to use the 'else'-branch, but it doesn't exist, delete
|
||||
// the whole 'if'.
|
||||
Replace.insert(replaceStmtWithText(*Result.SourceManager, *Node, ""));
|
||||
auto Err =
|
||||
Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));
|
||||
// FIXME: better error handling. For now, just print error message in the
|
||||
// release version.
|
||||
if (Err)
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
assert(!Err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,17 +266,17 @@ static bool format(StringRef FileName) {
|
|||
bool IncompleteFormat = false;
|
||||
Replacements FormatChanges = reformat(FormatStyle, *ChangedCode, Ranges,
|
||||
AssumedFileName, &IncompleteFormat);
|
||||
Replaces = tooling::mergeReplacements(Replaces, FormatChanges);
|
||||
Replaces = Replaces.merge(FormatChanges);
|
||||
if (OutputXML) {
|
||||
outs() << "<?xml version='1.0'?>\n<replacements "
|
||||
"xml:space='preserve' incomplete_format='"
|
||||
<< (IncompleteFormat ? "true" : "false") << "'>\n";
|
||||
if (Cursor.getNumOccurrences() != 0)
|
||||
outs() << "<cursor>"
|
||||
<< tooling::shiftedCodePosition(FormatChanges, CursorPosition)
|
||||
<< FormatChanges.getShiftedCodePosition(CursorPosition)
|
||||
<< "</cursor>\n";
|
||||
|
||||
outputReplacementsXML(Replaces);
|
||||
outputReplacementsXML(Replaces);
|
||||
outs() << "</replacements>\n";
|
||||
} else {
|
||||
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
|
||||
|
@ -298,7 +298,7 @@ static bool format(StringRef FileName) {
|
|||
} else {
|
||||
if (Cursor.getNumOccurrences() != 0)
|
||||
outs() << "{ \"Cursor\": "
|
||||
<< tooling::shiftedCodePosition(FormatChanges, CursorPosition)
|
||||
<< FormatChanges.getShiftedCodePosition(CursorPosition)
|
||||
<< ", \"IncompleteFormat\": "
|
||||
<< (IncompleteFormat ? "true" : "false") << " }\n";
|
||||
Rewrite.getEditBuffer(ID).write(outs());
|
||||
|
|
|
@ -9,11 +9,15 @@
|
|||
|
||||
#include "clang/Format/Format.h"
|
||||
|
||||
#include "../Tooling/ReplacementTest.h"
|
||||
#include "../Tooling/RewriterTestContext.h"
|
||||
#include "clang/Tooling/Core/Replacement.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using clang::tooling::ReplacementTest;
|
||||
using clang::tooling::toReplacements;
|
||||
|
||||
namespace clang {
|
||||
namespace format {
|
||||
namespace {
|
||||
|
@ -241,7 +245,7 @@ TEST_F(CleanupTest, CtorInitializerInNamespace) {
|
|||
EXPECT_EQ(Expected, Result);
|
||||
}
|
||||
|
||||
class CleanUpReplacementsTest : public ::testing::Test {
|
||||
class CleanUpReplacementsTest : public ReplacementTest {
|
||||
protected:
|
||||
tooling::Replacement createReplacement(unsigned Offset, unsigned Length,
|
||||
StringRef Text) {
|
||||
|
@ -304,9 +308,9 @@ TEST_F(CleanUpReplacementsTest, FixOnlyAffectedCodeAfterReplacements) {
|
|||
"namespace D { int i; }\n\n"
|
||||
"int x= 0;"
|
||||
"}";
|
||||
tooling::Replacements Replaces = {
|
||||
createReplacement(getOffset(Code, 3, 3), 6, ""),
|
||||
createReplacement(getOffset(Code, 9, 34), 6, "")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createReplacement(getOffset(Code, 3, 3), 6, ""),
|
||||
createReplacement(getOffset(Code, 9, 34), 6, "")});
|
||||
|
||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||
}
|
||||
|
@ -315,7 +319,8 @@ TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithoutDefine) {
|
|||
std::string Code = "int main() {}";
|
||||
std::string Expected = "#include \"a.h\"\n"
|
||||
"int main() {}";
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"a.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include \"a.h\"")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -332,7 +337,8 @@ TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithDefine) {
|
|||
"#define MMM 123\n"
|
||||
"#endif";
|
||||
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"b.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include \"b.h\"")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -357,7 +363,8 @@ TEST_F(CleanUpReplacementsTest, InsertBeforeCategoryWithLowerPriority) {
|
|||
"#define MMM 123\n"
|
||||
"#endif";
|
||||
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"a.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include \"a.h\"")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -369,7 +376,8 @@ TEST_F(CleanUpReplacementsTest, InsertAfterMainHeader) {
|
|||
"#include <a>\n"
|
||||
"\n"
|
||||
"int main() {}";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <a>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <a>")});
|
||||
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
@ -382,7 +390,8 @@ TEST_F(CleanUpReplacementsTest, InsertBeforeSystemHeaderLLVM) {
|
|||
"#include <memory>\n"
|
||||
"\n"
|
||||
"int main() {}";
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"z.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include \"z.h\"")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -394,7 +403,8 @@ TEST_F(CleanUpReplacementsTest, InsertAfterSystemHeaderGoogle) {
|
|||
"#include \"z.h\"\n"
|
||||
"\n"
|
||||
"int main() {}";
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"z.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include \"z.h\"")});
|
||||
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
@ -412,8 +422,9 @@ TEST_F(CleanUpReplacementsTest, InsertOneIncludeLLVMStyle) {
|
|||
"#include \"clang/Format/Format.h\"\n"
|
||||
"#include \"llvm/x/y.h\"\n"
|
||||
"#include <memory>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"d.h\""),
|
||||
createInsertion("#include \"llvm/x/y.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include \"d.h\""),
|
||||
createInsertion("#include \"llvm/x/y.h\"")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -430,8 +441,9 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesLLVMStyle) {
|
|||
"#include \"clang/Format/Format.h\"\n"
|
||||
"#include <memory>\n"
|
||||
"#include <list>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <list>"),
|
||||
createInsertion("#include \"new/new.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <list>"),
|
||||
createInsertion("#include \"new/new.h\"")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -447,7 +459,8 @@ TEST_F(CleanUpReplacementsTest, InsertNewSystemIncludeGoogleStyle) {
|
|||
"\n"
|
||||
"#include \"y/a.h\"\n"
|
||||
"#include \"z/b.h\"\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
@ -467,8 +480,9 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesGoogleStyle) {
|
|||
"#include \"y/a.h\"\n"
|
||||
"#include \"z/b.h\"\n"
|
||||
"#include \"x/x.h\"\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <list>"),
|
||||
createInsertion("#include \"x/x.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <list>"),
|
||||
createInsertion("#include \"x/x.h\"")});
|
||||
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
@ -482,12 +496,11 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortLLVM) {
|
|||
"#include <list>\n"
|
||||
"#include <vector>\n"
|
||||
"int x;";
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"a.h\""),
|
||||
createInsertion("#include \"c.h\""),
|
||||
createInsertion("#include \"b.h\""),
|
||||
createInsertion("#include <vector>"),
|
||||
createInsertion("#include <list>"),
|
||||
createInsertion("#include \"fix.h\"")};
|
||||
tooling::Replacements Replaces = toReplacements(
|
||||
{createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
|
||||
createInsertion("#include \"b.h\""),
|
||||
createInsertion("#include <vector>"), createInsertion("#include <list>"),
|
||||
createInsertion("#include \"fix.h\"")});
|
||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -500,12 +513,11 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) {
|
|||
"#include \"b.h\"\n"
|
||||
"#include \"c.h\"\n"
|
||||
"int x;";
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"a.h\""),
|
||||
createInsertion("#include \"c.h\""),
|
||||
createInsertion("#include \"b.h\""),
|
||||
createInsertion("#include <vector>"),
|
||||
createInsertion("#include <list>"),
|
||||
createInsertion("#include \"fix.h\"")};
|
||||
tooling::Replacements Replaces = toReplacements(
|
||||
{createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
|
||||
createInsertion("#include \"b.h\""),
|
||||
createInsertion("#include <vector>"), createInsertion("#include <list>"),
|
||||
createInsertion("#include \"fix.h\"")});
|
||||
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||
}
|
||||
|
@ -526,13 +538,12 @@ TEST_F(CleanUpReplacementsTest, FormatCorrectLineWhenHeadersAreInserted) {
|
|||
"int a;\n"
|
||||
"int b;\n"
|
||||
"int a;";
|
||||
tooling::Replacements Replaces = {
|
||||
createReplacement(getOffset(Code, 4, 8), 1, "b"),
|
||||
createInsertion("#include <vector>"),
|
||||
createInsertion("#include <list>"),
|
||||
createInsertion("#include \"clang/x/x.h\""),
|
||||
createInsertion("#include \"y.h\""),
|
||||
createInsertion("#include \"x.h\"")};
|
||||
tooling::Replacements Replaces = toReplacements(
|
||||
{createReplacement(getOffset(Code, 4, 8), 1, "b"),
|
||||
createInsertion("#include <vector>"), createInsertion("#include <list>"),
|
||||
createInsertion("#include \"clang/x/x.h\""),
|
||||
createInsertion("#include \"y.h\""),
|
||||
createInsertion("#include \"x.h\"")});
|
||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -544,7 +555,8 @@ TEST_F(CleanUpReplacementsTest, NotConfusedByDefine) {
|
|||
"void f() {}\n"
|
||||
"#define A \\\n"
|
||||
" int i;";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -556,7 +568,8 @@ TEST_F(CleanUpReplacementsTest, SkippedTopComment) {
|
|||
"\n"
|
||||
" // comment\n"
|
||||
"#include <vector>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -574,7 +587,8 @@ TEST_F(CleanUpReplacementsTest, SkippedMixedComments) {
|
|||
"* comment\n"
|
||||
"*/\n"
|
||||
"#include <vector>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -592,7 +606,8 @@ TEST_F(CleanUpReplacementsTest, MultipleBlockCommentsInOneLine) {
|
|||
"\n\n"
|
||||
"/* c1 */ /*c2 */\n"
|
||||
"#include <vector>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -614,7 +629,8 @@ TEST_F(CleanUpReplacementsTest, CodeAfterComments) {
|
|||
"\n"
|
||||
"#include <vector>\n"
|
||||
"int x;\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -626,7 +642,8 @@ TEST_F(CleanUpReplacementsTest, FakeHeaderGuardIfDef) {
|
|||
"#include <vector>\n"
|
||||
"#ifdef X\n"
|
||||
"#define X\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -642,7 +659,8 @@ TEST_F(CleanUpReplacementsTest, RealHeaderGuardAfterComments) {
|
|||
"#include <vector>\n"
|
||||
"int x;\n"
|
||||
"#define Y 1\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -656,7 +674,8 @@ TEST_F(CleanUpReplacementsTest, IfNDefWithNoDefine) {
|
|||
"#ifndef X\n"
|
||||
"int x;\n"
|
||||
"#define Y 1\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -678,14 +697,16 @@ TEST_F(CleanUpReplacementsTest, HeaderGuardWithComment) {
|
|||
"#include <vector>\n"
|
||||
"int x;\n"
|
||||
"#define Y 1\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
TEST_F(CleanUpReplacementsTest, EmptyCode) {
|
||||
std::string Code = "";
|
||||
std::string Expected = "#include <vector>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -694,7 +715,8 @@ TEST_F(CleanUpReplacementsTest, EmptyCode) {
|
|||
TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCode) {
|
||||
std::string Code = "#include <map>";
|
||||
std::string Expected = "#include <map>#include <vector>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -703,8 +725,9 @@ TEST_F(CleanUpReplacementsTest, SkipExistingHeaders) {
|
|||
"#include <vector>\n";
|
||||
std::string Expected = "#include \"a.h\"\n"
|
||||
"#include <vector>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include <vector>"),
|
||||
createInsertion("#include \"a.h\"")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include <vector>"),
|
||||
createInsertion("#include \"a.h\"")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
@ -716,8 +739,9 @@ TEST_F(CleanUpReplacementsTest, AddIncludesWithDifferentForms) {
|
|||
"#include \"vector\"\n"
|
||||
"#include <vector>\n"
|
||||
"#include <a.h>\n";
|
||||
tooling::Replacements Replaces = {createInsertion("#include \"vector\""),
|
||||
createInsertion("#include <a.h>")};
|
||||
tooling::Replacements Replaces =
|
||||
toReplacements({createInsertion("#include \"vector\""),
|
||||
createInsertion("#include <a.h>")});
|
||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "clang/Format/Format.h"
|
||||
|
||||
#include "../Tooling/RewriterTestContext.h"
|
||||
#include "../Tooling/ReplacementTest.h"
|
||||
#include "FormatTestUtils.h"
|
||||
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
|
@ -19,6 +19,9 @@
|
|||
|
||||
#define DEBUG_TYPE "format-test"
|
||||
|
||||
using clang::tooling::ReplacementTest;
|
||||
using clang::tooling::toReplacements;
|
||||
|
||||
namespace clang {
|
||||
namespace format {
|
||||
namespace {
|
||||
|
@ -11520,17 +11523,6 @@ TEST(FormatStyle, GetStyleOfFile) {
|
|||
|
||||
#endif // _MSC_VER
|
||||
|
||||
class ReplacementTest : public ::testing::Test {
|
||||
protected:
|
||||
tooling::Replacement createReplacement(SourceLocation Start, unsigned Length,
|
||||
llvm::StringRef ReplacementText) {
|
||||
return tooling::Replacement(Context.Sources, Start, Length,
|
||||
ReplacementText);
|
||||
}
|
||||
|
||||
RewriterTestContext Context;
|
||||
};
|
||||
|
||||
TEST_F(ReplacementTest, FormatCodeAfterReplacements) {
|
||||
// Column limit is 20.
|
||||
std::string Code = "Type *a =\n"
|
||||
|
@ -11545,15 +11537,15 @@ TEST_F(ReplacementTest, FormatCodeAfterReplacements) {
|
|||
" mm);\n"
|
||||
"int bad = format ;";
|
||||
FileID ID = Context.createInMemoryFile("format.cpp", Code);
|
||||
tooling::Replacements Replaces;
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID, 1, 1), 6, "auto "));
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID, 3, 10), 1, "nullptr"));
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID, 4, 3), 1, "nullptr"));
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID, 4, 13), 1, "nullptr"));
|
||||
tooling::Replacements Replaces = toReplacements(
|
||||
{tooling::Replacement(Context.Sources, Context.getLocation(ID, 1, 1), 6,
|
||||
"auto "),
|
||||
tooling::Replacement(Context.Sources, Context.getLocation(ID, 3, 10), 1,
|
||||
"nullptr"),
|
||||
tooling::Replacement(Context.Sources, Context.getLocation(ID, 4, 3), 1,
|
||||
"nullptr"),
|
||||
tooling::Replacement(Context.Sources, Context.getLocation(ID, 4, 13), 1,
|
||||
"nullptr")});
|
||||
|
||||
format::FormatStyle Style = format::getLLVMStyle();
|
||||
Style.ColumnLimit = 20; // Set column limit to 20 to increase readibility.
|
||||
|
@ -11580,9 +11572,9 @@ TEST_F(ReplacementTest, SortIncludesAfterReplacement) {
|
|||
" return 0;\n"
|
||||
"}";
|
||||
FileID ID = Context.createInMemoryFile("fix.cpp", Code);
|
||||
tooling::Replacements Replaces;
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID, 1, 1), 0, "#include \"b.h\"\n"));
|
||||
tooling::Replacements Replaces = toReplacements(
|
||||
{tooling::Replacement(Context.Sources, Context.getLocation(ID, 1, 1), 0,
|
||||
"#include \"b.h\"\n")});
|
||||
|
||||
format::FormatStyle Style = format::getLLVMStyle();
|
||||
Style.SortIncludes = true;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ReplacementTest.h"
|
||||
#include "RewriterTestContext.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
@ -31,16 +32,6 @@
|
|||
namespace clang {
|
||||
namespace tooling {
|
||||
|
||||
class ReplacementTest : public ::testing::Test {
|
||||
protected:
|
||||
Replacement createReplacement(SourceLocation Start, unsigned Length,
|
||||
llvm::StringRef ReplacementText) {
|
||||
return Replacement(Context.Sources, Start, Length, ReplacementText);
|
||||
}
|
||||
|
||||
RewriterTestContext Context;
|
||||
};
|
||||
|
||||
TEST_F(ReplacementTest, CanDeleteAllText) {
|
||||
FileID ID = Context.createInMemoryFile("input.cpp", "text");
|
||||
SourceLocation Location = Context.getLocation(ID, 1, 1);
|
||||
|
@ -108,29 +99,30 @@ TEST_F(ReplacementTest, ReturnsInvalidPath) {
|
|||
EXPECT_TRUE(Replace2.getFilePath().empty());
|
||||
}
|
||||
|
||||
TEST_F(ReplacementTest, FailAddReplacements) {
|
||||
Replacements Replaces;
|
||||
auto Err = Replaces.add(Replacement("x.cc", 0, 10, "3"));
|
||||
EXPECT_TRUE(!Err);
|
||||
llvm::consumeError(std::move(Err));
|
||||
Err = Replaces.add(Replacement("x.cc", 0, 2, ""));
|
||||
EXPECT_TRUE((bool)Err);
|
||||
llvm::consumeError(std::move(Err));
|
||||
Err = Replaces.add(Replacement("x.cc", 2, 2, ""));
|
||||
EXPECT_TRUE((bool)Err);
|
||||
llvm::consumeError(std::move(Err));
|
||||
Err = Replaces.add(Replacement("y.cc", 20, 2, ""));
|
||||
EXPECT_TRUE((bool)Err);
|
||||
llvm::consumeError(std::move(Err));
|
||||
}
|
||||
|
||||
TEST_F(ReplacementTest, CanApplyReplacements) {
|
||||
FileID ID = Context.createInMemoryFile("input.cpp",
|
||||
"line1\nline2\nline3\nline4");
|
||||
Replacements Replaces;
|
||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
||||
5, "replaced"));
|
||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 3, 1),
|
||||
5, "other"));
|
||||
EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||
EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
|
||||
}
|
||||
|
||||
// FIXME: Remove this test case when Replacements is implemented as std::vector
|
||||
// instead of std::set. The other ReplacementTest tests will need to be updated
|
||||
// at that point as well.
|
||||
TEST_F(ReplacementTest, VectorCanApplyReplacements) {
|
||||
FileID ID = Context.createInMemoryFile("input.cpp",
|
||||
"line1\nline2\nline3\nline4");
|
||||
std::vector<Replacement> Replaces;
|
||||
Replaces.push_back(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
||||
5, "replaced"));
|
||||
Replaces.push_back(
|
||||
Replacement(Context.Sources, Context.getLocation(ID, 3, 1), 5, "other"));
|
||||
Replacements Replaces =
|
||||
toReplacements({Replacement(Context.Sources,
|
||||
Context.getLocation(ID, 2, 1), 5, "replaced"),
|
||||
Replacement(Context.Sources,
|
||||
Context.getLocation(ID, 3, 1), 5, "other")});
|
||||
EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||
EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
|
||||
}
|
||||
|
@ -138,32 +130,28 @@ TEST_F(ReplacementTest, VectorCanApplyReplacements) {
|
|||
TEST_F(ReplacementTest, SkipsDuplicateReplacements) {
|
||||
FileID ID = Context.createInMemoryFile("input.cpp",
|
||||
"line1\nline2\nline3\nline4");
|
||||
Replacements Replaces;
|
||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
||||
5, "replaced"));
|
||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
||||
5, "replaced"));
|
||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
||||
5, "replaced"));
|
||||
auto Replaces = toReplacements({Replacement(
|
||||
Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")});
|
||||
|
||||
auto Err = Replaces.add(Replacement(
|
||||
Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced"));
|
||||
EXPECT_TRUE((bool)Err);
|
||||
llvm::consumeError(std::move(Err));
|
||||
|
||||
Err = Replaces.add(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
||||
5, "replaced"));
|
||||
EXPECT_TRUE((bool)Err);
|
||||
llvm::consumeError(std::move(Err));
|
||||
|
||||
EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||
EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID));
|
||||
}
|
||||
|
||||
TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) {
|
||||
// This test depends on the value of the file name of an invalid source
|
||||
// location being in the range ]a, z[.
|
||||
FileID IDa = Context.createInMemoryFile("a.cpp", "text");
|
||||
FileID IDz = Context.createInMemoryFile("z.cpp", "text");
|
||||
Replacements Replaces;
|
||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDa, 1, 1),
|
||||
4, "a"));
|
||||
Replaces.insert(Replacement(Context.Sources, SourceLocation(),
|
||||
5, "2"));
|
||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDz, 1, 1),
|
||||
4, "z"));
|
||||
TEST_F(ReplacementTest, InvalidSourceLocationFailsApplyAll) {
|
||||
Replacements Replaces =
|
||||
toReplacements({Replacement(Context.Sources, SourceLocation(), 5, "2")});
|
||||
|
||||
EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||
EXPECT_EQ("a", Context.getRewrittenText(IDa));
|
||||
EXPECT_EQ("z", Context.getRewrittenText(IDz));
|
||||
}
|
||||
|
||||
TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
|
||||
|
@ -179,76 +167,66 @@ TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
|
|||
std::string Expected2 = "int x =\n"
|
||||
" 1234567890123;\n"
|
||||
"int y = 10;";
|
||||
FileID ID1 = Context.createInMemoryFile("format_1.cpp", Code1);
|
||||
FileID ID2 = Context.createInMemoryFile("format_2.cpp", Code2);
|
||||
StringRef File1 = "format_1.cpp";
|
||||
StringRef File2 = "format_2.cpp";
|
||||
FileID ID1 = Context.createInMemoryFile(File1, Code1);
|
||||
FileID ID2 = Context.createInMemoryFile(File2, Code2);
|
||||
|
||||
tooling::Replacements Replaces;
|
||||
// Scrambled the order of replacements.
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID2, 1, 12), 0, "4567890123"));
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID1, 1, 1), 6, "auto "));
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID2, 2, 9), 1, "10"));
|
||||
Replaces.insert(tooling::Replacement(
|
||||
Context.Sources, Context.getLocation(ID1, 3, 10), 1, "12345678901"));
|
||||
|
||||
EXPECT_TRUE(formatAndApplyAllReplacements(
|
||||
Replaces, Context.Rewrite, "{BasedOnStyle: LLVM, ColumnLimit: 20}"));
|
||||
std::map<std::string, Replacements> FileToReplaces;
|
||||
FileToReplaces[File1] = toReplacements(
|
||||
{tooling::Replacement(Context.Sources, Context.getLocation(ID1, 1, 1), 6,
|
||||
"auto "),
|
||||
tooling::Replacement(Context.Sources, Context.getLocation(ID1, 3, 10), 1,
|
||||
"12345678901")});
|
||||
FileToReplaces[File2] = toReplacements(
|
||||
{tooling::Replacement(Context.Sources, Context.getLocation(ID2, 1, 12), 0,
|
||||
"4567890123"),
|
||||
tooling::Replacement(Context.Sources, Context.getLocation(ID2, 2, 9), 1,
|
||||
"10")});
|
||||
EXPECT_TRUE(
|
||||
formatAndApplyAllReplacements(FileToReplaces, Context.Rewrite,
|
||||
"{BasedOnStyle: LLVM, ColumnLimit: 20}"));
|
||||
EXPECT_EQ(Expected1, Context.getRewrittenText(ID1));
|
||||
EXPECT_EQ(Expected2, Context.getRewrittenText(ID2));
|
||||
}
|
||||
|
||||
TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
|
||||
Replacements Replaces;
|
||||
Replaces.insert(Replacement("", 0, 1, ""));
|
||||
Replaces.insert(Replacement("", 4, 3, " "));
|
||||
Replacements Replaces =
|
||||
toReplacements({Replacement("", 0, 1, ""), Replacement("", 4, 3, " ")});
|
||||
// Assume ' int i;' is turned into 'int i;' and cursor is located at '|'.
|
||||
EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int i;
|
||||
EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); // |nt i;
|
||||
EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); // i|t i;
|
||||
EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); // in| i;
|
||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); // int| i;
|
||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 5)); // int | i;
|
||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 6)); // int |i;
|
||||
EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); // int |;
|
||||
EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); // int i|
|
||||
}
|
||||
|
||||
// FIXME: Remove this test case when Replacements is implemented as std::vector
|
||||
// instead of std::set. The other ReplacementTest tests will need to be updated
|
||||
// at that point as well.
|
||||
TEST(ShiftedCodePositionTest, VectorFindsNewCodePositionWithInserts) {
|
||||
std::vector<Replacement> Replaces;
|
||||
Replaces.push_back(Replacement("", 0, 1, ""));
|
||||
Replaces.push_back(Replacement("", 4, 3, " "));
|
||||
// Assume ' int i;' is turned into 'int i;' and cursor is located at '|'.
|
||||
EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int i;
|
||||
EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); // |nt i;
|
||||
EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); // i|t i;
|
||||
EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); // in| i;
|
||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); // int| i;
|
||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 5)); // int | i;
|
||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 6)); // int |i;
|
||||
EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); // int |;
|
||||
EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); // int i|
|
||||
EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0)); // |int i;
|
||||
EXPECT_EQ(0u, Replaces.getShiftedCodePosition(1)); // |nt i;
|
||||
EXPECT_EQ(1u, Replaces.getShiftedCodePosition(2)); // i|t i;
|
||||
EXPECT_EQ(2u, Replaces.getShiftedCodePosition(3)); // in| i;
|
||||
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(4)); // int| i;
|
||||
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(5)); // int | i;
|
||||
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(6)); // int |i;
|
||||
EXPECT_EQ(4u, Replaces.getShiftedCodePosition(7)); // int |;
|
||||
EXPECT_EQ(5u, Replaces.getShiftedCodePosition(8)); // int i|
|
||||
}
|
||||
|
||||
TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) {
|
||||
Replacements Replaces;
|
||||
Replaces.insert(Replacement("", 4, 0, "\"\n\""));
|
||||
Replacements Replaces = toReplacements({Replacement("", 4, 0, "\"\n\"")});
|
||||
// Assume '"12345678"' is turned into '"1234"\n"5678"'.
|
||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 3)); // "123|5678"
|
||||
EXPECT_EQ(7u, shiftedCodePosition(Replaces, 4)); // "1234|678"
|
||||
EXPECT_EQ(8u, shiftedCodePosition(Replaces, 5)); // "12345|78"
|
||||
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(3)); // "123|5678"
|
||||
EXPECT_EQ(7u, Replaces.getShiftedCodePosition(4)); // "1234|678"
|
||||
EXPECT_EQ(8u, Replaces.getShiftedCodePosition(5)); // "12345|78"
|
||||
}
|
||||
|
||||
TEST(ShiftedCodePositionTest, FindsNewCodePositionInReplacedText) {
|
||||
Replacements Replaces;
|
||||
// Replace the first four characters with "abcd".
|
||||
Replaces.insert(Replacement("", 0, 4, "abcd"));
|
||||
auto Replaces = toReplacements({Replacement("", 0, 4, "abcd")});
|
||||
for (unsigned i = 0; i < 3; ++i)
|
||||
EXPECT_EQ(i, shiftedCodePosition(Replaces, i));
|
||||
EXPECT_EQ(i, Replaces.getShiftedCodePosition(i));
|
||||
}
|
||||
|
||||
TEST(ShiftedCodePositionTest, NoReplacementText) {
|
||||
Replacements Replaces = toReplacements({Replacement("", 0, 42, "")});
|
||||
EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0));
|
||||
EXPECT_EQ(0u, Replaces.getShiftedCodePosition(39));
|
||||
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(45));
|
||||
EXPECT_EQ(0u, Replaces.getShiftedCodePosition(42));
|
||||
}
|
||||
|
||||
class FlushRewrittenFilesTest : public ::testing::Test {
|
||||
|
@ -304,9 +282,8 @@ public:
|
|||
|
||||
TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) {
|
||||
FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4");
|
||||
Replacements Replaces;
|
||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
||||
5, "replaced"));
|
||||
Replacements Replaces = toReplacements({Replacement(
|
||||
Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")});
|
||||
EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||
EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
|
||||
EXPECT_EQ("line1\nreplaced\nline3\nline4",
|
||||
|
@ -454,12 +431,11 @@ TEST(Range, contains) {
|
|||
TEST(Range, CalculateRangesOfReplacements) {
|
||||
// Before: aaaabbbbbbz
|
||||
// After : bbbbbbzzzzzzoooooooooooooooo
|
||||
Replacements Replaces;
|
||||
Replaces.insert(Replacement("foo", 0, 4, ""));
|
||||
Replaces.insert(Replacement("foo", 10, 1, "zzzzzz"));
|
||||
Replaces.insert(Replacement("foo", 11, 0, "oooooooooooooooo"));
|
||||
Replacements Replaces = toReplacements(
|
||||
{Replacement("foo", 0, 4, ""), Replacement("foo", 10, 1, "zzzzzz"),
|
||||
Replacement("foo", 11, 0, "oooooooooooooooo")});
|
||||
|
||||
std::vector<Range> Ranges = calculateChangedRanges(Replaces);
|
||||
std::vector<Range> Ranges = Replaces.getAffectedRanges();
|
||||
|
||||
EXPECT_EQ(2ul, Ranges.size());
|
||||
EXPECT_TRUE(Ranges[0].getOffset() == 0);
|
||||
|
@ -470,23 +446,23 @@ TEST(Range, CalculateRangesOfReplacements) {
|
|||
|
||||
TEST(Range, RangesAfterReplacements) {
|
||||
std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
|
||||
Replacements Replaces = {Replacement("foo", 0, 2, "1234")};
|
||||
Replacements Replaces = toReplacements({Replacement("foo", 0, 2, "1234")});
|
||||
std::vector<Range> Expected = {Range(0, 4), Range(7, 2), Range(12, 5)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
}
|
||||
|
||||
TEST(Range, RangesBeforeReplacements) {
|
||||
std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
|
||||
Replacements Replaces = {Replacement("foo", 20, 2, "1234")};
|
||||
Replacements Replaces = toReplacements({Replacement("foo", 20, 2, "1234")});
|
||||
std::vector<Range> Expected = {Range(5, 2), Range(10, 5), Range(20, 4)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
}
|
||||
|
||||
TEST(Range, NotAffectedByReplacements) {
|
||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
|
||||
Replacements Replaces = {Replacement("foo", 3, 2, "12"),
|
||||
Replacement("foo", 12, 2, "12"),
|
||||
Replacement("foo", 20, 5, "")};
|
||||
Replacements Replaces = toReplacements({Replacement("foo", 3, 2, "12"),
|
||||
Replacement("foo", 12, 2, "12"),
|
||||
Replacement("foo", 20, 5, "")});
|
||||
std::vector<Range> Expected = {Range(0, 2), Range(3, 4), Range(10, 5),
|
||||
Range(20, 0)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
|
@ -494,9 +470,9 @@ TEST(Range, NotAffectedByReplacements) {
|
|||
|
||||
TEST(Range, RangesWithNonOverlappingReplacements) {
|
||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
|
||||
Replacements Replaces = {Replacement("foo", 3, 1, ""),
|
||||
Replacement("foo", 6, 1, "123"),
|
||||
Replacement("foo", 20, 2, "12345")};
|
||||
Replacements Replaces = toReplacements({Replacement("foo", 3, 1, ""),
|
||||
Replacement("foo", 6, 1, "123"),
|
||||
Replacement("foo", 20, 2, "12345")});
|
||||
std::vector<Range> Expected = {Range(0, 2), Range(3, 0), Range(4, 4),
|
||||
Range(11, 5), Range(21, 5)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
|
@ -505,9 +481,9 @@ TEST(Range, RangesWithNonOverlappingReplacements) {
|
|||
TEST(Range, RangesWithOverlappingReplacements) {
|
||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5),
|
||||
Range(30, 5)};
|
||||
Replacements Replaces = {
|
||||
Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
|
||||
Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")};
|
||||
Replacements Replaces = toReplacements(
|
||||
{Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
|
||||
Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")});
|
||||
std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(12, 5),
|
||||
Range(22, 0)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
|
@ -515,122 +491,52 @@ TEST(Range, RangesWithOverlappingReplacements) {
|
|||
|
||||
TEST(Range, MergeIntoOneRange) {
|
||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
|
||||
Replacements Replaces = {Replacement("foo", 1, 15, "1234567890")};
|
||||
Replacements Replaces =
|
||||
toReplacements({Replacement("foo", 1, 15, "1234567890")});
|
||||
std::vector<Range> Expected = {Range(0, 15)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
}
|
||||
|
||||
TEST(Range, ReplacementsStartingAtRangeOffsets) {
|
||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 5), Range(15, 5)};
|
||||
Replacements Replaces = {
|
||||
Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
|
||||
Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")};
|
||||
Replacements Replaces = toReplacements(
|
||||
{Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
|
||||
Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")});
|
||||
std::vector<Range> Expected = {Range(0, 2), Range(5, 9), Range(18, 2)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
}
|
||||
|
||||
TEST(Range, ReplacementsEndingAtRangeEnds) {
|
||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
|
||||
Replacements Replaces = {Replacement("foo", 6, 1, "123"),
|
||||
Replacement("foo", 17, 3, "12")};
|
||||
Replacements Replaces = toReplacements(
|
||||
{Replacement("foo", 6, 1, "123"), Replacement("foo", 17, 3, "12")});
|
||||
std::vector<Range> Expected = {Range(0, 2), Range(5, 4), Range(17, 4)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
}
|
||||
|
||||
TEST(Range, AjacentReplacements) {
|
||||
std::vector<Range> Ranges = {Range(0, 0), Range(15, 5)};
|
||||
Replacements Replaces = {Replacement("foo", 1, 2, "123"),
|
||||
Replacement("foo", 12, 3, "1234")};
|
||||
Replacements Replaces = toReplacements(
|
||||
{Replacement("foo", 1, 2, "123"), Replacement("foo", 12, 3, "1234")});
|
||||
std::vector<Range> Expected = {Range(0, 0), Range(1, 3), Range(13, 9)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
}
|
||||
|
||||
TEST(Range, MergeRangesAfterReplacements) {
|
||||
std::vector<Range> Ranges = {Range(8, 0), Range(5, 2), Range(9, 0), Range(0, 1)};
|
||||
Replacements Replaces = {Replacement("foo", 1, 3, ""),
|
||||
Replacement("foo", 7, 0, "12"), Replacement("foo", 9, 2, "")};
|
||||
std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0), Range(8, 0)};
|
||||
Replacements Replaces = toReplacements({Replacement("foo", 1, 3, ""),
|
||||
Replacement("foo", 7, 0, "12"),
|
||||
Replacement("foo", 9, 2, "")});
|
||||
std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0),
|
||||
Range(8, 0)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
}
|
||||
|
||||
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("fileB", 60, 1, " bar "));
|
||||
Input.push_back(Replacement("fileA", 60, 2, " bar "));
|
||||
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", 60, 1, " bar "));
|
||||
Expected.push_back(Replacement("fileA", 60, 2, " bar "));
|
||||
|
||||
std::vector<Range> Conflicts; // Ignored for this test
|
||||
deduplicate(Input, Conflicts);
|
||||
|
||||
EXPECT_EQ(3U, Conflicts.size());
|
||||
EXPECT_EQ(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", 6, 0, " bar ")); // 3
|
||||
Input.push_back(Replacement("fileA", 5, 5, " moo ")); // 2
|
||||
Input.push_back(Replacement("fileA", 7, 2, " bar ")); // 4
|
||||
Input.push_back(Replacement("fileA", 15, 5, " golf ")); // 5
|
||||
Input.push_back(Replacement("fileA", 16, 5, " bag ")); // 6
|
||||
Input.push_back(Replacement("fileA", 10, 3, " club ")); // 7
|
||||
|
||||
// #3 is special in that it is completely contained by another conflicting
|
||||
// Replacement. #4 ensures #3 hasn't messed up the conflicting range size.
|
||||
|
||||
std::vector<Range> Conflicts;
|
||||
deduplicate(Input, Conflicts);
|
||||
|
||||
// No duplicates
|
||||
ASSERT_EQ(8u, Input.size());
|
||||
ASSERT_EQ(2u, Conflicts.size());
|
||||
ASSERT_EQ(1u, Conflicts[0].getOffset());
|
||||
ASSERT_EQ(4u, Conflicts[0].getLength());
|
||||
ASSERT_EQ(6u, Conflicts[1].getOffset());
|
||||
ASSERT_EQ(2u, Conflicts[1].getLength());
|
||||
}
|
||||
TEST(Range, ConflictingRangesBeforeReplacements) {
|
||||
std::vector<Range> Ranges = {Range(8, 3), Range(5, 4), Range(9, 1)};
|
||||
Replacements Replaces = toReplacements({Replacement("foo", 1, 3, "")});
|
||||
std::vector<Range> Expected = {Range(1, 0), Range(2, 6)};
|
||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||
}
|
||||
|
||||
class MergeReplacementsTest : public ::testing::Test {
|
||||
|
@ -646,7 +552,7 @@ protected:
|
|||
EXPECT_EQ(Intermediate, *AfterFirst);
|
||||
EXPECT_EQ(Result, *InSequenceRewrite);
|
||||
|
||||
tooling::Replacements Merged = mergeReplacements(First, Second);
|
||||
tooling::Replacements Merged = First.merge(Second);
|
||||
auto MergedRewrite = applyAllReplacements(Code, Merged);
|
||||
EXPECT_TRUE(static_cast<bool>(MergedRewrite));
|
||||
EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
|
||||
|
@ -660,7 +566,7 @@ protected:
|
|||
auto AfterFirst = applyAllReplacements(Code, First);
|
||||
EXPECT_TRUE(static_cast<bool>(AfterFirst));
|
||||
auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second);
|
||||
tooling::Replacements Merged = mergeReplacements(First, Second);
|
||||
tooling::Replacements Merged = First.merge(Second);
|
||||
auto MergedRewrite = applyAllReplacements(Code, Merged);
|
||||
EXPECT_TRUE(static_cast<bool>(MergedRewrite));
|
||||
EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
|
||||
|
@ -673,62 +579,82 @@ protected:
|
|||
|
||||
TEST_F(MergeReplacementsTest, Offsets) {
|
||||
mergeAndTestRewrite("aaa", "aabab", "cacabab",
|
||||
{{"", 2, 0, "b"}, {"", 3, 0, "b"}},
|
||||
{{"", 0, 0, "c"}, {"", 1, 0, "c"}});
|
||||
toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
|
||||
toReplacements({{"", 0, 0, "c"}, {"", 1, 0, "c"}}));
|
||||
mergeAndTestRewrite("aaa", "babaa", "babacac",
|
||||
{{"", 0, 0, "b"}, {"", 1, 0, "b"}},
|
||||
{{"", 4, 0, "c"}, {"", 5, 0, "c"}});
|
||||
mergeAndTestRewrite("aaaa", "aaa", "aac", {{"", 1, 1, ""}},
|
||||
{{"", 2, 1, "c"}});
|
||||
toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "b"}}),
|
||||
toReplacements({{"", 4, 0, "c"}, {"", 5, 0, "c"}}));
|
||||
mergeAndTestRewrite("aaaa", "aaa", "aac", toReplacements({{"", 1, 1, ""}}),
|
||||
toReplacements({{"", 2, 1, "c"}}));
|
||||
|
||||
mergeAndTestRewrite("aa", "bbabba", "bbabcba",
|
||||
{{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}, {{"", 4, 0, "c"}});
|
||||
toReplacements({{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}),
|
||||
toReplacements({{"", 4, 0, "c"}}));
|
||||
}
|
||||
|
||||
TEST_F(MergeReplacementsTest, Concatenations) {
|
||||
// Basic concatenations. It is important to merge these into a single
|
||||
// replacement to ensure the correct order.
|
||||
EXPECT_EQ((Replacements{{"", 0, 0, "ab"}}),
|
||||
mergeReplacements({{"", 0, 0, "a"}}, {{"", 1, 0, "b"}}));
|
||||
EXPECT_EQ((Replacements{{"", 0, 0, "ba"}}),
|
||||
mergeReplacements({{"", 0, 0, "a"}}, {{"", 0, 0, "b"}}));
|
||||
mergeAndTestRewrite("", "a", "ab", {{"", 0, 0, "a"}}, {{"", 1, 0, "b"}});
|
||||
mergeAndTestRewrite("", "a", "ba", {{"", 0, 0, "a"}}, {{"", 0, 0, "b"}});
|
||||
{
|
||||
auto First = toReplacements({{"", 0, 0, "a"}});
|
||||
auto Second = toReplacements({{"", 1, 0, "b"}});
|
||||
EXPECT_EQ(toReplacements({{"", 0, 0, "ab"}}), First.merge(Second));
|
||||
}
|
||||
{
|
||||
auto First = toReplacements({{"", 0, 0, "a"}});
|
||||
auto Second = toReplacements({{"", 0, 0, "b"}});
|
||||
EXPECT_EQ(toReplacements({{"", 0, 0, "ba"}}), First.merge(Second));
|
||||
}
|
||||
mergeAndTestRewrite("", "a", "ab", toReplacements({{"", 0, 0, "a"}}),
|
||||
toReplacements({{"", 1, 0, "b"}}));
|
||||
mergeAndTestRewrite("", "a", "ba", toReplacements({{"", 0, 0, "a"}}),
|
||||
toReplacements({{"", 0, 0, "b"}}));
|
||||
}
|
||||
|
||||
TEST_F(MergeReplacementsTest, NotChangingLengths) {
|
||||
mergeAndTestRewrite("aaaa", "abba", "acca", {{"", 1, 2, "bb"}},
|
||||
{{"", 1, 2, "cc"}});
|
||||
mergeAndTestRewrite("aaaa", "abba", "abcc", {{"", 1, 2, "bb"}},
|
||||
{{"", 2, 2, "cc"}});
|
||||
mergeAndTestRewrite("aaaa", "abba", "ccba", {{"", 1, 2, "bb"}},
|
||||
{{"", 0, 2, "cc"}});
|
||||
mergeAndTestRewrite("aaaa", "abba", "acca",
|
||||
toReplacements({{"", 1, 2, "bb"}}),
|
||||
toReplacements({{"", 1, 2, "cc"}}));
|
||||
mergeAndTestRewrite("aaaa", "abba", "abcc",
|
||||
toReplacements({{"", 1, 2, "bb"}}),
|
||||
toReplacements({{"", 2, 2, "cc"}}));
|
||||
mergeAndTestRewrite("aaaa", "abba", "ccba",
|
||||
toReplacements({{"", 1, 2, "bb"}}),
|
||||
toReplacements({{"", 0, 2, "cc"}}));
|
||||
mergeAndTestRewrite("aaaaaa", "abbdda", "abccda",
|
||||
{{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}, {{"", 2, 2, "cc"}});
|
||||
toReplacements({{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}),
|
||||
toReplacements({{"", 2, 2, "cc"}}));
|
||||
}
|
||||
|
||||
TEST_F(MergeReplacementsTest, OverlappingRanges) {
|
||||
mergeAndTestRewrite("aaa", "bbd", "bcbcd",
|
||||
{{"", 0, 1, "bb"}, {"", 1, 2, "d"}},
|
||||
{{"", 1, 0, "c"}, {"", 2, 0, "c"}});
|
||||
toReplacements({{"", 0, 1, "bb"}, {"", 1, 2, "d"}}),
|
||||
toReplacements({{"", 1, 0, "c"}, {"", 2, 0, "c"}}));
|
||||
|
||||
mergeAndTestRewrite("aaaa", "aabbaa", "acccca", {{"", 2, 0, "bb"}},
|
||||
{{"", 1, 4, "cccc"}});
|
||||
mergeAndTestRewrite("aaaa", "aabbaa", "acccca",
|
||||
toReplacements({{"", 2, 0, "bb"}}),
|
||||
toReplacements({{"", 1, 4, "cccc"}}));
|
||||
mergeAndTestRewrite("aaaa", "aababa", "acccca",
|
||||
{{"", 2, 0, "b"}, {"", 3, 0, "b"}}, {{"", 1, 4, "cccc"}});
|
||||
mergeAndTestRewrite("aaaaaa", "abbbba", "abba", {{"", 1, 4, "bbbb"}},
|
||||
{{"", 2, 2, ""}});
|
||||
mergeAndTestRewrite("aaaa", "aa", "cc", {{"", 1, 1, ""}, {"", 2, 1, ""}},
|
||||
{{"", 0, 2, "cc"}});
|
||||
mergeAndTestRewrite("aa", "abbba", "abcbcba", {{"", 1, 0, "bbb"}},
|
||||
{{"", 2, 0, "c"}, {"", 3, 0, "c"}});
|
||||
toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
|
||||
toReplacements({{"", 1, 4, "cccc"}}));
|
||||
mergeAndTestRewrite("aaaaaa", "abbbba", "abba",
|
||||
toReplacements({{"", 1, 4, "bbbb"}}),
|
||||
toReplacements({{"", 2, 2, ""}}));
|
||||
mergeAndTestRewrite("aaaa", "aa", "cc",
|
||||
toReplacements({{"", 1, 1, ""}, {"", 2, 1, ""}}),
|
||||
toReplacements({{"", 0, 2, "cc"}}));
|
||||
mergeAndTestRewrite("aa", "abbba", "abcbcba",
|
||||
toReplacements({{"", 1, 0, "bbb"}}),
|
||||
toReplacements({{"", 2, 0, "c"}, {"", 3, 0, "c"}}));
|
||||
|
||||
mergeAndTestRewrite("aaa", "abbab", "ccdd",
|
||||
{{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}},
|
||||
{{"", 0, 2, "cc"}, {"", 2, 3, "dd"}});
|
||||
mergeAndTestRewrite("aa", "babbab", "ccdd",
|
||||
{{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}},
|
||||
{{"", 0, 3, "cc"}, {"", 3, 3, "dd"}});
|
||||
mergeAndTestRewrite(
|
||||
"aaa", "abbab", "ccdd",
|
||||
toReplacements({{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}}),
|
||||
toReplacements({{"", 0, 2, "cc"}, {"", 2, 3, "dd"}}));
|
||||
mergeAndTestRewrite(
|
||||
"aa", "babbab", "ccdd",
|
||||
toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}}),
|
||||
toReplacements({{"", 0, 3, "cc"}, {"", 3, 3, "dd"}}));
|
||||
}
|
||||
|
||||
} // end namespace tooling
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
//===- unittest/Tooling/ReplacementTest.h - Replacements related test------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines utility class and function for Replacement related tests.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H
|
||||
#define LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H
|
||||
|
||||
#include "RewriterTestContext.h"
|
||||
#include "clang/Tooling/Core/Replacement.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tooling {
|
||||
|
||||
/// \brief Converts a set of replacements to Replacements class.
|
||||
/// \return A Replacements class containing \p Replaces on success; otherwise,
|
||||
/// an empty Replacements is returned.
|
||||
static tooling::Replacements
|
||||
toReplacements(const std::set<tooling::Replacement> &Replaces) {
|
||||
tooling::Replacements Result;
|
||||
for (const auto &R : Replaces) {
|
||||
auto Err = Result.add(R);
|
||||
EXPECT_TRUE(!Err);
|
||||
if (Err) {
|
||||
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
||||
return tooling::Replacements();
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// \brief A utility class for replacement related tests.
|
||||
class ReplacementTest : public ::testing::Test {
|
||||
protected:
|
||||
tooling::Replacement createReplacement(SourceLocation Start, unsigned Length,
|
||||
llvm::StringRef ReplacementText) {
|
||||
return tooling::Replacement(Context.Sources, Start, Length,
|
||||
ReplacementText);
|
||||
}
|
||||
|
||||
RewriterTestContext Context;
|
||||
};
|
||||
|
||||
} // namespace tooling
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H
|
|
@ -39,8 +39,11 @@ TEST(Rewriter, ContinuesOverwritingFilesOnError) {
|
|||
|
||||
TEST(Rewriter, AdjacentInsertAndDelete) {
|
||||
Replacements Replaces;
|
||||
Replaces.insert(Replacement("<file>", 6, 6, ""));
|
||||
Replaces.insert(Replacement("<file>", 6, 0, "replaced\n"));
|
||||
auto Err = Replaces.add(Replacement("<file>", 6, 6, ""));
|
||||
EXPECT_TRUE(!Err);
|
||||
Replaces =
|
||||
Replaces.merge(Replacements(Replacement("<file>", 6, 0, "replaced\n")));
|
||||
|
||||
auto Rewritten = applyAllReplacements("line1\nline2\nline3\nline4", Replaces);
|
||||
EXPECT_TRUE(static_cast<bool>(Rewritten));
|
||||
EXPECT_EQ("line1\nreplaced\nline3\nline4", *Rewritten);
|
||||
|
|
Loading…
Reference in New Issue