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.
|
/// \brief Returns a human readable string representation.
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setFromSourceLocation(const SourceManager &Sources,
|
void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start,
|
||||||
SourceLocation Start, unsigned Length,
|
unsigned Length, StringRef ReplacementText);
|
||||||
StringRef ReplacementText);
|
void setFromSourceRange(const SourceManager &Sources,
|
||||||
void setFromSourceRange(const SourceManager &Sources,
|
const CharSourceRange &Range,
|
||||||
const CharSourceRange &Range,
|
StringRef ReplacementText,
|
||||||
StringRef ReplacementText,
|
const LangOptions &LangOpts);
|
||||||
const LangOptions &LangOpts);
|
|
||||||
|
|
||||||
std::string FilePath;
|
std::string FilePath;
|
||||||
Range ReplacementRange;
|
Range ReplacementRange;
|
||||||
|
@ -143,9 +142,70 @@ bool operator<(const Replacement &LHS, const Replacement &RHS);
|
||||||
/// \brief Equal-to operator between two Replacements.
|
/// \brief Equal-to operator between two Replacements.
|
||||||
bool operator==(const Replacement &LHS, const Replacement &RHS);
|
bool operator==(const Replacement &LHS, const Replacement &RHS);
|
||||||
|
|
||||||
/// \brief A set of Replacements.
|
/// \brief Maintains a set of replacements that are conflict-free.
|
||||||
/// FIXME: Change to a vector and deduplicate in the RefactoringTool.
|
/// Two replacements are considered conflicts if they overlap or have the same
|
||||||
typedef std::set<Replacement> Replacements;
|
/// 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.
|
/// \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.
|
/// \returns true if all replacements apply. false otherwise.
|
||||||
bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite);
|
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.
|
/// \brief Applies all replacements in \p Replaces to \p Code.
|
||||||
///
|
///
|
||||||
/// This completely ignores the path stored in each replacement. If all
|
/// 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,
|
llvm::Expected<std::string> applyAllReplacements(StringRef Code,
|
||||||
const Replacements &Replaces);
|
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.
|
/// \brief Collection of Replacements generated from a single translation unit.
|
||||||
struct TranslationUnitReplacements {
|
struct TranslationUnitReplacements {
|
||||||
/// Name of the main source for the translation unit.
|
/// Name of the main source for the translation unit.
|
||||||
|
@ -208,14 +238,6 @@ struct TranslationUnitReplacements {
|
||||||
std::vector<Replacement> Replacements;
|
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
|
/// \brief Calculates the new ranges after \p Replaces are applied. These
|
||||||
/// include both the original \p Ranges and the affected ranges of \p Replaces
|
/// include both the original \p Ranges and the affected ranges of \p Replaces
|
||||||
/// in the new code.
|
/// in the new code.
|
||||||
|
@ -233,12 +255,6 @@ calculateRangesAfterReplacements(const Replacements &Replaces,
|
||||||
std::map<std::string, Replacements>
|
std::map<std::string, Replacements>
|
||||||
groupReplacementsByFile(const Replacements &Replaces);
|
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>
|
template <typename Node>
|
||||||
Replacement::Replacement(const SourceManager &Sources,
|
Replacement::Replacement(const SourceManager &Sources,
|
||||||
const Node &NodeToReplace, StringRef ReplacementText,
|
const Node &NodeToReplace, StringRef ReplacementText,
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "clang/Tooling/Core/Replacement.h"
|
#include "clang/Tooling/Core/Replacement.h"
|
||||||
#include "clang/Tooling/Tooling.h"
|
#include "clang/Tooling/Tooling.h"
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
@ -42,9 +43,9 @@ public:
|
||||||
std::shared_ptr<PCHContainerOperations> PCHContainerOps =
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps =
|
||||||
std::make_shared<PCHContainerOperations>());
|
std::make_shared<PCHContainerOperations>());
|
||||||
|
|
||||||
/// \brief Returns the set of replacements to which replacements should
|
/// \brief Returns the file path to replacements map to which replacements
|
||||||
/// be added during the run of the tool.
|
/// should be added during the run of the tool.
|
||||||
Replacements &getReplacements();
|
std::map<std::string, Replacements> &getReplacements();
|
||||||
|
|
||||||
/// \brief Call run(), apply all generated replacements, and immediately save
|
/// \brief Call run(), apply all generated replacements, and immediately save
|
||||||
/// the results to disk.
|
/// the results to disk.
|
||||||
|
@ -65,7 +66,7 @@ private:
|
||||||
int saveRewrittenFiles(Rewriter &Rewrite);
|
int saveRewrittenFiles(Rewriter &Rewrite);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Replacements Replace;
|
std::map<std::string, Replacements> FileToReplaces;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Groups \p Replaces by the file path and applies each group of
|
/// \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
|
/// Replacement applications happen independently of the success of other
|
||||||
/// applications.
|
/// 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] Rewrite The `Rewritter` to apply replacements on.
|
||||||
/// \param[in] Style The style name used for reformatting. See ```getStyle``` in
|
/// \param[in] Style The style name used for reformatting. See ```getStyle``` in
|
||||||
/// "include/clang/Format/Format.h" for all possible style forms.
|
/// "include/clang/Format/Format.h" for all possible style forms.
|
||||||
///
|
///
|
||||||
/// \returns true if all replacements applied and formatted. false otherwise.
|
/// \returns true if all replacements applied and formatted. false otherwise.
|
||||||
bool formatAndApplyAllReplacements(const Replacements &Replaces,
|
bool formatAndApplyAllReplacements(
|
||||||
Rewriter &Rewrite, StringRef Style = "file");
|
const std::map<std::string, Replacements> &FileToReplaces,
|
||||||
|
Rewriter &Rewrite, StringRef Style = "file");
|
||||||
|
|
||||||
} // end namespace tooling
|
} // end namespace tooling
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
|
@ -854,8 +854,13 @@ private:
|
||||||
SourceLocation Start = FormatTok->Tok.getLocation();
|
SourceLocation Start = FormatTok->Tok.getLocation();
|
||||||
auto Replace = [&](SourceLocation Start, unsigned Length,
|
auto Replace = [&](SourceLocation Start, unsigned Length,
|
||||||
StringRef ReplacementText) {
|
StringRef ReplacementText) {
|
||||||
Result.insert(tooling::Replacement(Env.getSourceManager(), Start,
|
auto Err = Result.add(tooling::Replacement(
|
||||||
Length, ReplacementText));
|
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(Start, 1, IsSingle ? "'" : "\"");
|
||||||
Replace(FormatTok->Tok.getEndLoc().getLocWithOffset(-1), 1,
|
Replace(FormatTok->Tok.getEndLoc().getLocWithOffset(-1), 1,
|
||||||
|
@ -1163,7 +1168,13 @@ private:
|
||||||
}
|
}
|
||||||
auto SR = CharSourceRange::getCharRange(Tokens[St]->Tok.getLocation(),
|
auto SR = CharSourceRange::getCharRange(Tokens[St]->Tok.getLocation(),
|
||||||
Tokens[End]->Tok.getEndLoc());
|
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;
|
Idx = End + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1256,8 +1267,13 @@ static void sortCppIncludes(const FormatStyle &Style,
|
||||||
Includes.back().Offset + Includes.back().Text.size() -
|
Includes.back().Offset + Includes.back().Text.size() -
|
||||||
Includes.front().Offset);
|
Includes.front().Offset);
|
||||||
|
|
||||||
Replaces.insert(tooling::Replacement(FileName, Includes.front().Offset,
|
auto Err = Replaces.add(tooling::Replacement(
|
||||||
result.size(), result));
|
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 {
|
namespace {
|
||||||
|
@ -1402,14 +1418,13 @@ processReplacements(T ProcessFunc, StringRef Code,
|
||||||
auto NewCode = applyAllReplacements(Code, Replaces);
|
auto NewCode = applyAllReplacements(Code, Replaces);
|
||||||
if (!NewCode)
|
if (!NewCode)
|
||||||
return NewCode.takeError();
|
return NewCode.takeError();
|
||||||
std::vector<tooling::Range> ChangedRanges =
|
std::vector<tooling::Range> ChangedRanges = Replaces.getAffectedRanges();
|
||||||
tooling::calculateChangedRanges(Replaces);
|
|
||||||
StringRef FileName = Replaces.begin()->getFilePath();
|
StringRef FileName = Replaces.begin()->getFilePath();
|
||||||
|
|
||||||
tooling::Replacements FormatReplaces =
|
tooling::Replacements FormatReplaces =
|
||||||
ProcessFunc(Style, *NewCode, ChangedRanges, FileName);
|
ProcessFunc(Style, *NewCode, ChangedRanges, FileName);
|
||||||
|
|
||||||
return mergeReplacements(Replaces, FormatReplaces);
|
return Replaces.merge(FormatReplaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Expected<tooling::Replacements>
|
llvm::Expected<tooling::Replacements>
|
||||||
|
@ -1497,20 +1512,22 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces,
|
||||||
return Replaces;
|
return Replaces;
|
||||||
|
|
||||||
tooling::Replacements HeaderInsertions;
|
tooling::Replacements HeaderInsertions;
|
||||||
|
tooling::Replacements Result;
|
||||||
for (const auto &R : Replaces) {
|
for (const auto &R : Replaces) {
|
||||||
if (isHeaderInsertion(R))
|
if (isHeaderInsertion(R)) {
|
||||||
HeaderInsertions.insert(R);
|
// Replacements from \p Replaces must be conflict-free already, so we can
|
||||||
else if (R.getOffset() == UINT_MAX)
|
// simply consume the error.
|
||||||
|
llvm::consumeError(HeaderInsertions.add(R));
|
||||||
|
} else if (R.getOffset() == UINT_MAX) {
|
||||||
llvm::errs() << "Insertions other than header #include insertion are "
|
llvm::errs() << "Insertions other than header #include insertion are "
|
||||||
"not supported! "
|
"not supported! "
|
||||||
<< R.getReplacementText() << "\n";
|
<< R.getReplacementText() << "\n";
|
||||||
|
} else {
|
||||||
|
llvm::consumeError(Result.add(R));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (HeaderInsertions.empty())
|
if (HeaderInsertions.empty())
|
||||||
return Replaces;
|
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 IncludeRegex(IncludeRegexPattern);
|
||||||
llvm::Regex DefineRegex(R"(^[\t\ ]*#[\t\ ]*define[\t\ ]*[^\\]*$)");
|
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")
|
std::string NewInclude = !IncludeDirective.endswith("\n")
|
||||||
? (IncludeDirective + "\n").str()
|
? (IncludeDirective + "\n").str()
|
||||||
: IncludeDirective.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;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,8 @@ public:
|
||||||
tooling::Replacements
|
tooling::Replacements
|
||||||
analyze(TokenAnnotator &Annotator,
|
analyze(TokenAnnotator &Annotator,
|
||||||
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
|
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
|
||||||
FormatTokenLexer &Tokens, tooling::Replacements &Result) override {
|
FormatTokenLexer &Tokens, tooling::Replacements &) override {
|
||||||
|
tooling::Replacements Result;
|
||||||
AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(),
|
AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(),
|
||||||
AnnotatedLines.end());
|
AnnotatedLines.end());
|
||||||
|
|
||||||
|
@ -192,9 +193,14 @@ public:
|
||||||
DEBUG(llvm::dbgs() << "Replacing imports:\n"
|
DEBUG(llvm::dbgs() << "Replacing imports:\n"
|
||||||
<< getSourceText(InsertionPoint) << "\nwith:\n"
|
<< getSourceText(InsertionPoint) << "\nwith:\n"
|
||||||
<< ReferencesText << "\n");
|
<< ReferencesText << "\n");
|
||||||
Result.insert(tooling::Replacement(
|
auto Err = Result.add(tooling::Replacement(
|
||||||
Env.getSourceManager(), CharSourceRange::getCharRange(InsertionPoint),
|
Env.getSourceManager(), CharSourceRange::getCharRange(InsertionPoint),
|
||||||
ReferencesText));
|
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;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,8 +111,8 @@ tooling::Replacements TokenAnalyzer::process() {
|
||||||
|
|
||||||
DEBUG({
|
DEBUG({
|
||||||
llvm::dbgs() << "Replacements for run " << Run << ":\n";
|
llvm::dbgs() << "Replacements for run " << Run << ":\n";
|
||||||
for (tooling::Replacements::iterator I = RunResult.begin(),
|
for (tooling::Replacements::const_iterator I = RunResult.begin(),
|
||||||
E = RunResult.end();
|
E = RunResult.end();
|
||||||
I != E; ++I) {
|
I != E; ++I) {
|
||||||
llvm::dbgs() << I->toString() << "\n";
|
llvm::dbgs() << I->toString() << "\n";
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,15 @@ tooling::Replacements TokenAnalyzer::process() {
|
||||||
for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
|
for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
|
||||||
delete AnnotatedLines[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;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -502,8 +502,13 @@ void WhitespaceManager::storeReplacement(SourceRange Range,
|
||||||
if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
|
if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
|
||||||
WhitespaceLength) == Text)
|
WhitespaceLength) == Text)
|
||||||
return;
|
return;
|
||||||
Replaces.insert(tooling::Replacement(
|
auto Err = Replaces.add(tooling::Replacement(
|
||||||
SourceMgr, CharSourceRange::getCharRange(Range), Text));
|
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,
|
void WhitespaceManager::appendNewlineText(std::string &Text,
|
||||||
|
|
|
@ -137,200 +137,30 @@ void Replacement::setFromSourceRange(const SourceManager &Sources,
|
||||||
ReplacementText);
|
ReplacementText);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
llvm::Error Replacements::add(const Replacement &R) {
|
||||||
unsigned shiftedCodePositionInternal(const T &Replaces, unsigned Position) {
|
if (R.getOffset() != UINT_MAX)
|
||||||
unsigned Offset = 0;
|
for (auto Replace : Replaces) {
|
||||||
for (const auto& R : Replaces) {
|
if (R.getFilePath() != Replace.getFilePath())
|
||||||
if (R.getOffset() + R.getLength() <= Position) {
|
return llvm::make_error<llvm::StringError>(
|
||||||
Offset += R.getReplacementText().size() - R.getLength();
|
"All replacements must have the same file path. New replacement: " +
|
||||||
continue;
|
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) {
|
Replaces.insert(R);
|
||||||
return shiftedCodePositionInternal(Replaces, Position);
|
return llvm::Error::success();
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Represents a merged replacement, i.e. a replacement consisting of multiple
|
// Represents a merged replacement, i.e. a replacement consisting of multiple
|
||||||
// overlapping replacements from 'First' and 'Second' in mergeReplacements.
|
// overlapping replacements from 'First' and 'Second' in mergeReplacements.
|
||||||
//
|
//
|
||||||
|
@ -424,26 +254,19 @@ private:
|
||||||
unsigned Length;
|
unsigned Length;
|
||||||
std::string Text;
|
std::string Text;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::map<std::string, Replacements>
|
Replacements Replacements::merge(const Replacements &ReplacesToMerge) const {
|
||||||
groupReplacementsByFile(const Replacements &Replaces) {
|
if (empty() || ReplacesToMerge.empty())
|
||||||
std::map<std::string, Replacements> FileToReplaces;
|
return empty() ? ReplacesToMerge : *this;
|
||||||
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;
|
|
||||||
|
|
||||||
|
auto &First = Replaces;
|
||||||
|
auto &Second = ReplacesToMerge.Replaces;
|
||||||
// Delta is the amount of characters that replacements from 'Second' need to
|
// Delta is the amount of characters that replacements from 'Second' need to
|
||||||
// be shifted so that their offsets refer to the original text.
|
// be shifted so that their offsets refer to the original text.
|
||||||
int Delta = 0;
|
int Delta = 0;
|
||||||
Replacements Result;
|
ReplacementsImpl Result;
|
||||||
|
|
||||||
// Iterate over both sets and always add the next element (smallest total
|
// Iterate over both sets and always add the next element (smallest total
|
||||||
// Offset) from either 'First' or 'Second'. Merge that element with
|
// Offset) from either 'First' or 'Second'. Merge that element with
|
||||||
|
@ -469,8 +292,141 @@ Replacements mergeReplacements(const Replacements &First,
|
||||||
Delta -= Merged.deltaFirst();
|
Delta -= Merged.deltaFirst();
|
||||||
Result.insert(Merged.asReplacement());
|
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;
|
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 tooling
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
|
@ -30,7 +30,9 @@ RefactoringTool::RefactoringTool(
|
||||||
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
|
||||||
: ClangTool(Compilations, SourcePaths, PCHContainerOps) {}
|
: ClangTool(Compilations, SourcePaths, PCHContainerOps) {}
|
||||||
|
|
||||||
Replacements &RefactoringTool::getReplacements() { return Replace; }
|
std::map<std::string, Replacements> &RefactoringTool::getReplacements() {
|
||||||
|
return FileToReplaces;
|
||||||
|
}
|
||||||
|
|
||||||
int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
|
int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
|
||||||
if (int Result = run(ActionFactory)) {
|
if (int Result = run(ActionFactory)) {
|
||||||
|
@ -54,20 +56,22 @@ int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) {
|
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) {
|
int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) {
|
||||||
return Rewrite.overwriteChangedFiles() ? 1 : 0;
|
return Rewrite.overwriteChangedFiles() ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool formatAndApplyAllReplacements(const Replacements &Replaces,
|
bool formatAndApplyAllReplacements(
|
||||||
Rewriter &Rewrite, StringRef Style) {
|
const std::map<std::string, Replacements> &FileToReplaces, Rewriter &Rewrite,
|
||||||
|
StringRef Style) {
|
||||||
SourceManager &SM = Rewrite.getSourceMgr();
|
SourceManager &SM = Rewrite.getSourceMgr();
|
||||||
FileManager &Files = SM.getFileManager();
|
FileManager &Files = SM.getFileManager();
|
||||||
|
|
||||||
auto FileToReplaces = groupReplacementsByFile(Replaces);
|
|
||||||
|
|
||||||
bool Result = true;
|
bool Result = true;
|
||||||
for (const auto &FileAndReplaces : FileToReplaces) {
|
for (const auto &FileAndReplaces : FileToReplaces) {
|
||||||
const std::string &FilePath = FileAndReplaces.first;
|
const std::string &FilePath = FileAndReplaces.first;
|
||||||
|
|
|
@ -40,10 +40,14 @@ ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
|
||||||
void ReplaceStmtWithText::run(
|
void ReplaceStmtWithText::run(
|
||||||
const ast_matchers::MatchFinder::MatchResult &Result) {
|
const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||||
if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) {
|
if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) {
|
||||||
Replace.insert(tooling::Replacement(
|
auto Err = Replace.add(tooling::Replacement(
|
||||||
*Result.SourceManager,
|
*Result.SourceManager,
|
||||||
CharSourceRange::getTokenRange(FromMatch->getSourceRange()),
|
CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
|
||||||
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 ast_matchers::MatchFinder::MatchResult &Result) {
|
||||||
const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId);
|
const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId);
|
||||||
const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId);
|
const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId);
|
||||||
if (FromMatch && ToMatch)
|
if (FromMatch && ToMatch) {
|
||||||
Replace.insert(replaceStmtWithStmt(
|
auto Err = Replace.add(
|
||||||
*Result.SourceManager, *FromMatch, *ToMatch));
|
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,
|
ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
|
||||||
|
@ -68,11 +78,23 @@ void ReplaceIfStmtWithItsBody::run(
|
||||||
if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) {
|
if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) {
|
||||||
const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
|
const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
|
||||||
if (Body) {
|
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) {
|
} else if (!PickTrueBranch) {
|
||||||
// If we want to use the 'else'-branch, but it doesn't exist, delete
|
// If we want to use the 'else'-branch, but it doesn't exist, delete
|
||||||
// the whole 'if'.
|
// 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;
|
bool IncompleteFormat = false;
|
||||||
Replacements FormatChanges = reformat(FormatStyle, *ChangedCode, Ranges,
|
Replacements FormatChanges = reformat(FormatStyle, *ChangedCode, Ranges,
|
||||||
AssumedFileName, &IncompleteFormat);
|
AssumedFileName, &IncompleteFormat);
|
||||||
Replaces = tooling::mergeReplacements(Replaces, FormatChanges);
|
Replaces = Replaces.merge(FormatChanges);
|
||||||
if (OutputXML) {
|
if (OutputXML) {
|
||||||
outs() << "<?xml version='1.0'?>\n<replacements "
|
outs() << "<?xml version='1.0'?>\n<replacements "
|
||||||
"xml:space='preserve' incomplete_format='"
|
"xml:space='preserve' incomplete_format='"
|
||||||
<< (IncompleteFormat ? "true" : "false") << "'>\n";
|
<< (IncompleteFormat ? "true" : "false") << "'>\n";
|
||||||
if (Cursor.getNumOccurrences() != 0)
|
if (Cursor.getNumOccurrences() != 0)
|
||||||
outs() << "<cursor>"
|
outs() << "<cursor>"
|
||||||
<< tooling::shiftedCodePosition(FormatChanges, CursorPosition)
|
<< FormatChanges.getShiftedCodePosition(CursorPosition)
|
||||||
<< "</cursor>\n";
|
<< "</cursor>\n";
|
||||||
|
|
||||||
outputReplacementsXML(Replaces);
|
outputReplacementsXML(Replaces);
|
||||||
outs() << "</replacements>\n";
|
outs() << "</replacements>\n";
|
||||||
} else {
|
} else {
|
||||||
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
|
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
|
||||||
|
@ -298,7 +298,7 @@ static bool format(StringRef FileName) {
|
||||||
} else {
|
} else {
|
||||||
if (Cursor.getNumOccurrences() != 0)
|
if (Cursor.getNumOccurrences() != 0)
|
||||||
outs() << "{ \"Cursor\": "
|
outs() << "{ \"Cursor\": "
|
||||||
<< tooling::shiftedCodePosition(FormatChanges, CursorPosition)
|
<< FormatChanges.getShiftedCodePosition(CursorPosition)
|
||||||
<< ", \"IncompleteFormat\": "
|
<< ", \"IncompleteFormat\": "
|
||||||
<< (IncompleteFormat ? "true" : "false") << " }\n";
|
<< (IncompleteFormat ? "true" : "false") << " }\n";
|
||||||
Rewrite.getEditBuffer(ID).write(outs());
|
Rewrite.getEditBuffer(ID).write(outs());
|
||||||
|
|
|
@ -9,11 +9,15 @@
|
||||||
|
|
||||||
#include "clang/Format/Format.h"
|
#include "clang/Format/Format.h"
|
||||||
|
|
||||||
|
#include "../Tooling/ReplacementTest.h"
|
||||||
#include "../Tooling/RewriterTestContext.h"
|
#include "../Tooling/RewriterTestContext.h"
|
||||||
#include "clang/Tooling/Core/Replacement.h"
|
#include "clang/Tooling/Core/Replacement.h"
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using clang::tooling::ReplacementTest;
|
||||||
|
using clang::tooling::toReplacements;
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace format {
|
namespace format {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -241,7 +245,7 @@ TEST_F(CleanupTest, CtorInitializerInNamespace) {
|
||||||
EXPECT_EQ(Expected, Result);
|
EXPECT_EQ(Expected, Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CleanUpReplacementsTest : public ::testing::Test {
|
class CleanUpReplacementsTest : public ReplacementTest {
|
||||||
protected:
|
protected:
|
||||||
tooling::Replacement createReplacement(unsigned Offset, unsigned Length,
|
tooling::Replacement createReplacement(unsigned Offset, unsigned Length,
|
||||||
StringRef Text) {
|
StringRef Text) {
|
||||||
|
@ -304,9 +308,9 @@ TEST_F(CleanUpReplacementsTest, FixOnlyAffectedCodeAfterReplacements) {
|
||||||
"namespace D { int i; }\n\n"
|
"namespace D { int i; }\n\n"
|
||||||
"int x= 0;"
|
"int x= 0;"
|
||||||
"}";
|
"}";
|
||||||
tooling::Replacements Replaces = {
|
tooling::Replacements Replaces =
|
||||||
createReplacement(getOffset(Code, 3, 3), 6, ""),
|
toReplacements({createReplacement(getOffset(Code, 3, 3), 6, ""),
|
||||||
createReplacement(getOffset(Code, 9, 34), 6, "")};
|
createReplacement(getOffset(Code, 9, 34), 6, "")});
|
||||||
|
|
||||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
@ -315,7 +319,8 @@ TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithoutDefine) {
|
||||||
std::string Code = "int main() {}";
|
std::string Code = "int main() {}";
|
||||||
std::string Expected = "#include \"a.h\"\n"
|
std::string Expected = "#include \"a.h\"\n"
|
||||||
"int main() {}";
|
"int main() {}";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include \"a.h\"")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include \"a.h\"")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +337,8 @@ TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithDefine) {
|
||||||
"#define MMM 123\n"
|
"#define MMM 123\n"
|
||||||
"#endif";
|
"#endif";
|
||||||
|
|
||||||
tooling::Replacements Replaces = {createInsertion("#include \"b.h\"")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include \"b.h\"")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +363,8 @@ TEST_F(CleanUpReplacementsTest, InsertBeforeCategoryWithLowerPriority) {
|
||||||
"#define MMM 123\n"
|
"#define MMM 123\n"
|
||||||
"#endif";
|
"#endif";
|
||||||
|
|
||||||
tooling::Replacements Replaces = {createInsertion("#include \"a.h\"")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include \"a.h\"")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +376,8 @@ TEST_F(CleanUpReplacementsTest, InsertAfterMainHeader) {
|
||||||
"#include <a>\n"
|
"#include <a>\n"
|
||||||
"\n"
|
"\n"
|
||||||
"int main() {}";
|
"int main() {}";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <a>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <a>")});
|
||||||
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
@ -382,7 +390,8 @@ TEST_F(CleanUpReplacementsTest, InsertBeforeSystemHeaderLLVM) {
|
||||||
"#include <memory>\n"
|
"#include <memory>\n"
|
||||||
"\n"
|
"\n"
|
||||||
"int main() {}";
|
"int main() {}";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include \"z.h\"")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include \"z.h\"")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +403,8 @@ TEST_F(CleanUpReplacementsTest, InsertAfterSystemHeaderGoogle) {
|
||||||
"#include \"z.h\"\n"
|
"#include \"z.h\"\n"
|
||||||
"\n"
|
"\n"
|
||||||
"int main() {}";
|
"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);
|
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
@ -412,8 +422,9 @@ TEST_F(CleanUpReplacementsTest, InsertOneIncludeLLVMStyle) {
|
||||||
"#include \"clang/Format/Format.h\"\n"
|
"#include \"clang/Format/Format.h\"\n"
|
||||||
"#include \"llvm/x/y.h\"\n"
|
"#include \"llvm/x/y.h\"\n"
|
||||||
"#include <memory>\n";
|
"#include <memory>\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include \"d.h\""),
|
tooling::Replacements Replaces =
|
||||||
createInsertion("#include \"llvm/x/y.h\"")};
|
toReplacements({createInsertion("#include \"d.h\""),
|
||||||
|
createInsertion("#include \"llvm/x/y.h\"")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,8 +441,9 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesLLVMStyle) {
|
||||||
"#include \"clang/Format/Format.h\"\n"
|
"#include \"clang/Format/Format.h\"\n"
|
||||||
"#include <memory>\n"
|
"#include <memory>\n"
|
||||||
"#include <list>\n";
|
"#include <list>\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <list>"),
|
tooling::Replacements Replaces =
|
||||||
createInsertion("#include \"new/new.h\"")};
|
toReplacements({createInsertion("#include <list>"),
|
||||||
|
createInsertion("#include \"new/new.h\"")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,7 +459,8 @@ TEST_F(CleanUpReplacementsTest, InsertNewSystemIncludeGoogleStyle) {
|
||||||
"\n"
|
"\n"
|
||||||
"#include \"y/a.h\"\n"
|
"#include \"y/a.h\"\n"
|
||||||
"#include \"z/b.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);
|
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
@ -467,8 +480,9 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesGoogleStyle) {
|
||||||
"#include \"y/a.h\"\n"
|
"#include \"y/a.h\"\n"
|
||||||
"#include \"z/b.h\"\n"
|
"#include \"z/b.h\"\n"
|
||||||
"#include \"x/x.h\"\n";
|
"#include \"x/x.h\"\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <list>"),
|
tooling::Replacements Replaces =
|
||||||
createInsertion("#include \"x/x.h\"")};
|
toReplacements({createInsertion("#include <list>"),
|
||||||
|
createInsertion("#include \"x/x.h\"")});
|
||||||
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
@ -482,12 +496,11 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortLLVM) {
|
||||||
"#include <list>\n"
|
"#include <list>\n"
|
||||||
"#include <vector>\n"
|
"#include <vector>\n"
|
||||||
"int x;";
|
"int x;";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include \"a.h\""),
|
tooling::Replacements Replaces = toReplacements(
|
||||||
createInsertion("#include \"c.h\""),
|
{createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
|
||||||
createInsertion("#include \"b.h\""),
|
createInsertion("#include \"b.h\""),
|
||||||
createInsertion("#include <vector>"),
|
createInsertion("#include <vector>"), createInsertion("#include <list>"),
|
||||||
createInsertion("#include <list>"),
|
createInsertion("#include \"fix.h\"")});
|
||||||
createInsertion("#include \"fix.h\"")};
|
|
||||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,12 +513,11 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) {
|
||||||
"#include \"b.h\"\n"
|
"#include \"b.h\"\n"
|
||||||
"#include \"c.h\"\n"
|
"#include \"c.h\"\n"
|
||||||
"int x;";
|
"int x;";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include \"a.h\""),
|
tooling::Replacements Replaces = toReplacements(
|
||||||
createInsertion("#include \"c.h\""),
|
{createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
|
||||||
createInsertion("#include \"b.h\""),
|
createInsertion("#include \"b.h\""),
|
||||||
createInsertion("#include <vector>"),
|
createInsertion("#include <vector>"), createInsertion("#include <list>"),
|
||||||
createInsertion("#include <list>"),
|
createInsertion("#include \"fix.h\"")});
|
||||||
createInsertion("#include \"fix.h\"")};
|
|
||||||
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
|
||||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
@ -526,13 +538,12 @@ TEST_F(CleanUpReplacementsTest, FormatCorrectLineWhenHeadersAreInserted) {
|
||||||
"int a;\n"
|
"int a;\n"
|
||||||
"int b;\n"
|
"int b;\n"
|
||||||
"int a;";
|
"int a;";
|
||||||
tooling::Replacements Replaces = {
|
tooling::Replacements Replaces = toReplacements(
|
||||||
createReplacement(getOffset(Code, 4, 8), 1, "b"),
|
{createReplacement(getOffset(Code, 4, 8), 1, "b"),
|
||||||
createInsertion("#include <vector>"),
|
createInsertion("#include <vector>"), createInsertion("#include <list>"),
|
||||||
createInsertion("#include <list>"),
|
createInsertion("#include \"clang/x/x.h\""),
|
||||||
createInsertion("#include \"clang/x/x.h\""),
|
createInsertion("#include \"y.h\""),
|
||||||
createInsertion("#include \"y.h\""),
|
createInsertion("#include \"x.h\"")});
|
||||||
createInsertion("#include \"x.h\"")};
|
|
||||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,7 +555,8 @@ TEST_F(CleanUpReplacementsTest, NotConfusedByDefine) {
|
||||||
"void f() {}\n"
|
"void f() {}\n"
|
||||||
"#define A \\\n"
|
"#define A \\\n"
|
||||||
" int i;";
|
" int i;";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +568,8 @@ TEST_F(CleanUpReplacementsTest, SkippedTopComment) {
|
||||||
"\n"
|
"\n"
|
||||||
" // comment\n"
|
" // comment\n"
|
||||||
"#include <vector>\n";
|
"#include <vector>\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,7 +587,8 @@ TEST_F(CleanUpReplacementsTest, SkippedMixedComments) {
|
||||||
"* comment\n"
|
"* comment\n"
|
||||||
"*/\n"
|
"*/\n"
|
||||||
"#include <vector>\n";
|
"#include <vector>\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,7 +606,8 @@ TEST_F(CleanUpReplacementsTest, MultipleBlockCommentsInOneLine) {
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"/* c1 */ /*c2 */\n"
|
"/* c1 */ /*c2 */\n"
|
||||||
"#include <vector>\n";
|
"#include <vector>\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +629,8 @@ TEST_F(CleanUpReplacementsTest, CodeAfterComments) {
|
||||||
"\n"
|
"\n"
|
||||||
"#include <vector>\n"
|
"#include <vector>\n"
|
||||||
"int x;\n";
|
"int x;\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +642,8 @@ TEST_F(CleanUpReplacementsTest, FakeHeaderGuardIfDef) {
|
||||||
"#include <vector>\n"
|
"#include <vector>\n"
|
||||||
"#ifdef X\n"
|
"#ifdef X\n"
|
||||||
"#define X\n";
|
"#define X\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,7 +659,8 @@ TEST_F(CleanUpReplacementsTest, RealHeaderGuardAfterComments) {
|
||||||
"#include <vector>\n"
|
"#include <vector>\n"
|
||||||
"int x;\n"
|
"int x;\n"
|
||||||
"#define Y 1\n";
|
"#define Y 1\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,7 +674,8 @@ TEST_F(CleanUpReplacementsTest, IfNDefWithNoDefine) {
|
||||||
"#ifndef X\n"
|
"#ifndef X\n"
|
||||||
"int x;\n"
|
"int x;\n"
|
||||||
"#define Y 1\n";
|
"#define Y 1\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,14 +697,16 @@ TEST_F(CleanUpReplacementsTest, HeaderGuardWithComment) {
|
||||||
"#include <vector>\n"
|
"#include <vector>\n"
|
||||||
"int x;\n"
|
"int x;\n"
|
||||||
"#define Y 1\n";
|
"#define Y 1\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>")};
|
tooling::Replacements Replaces =
|
||||||
|
toReplacements({createInsertion("#include <vector>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CleanUpReplacementsTest, EmptyCode) {
|
TEST_F(CleanUpReplacementsTest, EmptyCode) {
|
||||||
std::string Code = "";
|
std::string Code = "";
|
||||||
std::string Expected = "#include <vector>\n";
|
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));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +715,8 @@ TEST_F(CleanUpReplacementsTest, EmptyCode) {
|
||||||
TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCode) {
|
TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCode) {
|
||||||
std::string Code = "#include <map>";
|
std::string Code = "#include <map>";
|
||||||
std::string Expected = "#include <map>#include <vector>\n";
|
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));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,8 +725,9 @@ TEST_F(CleanUpReplacementsTest, SkipExistingHeaders) {
|
||||||
"#include <vector>\n";
|
"#include <vector>\n";
|
||||||
std::string Expected = "#include \"a.h\"\n"
|
std::string Expected = "#include \"a.h\"\n"
|
||||||
"#include <vector>\n";
|
"#include <vector>\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include <vector>"),
|
tooling::Replacements Replaces =
|
||||||
createInsertion("#include \"a.h\"")};
|
toReplacements({createInsertion("#include <vector>"),
|
||||||
|
createInsertion("#include \"a.h\"")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,8 +739,9 @@ TEST_F(CleanUpReplacementsTest, AddIncludesWithDifferentForms) {
|
||||||
"#include \"vector\"\n"
|
"#include \"vector\"\n"
|
||||||
"#include <vector>\n"
|
"#include <vector>\n"
|
||||||
"#include <a.h>\n";
|
"#include <a.h>\n";
|
||||||
tooling::Replacements Replaces = {createInsertion("#include \"vector\""),
|
tooling::Replacements Replaces =
|
||||||
createInsertion("#include <a.h>")};
|
toReplacements({createInsertion("#include \"vector\""),
|
||||||
|
createInsertion("#include <a.h>")});
|
||||||
EXPECT_EQ(Expected, apply(Code, Replaces));
|
EXPECT_EQ(Expected, apply(Code, Replaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include "clang/Format/Format.h"
|
#include "clang/Format/Format.h"
|
||||||
|
|
||||||
#include "../Tooling/RewriterTestContext.h"
|
#include "../Tooling/ReplacementTest.h"
|
||||||
#include "FormatTestUtils.h"
|
#include "FormatTestUtils.h"
|
||||||
|
|
||||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||||
|
@ -19,6 +19,9 @@
|
||||||
|
|
||||||
#define DEBUG_TYPE "format-test"
|
#define DEBUG_TYPE "format-test"
|
||||||
|
|
||||||
|
using clang::tooling::ReplacementTest;
|
||||||
|
using clang::tooling::toReplacements;
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace format {
|
namespace format {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -11520,17 +11523,6 @@ TEST(FormatStyle, GetStyleOfFile) {
|
||||||
|
|
||||||
#endif // _MSC_VER
|
#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) {
|
TEST_F(ReplacementTest, FormatCodeAfterReplacements) {
|
||||||
// Column limit is 20.
|
// Column limit is 20.
|
||||||
std::string Code = "Type *a =\n"
|
std::string Code = "Type *a =\n"
|
||||||
|
@ -11545,15 +11537,15 @@ TEST_F(ReplacementTest, FormatCodeAfterReplacements) {
|
||||||
" mm);\n"
|
" mm);\n"
|
||||||
"int bad = format ;";
|
"int bad = format ;";
|
||||||
FileID ID = Context.createInMemoryFile("format.cpp", Code);
|
FileID ID = Context.createInMemoryFile("format.cpp", Code);
|
||||||
tooling::Replacements Replaces;
|
tooling::Replacements Replaces = toReplacements(
|
||||||
Replaces.insert(tooling::Replacement(
|
{tooling::Replacement(Context.Sources, Context.getLocation(ID, 1, 1), 6,
|
||||||
Context.Sources, Context.getLocation(ID, 1, 1), 6, "auto "));
|
"auto "),
|
||||||
Replaces.insert(tooling::Replacement(
|
tooling::Replacement(Context.Sources, Context.getLocation(ID, 3, 10), 1,
|
||||||
Context.Sources, Context.getLocation(ID, 3, 10), 1, "nullptr"));
|
"nullptr"),
|
||||||
Replaces.insert(tooling::Replacement(
|
tooling::Replacement(Context.Sources, Context.getLocation(ID, 4, 3), 1,
|
||||||
Context.Sources, Context.getLocation(ID, 4, 3), 1, "nullptr"));
|
"nullptr"),
|
||||||
Replaces.insert(tooling::Replacement(
|
tooling::Replacement(Context.Sources, Context.getLocation(ID, 4, 13), 1,
|
||||||
Context.Sources, Context.getLocation(ID, 4, 13), 1, "nullptr"));
|
"nullptr")});
|
||||||
|
|
||||||
format::FormatStyle Style = format::getLLVMStyle();
|
format::FormatStyle Style = format::getLLVMStyle();
|
||||||
Style.ColumnLimit = 20; // Set column limit to 20 to increase readibility.
|
Style.ColumnLimit = 20; // Set column limit to 20 to increase readibility.
|
||||||
|
@ -11580,9 +11572,9 @@ TEST_F(ReplacementTest, SortIncludesAfterReplacement) {
|
||||||
" return 0;\n"
|
" return 0;\n"
|
||||||
"}";
|
"}";
|
||||||
FileID ID = Context.createInMemoryFile("fix.cpp", Code);
|
FileID ID = Context.createInMemoryFile("fix.cpp", Code);
|
||||||
tooling::Replacements Replaces;
|
tooling::Replacements Replaces = toReplacements(
|
||||||
Replaces.insert(tooling::Replacement(
|
{tooling::Replacement(Context.Sources, Context.getLocation(ID, 1, 1), 0,
|
||||||
Context.Sources, Context.getLocation(ID, 1, 1), 0, "#include \"b.h\"\n"));
|
"#include \"b.h\"\n")});
|
||||||
|
|
||||||
format::FormatStyle Style = format::getLLVMStyle();
|
format::FormatStyle Style = format::getLLVMStyle();
|
||||||
Style.SortIncludes = true;
|
Style.SortIncludes = true;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "ReplacementTest.h"
|
||||||
#include "RewriterTestContext.h"
|
#include "RewriterTestContext.h"
|
||||||
#include "clang/AST/ASTConsumer.h"
|
#include "clang/AST/ASTConsumer.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
|
@ -31,16 +32,6 @@
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace tooling {
|
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) {
|
TEST_F(ReplacementTest, CanDeleteAllText) {
|
||||||
FileID ID = Context.createInMemoryFile("input.cpp", "text");
|
FileID ID = Context.createInMemoryFile("input.cpp", "text");
|
||||||
SourceLocation Location = Context.getLocation(ID, 1, 1);
|
SourceLocation Location = Context.getLocation(ID, 1, 1);
|
||||||
|
@ -108,29 +99,30 @@ TEST_F(ReplacementTest, ReturnsInvalidPath) {
|
||||||
EXPECT_TRUE(Replace2.getFilePath().empty());
|
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) {
|
TEST_F(ReplacementTest, CanApplyReplacements) {
|
||||||
FileID ID = Context.createInMemoryFile("input.cpp",
|
FileID ID = Context.createInMemoryFile("input.cpp",
|
||||||
"line1\nline2\nline3\nline4");
|
"line1\nline2\nline3\nline4");
|
||||||
Replacements Replaces;
|
Replacements Replaces =
|
||||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
toReplacements({Replacement(Context.Sources,
|
||||||
5, "replaced"));
|
Context.getLocation(ID, 2, 1), 5, "replaced"),
|
||||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 3, 1),
|
Replacement(Context.Sources,
|
||||||
5, "other"));
|
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"));
|
|
||||||
EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||||
EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
|
EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
|
||||||
}
|
}
|
||||||
|
@ -138,32 +130,28 @@ TEST_F(ReplacementTest, VectorCanApplyReplacements) {
|
||||||
TEST_F(ReplacementTest, SkipsDuplicateReplacements) {
|
TEST_F(ReplacementTest, SkipsDuplicateReplacements) {
|
||||||
FileID ID = Context.createInMemoryFile("input.cpp",
|
FileID ID = Context.createInMemoryFile("input.cpp",
|
||||||
"line1\nline2\nline3\nline4");
|
"line1\nline2\nline3\nline4");
|
||||||
Replacements Replaces;
|
auto Replaces = toReplacements({Replacement(
|
||||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")});
|
||||||
5, "replaced"));
|
|
||||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
auto Err = Replaces.add(Replacement(
|
||||||
5, "replaced"));
|
Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced"));
|
||||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
EXPECT_TRUE((bool)Err);
|
||||||
5, "replaced"));
|
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_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||||
EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID));
|
EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) {
|
TEST_F(ReplacementTest, InvalidSourceLocationFailsApplyAll) {
|
||||||
// This test depends on the value of the file name of an invalid source
|
Replacements Replaces =
|
||||||
// location being in the range ]a, z[.
|
toReplacements({Replacement(Context.Sources, SourceLocation(), 5, "2")});
|
||||||
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"));
|
|
||||||
EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite));
|
EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||||
EXPECT_EQ("a", Context.getRewrittenText(IDa));
|
|
||||||
EXPECT_EQ("z", Context.getRewrittenText(IDz));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
|
TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
|
||||||
|
@ -179,76 +167,66 @@ TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
|
||||||
std::string Expected2 = "int x =\n"
|
std::string Expected2 = "int x =\n"
|
||||||
" 1234567890123;\n"
|
" 1234567890123;\n"
|
||||||
"int y = 10;";
|
"int y = 10;";
|
||||||
FileID ID1 = Context.createInMemoryFile("format_1.cpp", Code1);
|
StringRef File1 = "format_1.cpp";
|
||||||
FileID ID2 = Context.createInMemoryFile("format_2.cpp", Code2);
|
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.
|
// Scrambled the order of replacements.
|
||||||
Replaces.insert(tooling::Replacement(
|
std::map<std::string, Replacements> FileToReplaces;
|
||||||
Context.Sources, Context.getLocation(ID2, 1, 12), 0, "4567890123"));
|
FileToReplaces[File1] = toReplacements(
|
||||||
Replaces.insert(tooling::Replacement(
|
{tooling::Replacement(Context.Sources, Context.getLocation(ID1, 1, 1), 6,
|
||||||
Context.Sources, Context.getLocation(ID1, 1, 1), 6, "auto "));
|
"auto "),
|
||||||
Replaces.insert(tooling::Replacement(
|
tooling::Replacement(Context.Sources, Context.getLocation(ID1, 3, 10), 1,
|
||||||
Context.Sources, Context.getLocation(ID2, 2, 9), 1, "10"));
|
"12345678901")});
|
||||||
Replaces.insert(tooling::Replacement(
|
FileToReplaces[File2] = toReplacements(
|
||||||
Context.Sources, Context.getLocation(ID1, 3, 10), 1, "12345678901"));
|
{tooling::Replacement(Context.Sources, Context.getLocation(ID2, 1, 12), 0,
|
||||||
|
"4567890123"),
|
||||||
EXPECT_TRUE(formatAndApplyAllReplacements(
|
tooling::Replacement(Context.Sources, Context.getLocation(ID2, 2, 9), 1,
|
||||||
Replaces, Context.Rewrite, "{BasedOnStyle: LLVM, ColumnLimit: 20}"));
|
"10")});
|
||||||
|
EXPECT_TRUE(
|
||||||
|
formatAndApplyAllReplacements(FileToReplaces, Context.Rewrite,
|
||||||
|
"{BasedOnStyle: LLVM, ColumnLimit: 20}"));
|
||||||
EXPECT_EQ(Expected1, Context.getRewrittenText(ID1));
|
EXPECT_EQ(Expected1, Context.getRewrittenText(ID1));
|
||||||
EXPECT_EQ(Expected2, Context.getRewrittenText(ID2));
|
EXPECT_EQ(Expected2, Context.getRewrittenText(ID2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
|
TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
|
||||||
Replacements Replaces;
|
Replacements Replaces =
|
||||||
Replaces.insert(Replacement("", 0, 1, ""));
|
toReplacements({Replacement("", 0, 1, ""), Replacement("", 4, 3, " ")});
|
||||||
Replaces.insert(Replacement("", 4, 3, " "));
|
|
||||||
// Assume ' int i;' is turned into 'int i;' and cursor is located at '|'.
|
// Assume ' int i;' is turned into 'int i;' and cursor is located at '|'.
|
||||||
EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int i;
|
EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0)); // |int i;
|
||||||
EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); // |nt i;
|
EXPECT_EQ(0u, Replaces.getShiftedCodePosition(1)); // |nt i;
|
||||||
EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); // i|t i;
|
EXPECT_EQ(1u, Replaces.getShiftedCodePosition(2)); // i|t i;
|
||||||
EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); // in| i;
|
EXPECT_EQ(2u, Replaces.getShiftedCodePosition(3)); // in| i;
|
||||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); // int| i;
|
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(4)); // int| i;
|
||||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 5)); // int | i;
|
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(5)); // int | i;
|
||||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 6)); // int |i;
|
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(6)); // int |i;
|
||||||
EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); // int |;
|
EXPECT_EQ(4u, Replaces.getShiftedCodePosition(7)); // int |;
|
||||||
EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); // int i|
|
EXPECT_EQ(5u, Replaces.getShiftedCodePosition(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|
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) {
|
TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) {
|
||||||
Replacements Replaces;
|
Replacements Replaces = toReplacements({Replacement("", 4, 0, "\"\n\"")});
|
||||||
Replaces.insert(Replacement("", 4, 0, "\"\n\""));
|
|
||||||
// Assume '"12345678"' is turned into '"1234"\n"5678"'.
|
// Assume '"12345678"' is turned into '"1234"\n"5678"'.
|
||||||
EXPECT_EQ(3u, shiftedCodePosition(Replaces, 3)); // "123|5678"
|
EXPECT_EQ(3u, Replaces.getShiftedCodePosition(3)); // "123|5678"
|
||||||
EXPECT_EQ(7u, shiftedCodePosition(Replaces, 4)); // "1234|678"
|
EXPECT_EQ(7u, Replaces.getShiftedCodePosition(4)); // "1234|678"
|
||||||
EXPECT_EQ(8u, shiftedCodePosition(Replaces, 5)); // "12345|78"
|
EXPECT_EQ(8u, Replaces.getShiftedCodePosition(5)); // "12345|78"
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ShiftedCodePositionTest, FindsNewCodePositionInReplacedText) {
|
TEST(ShiftedCodePositionTest, FindsNewCodePositionInReplacedText) {
|
||||||
Replacements Replaces;
|
|
||||||
// Replace the first four characters with "abcd".
|
// 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)
|
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 {
|
class FlushRewrittenFilesTest : public ::testing::Test {
|
||||||
|
@ -304,9 +282,8 @@ public:
|
||||||
|
|
||||||
TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) {
|
TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) {
|
||||||
FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4");
|
FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4");
|
||||||
Replacements Replaces;
|
Replacements Replaces = toReplacements({Replacement(
|
||||||
Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
|
Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")});
|
||||||
5, "replaced"));
|
|
||||||
EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
|
||||||
EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
|
EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
|
||||||
EXPECT_EQ("line1\nreplaced\nline3\nline4",
|
EXPECT_EQ("line1\nreplaced\nline3\nline4",
|
||||||
|
@ -454,12 +431,11 @@ TEST(Range, contains) {
|
||||||
TEST(Range, CalculateRangesOfReplacements) {
|
TEST(Range, CalculateRangesOfReplacements) {
|
||||||
// Before: aaaabbbbbbz
|
// Before: aaaabbbbbbz
|
||||||
// After : bbbbbbzzzzzzoooooooooooooooo
|
// After : bbbbbbzzzzzzoooooooooooooooo
|
||||||
Replacements Replaces;
|
Replacements Replaces = toReplacements(
|
||||||
Replaces.insert(Replacement("foo", 0, 4, ""));
|
{Replacement("foo", 0, 4, ""), Replacement("foo", 10, 1, "zzzzzz"),
|
||||||
Replaces.insert(Replacement("foo", 10, 1, "zzzzzz"));
|
Replacement("foo", 11, 0, "oooooooooooooooo")});
|
||||||
Replaces.insert(Replacement("foo", 11, 0, "oooooooooooooooo"));
|
|
||||||
|
|
||||||
std::vector<Range> Ranges = calculateChangedRanges(Replaces);
|
std::vector<Range> Ranges = Replaces.getAffectedRanges();
|
||||||
|
|
||||||
EXPECT_EQ(2ul, Ranges.size());
|
EXPECT_EQ(2ul, Ranges.size());
|
||||||
EXPECT_TRUE(Ranges[0].getOffset() == 0);
|
EXPECT_TRUE(Ranges[0].getOffset() == 0);
|
||||||
|
@ -470,23 +446,23 @@ TEST(Range, CalculateRangesOfReplacements) {
|
||||||
|
|
||||||
TEST(Range, RangesAfterReplacements) {
|
TEST(Range, RangesAfterReplacements) {
|
||||||
std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
|
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)};
|
std::vector<Range> Expected = {Range(0, 4), Range(7, 2), Range(12, 5)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Range, RangesBeforeReplacements) {
|
TEST(Range, RangesBeforeReplacements) {
|
||||||
std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
|
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)};
|
std::vector<Range> Expected = {Range(5, 2), Range(10, 5), Range(20, 4)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Range, NotAffectedByReplacements) {
|
TEST(Range, NotAffectedByReplacements) {
|
||||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
|
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
|
||||||
Replacements Replaces = {Replacement("foo", 3, 2, "12"),
|
Replacements Replaces = toReplacements({Replacement("foo", 3, 2, "12"),
|
||||||
Replacement("foo", 12, 2, "12"),
|
Replacement("foo", 12, 2, "12"),
|
||||||
Replacement("foo", 20, 5, "")};
|
Replacement("foo", 20, 5, "")});
|
||||||
std::vector<Range> Expected = {Range(0, 2), Range(3, 4), Range(10, 5),
|
std::vector<Range> Expected = {Range(0, 2), Range(3, 4), Range(10, 5),
|
||||||
Range(20, 0)};
|
Range(20, 0)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
|
@ -494,9 +470,9 @@ TEST(Range, NotAffectedByReplacements) {
|
||||||
|
|
||||||
TEST(Range, RangesWithNonOverlappingReplacements) {
|
TEST(Range, RangesWithNonOverlappingReplacements) {
|
||||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
|
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
|
||||||
Replacements Replaces = {Replacement("foo", 3, 1, ""),
|
Replacements Replaces = toReplacements({Replacement("foo", 3, 1, ""),
|
||||||
Replacement("foo", 6, 1, "123"),
|
Replacement("foo", 6, 1, "123"),
|
||||||
Replacement("foo", 20, 2, "12345")};
|
Replacement("foo", 20, 2, "12345")});
|
||||||
std::vector<Range> Expected = {Range(0, 2), Range(3, 0), Range(4, 4),
|
std::vector<Range> Expected = {Range(0, 2), Range(3, 0), Range(4, 4),
|
||||||
Range(11, 5), Range(21, 5)};
|
Range(11, 5), Range(21, 5)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
|
@ -505,9 +481,9 @@ TEST(Range, RangesWithNonOverlappingReplacements) {
|
||||||
TEST(Range, RangesWithOverlappingReplacements) {
|
TEST(Range, RangesWithOverlappingReplacements) {
|
||||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5),
|
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5),
|
||||||
Range(30, 5)};
|
Range(30, 5)};
|
||||||
Replacements Replaces = {
|
Replacements Replaces = toReplacements(
|
||||||
Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
|
{Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
|
||||||
Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")};
|
Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")});
|
||||||
std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(12, 5),
|
std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(12, 5),
|
||||||
Range(22, 0)};
|
Range(22, 0)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
|
@ -515,122 +491,52 @@ TEST(Range, RangesWithOverlappingReplacements) {
|
||||||
|
|
||||||
TEST(Range, MergeIntoOneRange) {
|
TEST(Range, MergeIntoOneRange) {
|
||||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
|
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)};
|
std::vector<Range> Expected = {Range(0, 15)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Range, ReplacementsStartingAtRangeOffsets) {
|
TEST(Range, ReplacementsStartingAtRangeOffsets) {
|
||||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 5), Range(15, 5)};
|
std::vector<Range> Ranges = {Range(0, 2), Range(5, 5), Range(15, 5)};
|
||||||
Replacements Replaces = {
|
Replacements Replaces = toReplacements(
|
||||||
Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
|
{Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
|
||||||
Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")};
|
Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")});
|
||||||
std::vector<Range> Expected = {Range(0, 2), Range(5, 9), Range(18, 2)};
|
std::vector<Range> Expected = {Range(0, 2), Range(5, 9), Range(18, 2)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Range, ReplacementsEndingAtRangeEnds) {
|
TEST(Range, ReplacementsEndingAtRangeEnds) {
|
||||||
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
|
std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
|
||||||
Replacements Replaces = {Replacement("foo", 6, 1, "123"),
|
Replacements Replaces = toReplacements(
|
||||||
Replacement("foo", 17, 3, "12")};
|
{Replacement("foo", 6, 1, "123"), Replacement("foo", 17, 3, "12")});
|
||||||
std::vector<Range> Expected = {Range(0, 2), Range(5, 4), Range(17, 4)};
|
std::vector<Range> Expected = {Range(0, 2), Range(5, 4), Range(17, 4)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Range, AjacentReplacements) {
|
TEST(Range, AjacentReplacements) {
|
||||||
std::vector<Range> Ranges = {Range(0, 0), Range(15, 5)};
|
std::vector<Range> Ranges = {Range(0, 0), Range(15, 5)};
|
||||||
Replacements Replaces = {Replacement("foo", 1, 2, "123"),
|
Replacements Replaces = toReplacements(
|
||||||
Replacement("foo", 12, 3, "1234")};
|
{Replacement("foo", 1, 2, "123"), Replacement("foo", 12, 3, "1234")});
|
||||||
std::vector<Range> Expected = {Range(0, 0), Range(1, 3), Range(13, 9)};
|
std::vector<Range> Expected = {Range(0, 0), Range(1, 3), Range(13, 9)};
|
||||||
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Range, MergeRangesAfterReplacements) {
|
TEST(Range, MergeRangesAfterReplacements) {
|
||||||
std::vector<Range> Ranges = {Range(8, 0), Range(5, 2), Range(9, 0), Range(0, 1)};
|
std::vector<Range> Ranges = {Range(8, 0), Range(5, 2), Range(9, 0), Range(0, 1)};
|
||||||
Replacements Replaces = {Replacement("foo", 1, 3, ""),
|
Replacements Replaces = toReplacements({Replacement("foo", 1, 3, ""),
|
||||||
Replacement("foo", 7, 0, "12"), Replacement("foo", 9, 2, "")};
|
Replacement("foo", 7, 0, "12"),
|
||||||
std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0), Range(8, 0)};
|
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));
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DeduplicateTest, removesDuplicates) {
|
TEST(Range, ConflictingRangesBeforeReplacements) {
|
||||||
std::vector<Replacement> Input;
|
std::vector<Range> Ranges = {Range(8, 3), Range(5, 4), Range(9, 1)};
|
||||||
Input.push_back(Replacement("fileA", 50, 0, " foo "));
|
Replacements Replaces = toReplacements({Replacement("foo", 1, 3, "")});
|
||||||
Input.push_back(Replacement("fileA", 10, 3, " bar "));
|
std::vector<Range> Expected = {Range(1, 0), Range(2, 6)};
|
||||||
Input.push_back(Replacement("fileA", 10, 2, " bar ")); // Length differs
|
EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MergeReplacementsTest : public ::testing::Test {
|
class MergeReplacementsTest : public ::testing::Test {
|
||||||
|
@ -646,7 +552,7 @@ protected:
|
||||||
EXPECT_EQ(Intermediate, *AfterFirst);
|
EXPECT_EQ(Intermediate, *AfterFirst);
|
||||||
EXPECT_EQ(Result, *InSequenceRewrite);
|
EXPECT_EQ(Result, *InSequenceRewrite);
|
||||||
|
|
||||||
tooling::Replacements Merged = mergeReplacements(First, Second);
|
tooling::Replacements Merged = First.merge(Second);
|
||||||
auto MergedRewrite = applyAllReplacements(Code, Merged);
|
auto MergedRewrite = applyAllReplacements(Code, Merged);
|
||||||
EXPECT_TRUE(static_cast<bool>(MergedRewrite));
|
EXPECT_TRUE(static_cast<bool>(MergedRewrite));
|
||||||
EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
|
EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
|
||||||
|
@ -660,7 +566,7 @@ protected:
|
||||||
auto AfterFirst = applyAllReplacements(Code, First);
|
auto AfterFirst = applyAllReplacements(Code, First);
|
||||||
EXPECT_TRUE(static_cast<bool>(AfterFirst));
|
EXPECT_TRUE(static_cast<bool>(AfterFirst));
|
||||||
auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second);
|
auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second);
|
||||||
tooling::Replacements Merged = mergeReplacements(First, Second);
|
tooling::Replacements Merged = First.merge(Second);
|
||||||
auto MergedRewrite = applyAllReplacements(Code, Merged);
|
auto MergedRewrite = applyAllReplacements(Code, Merged);
|
||||||
EXPECT_TRUE(static_cast<bool>(MergedRewrite));
|
EXPECT_TRUE(static_cast<bool>(MergedRewrite));
|
||||||
EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
|
EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
|
||||||
|
@ -673,62 +579,82 @@ protected:
|
||||||
|
|
||||||
TEST_F(MergeReplacementsTest, Offsets) {
|
TEST_F(MergeReplacementsTest, Offsets) {
|
||||||
mergeAndTestRewrite("aaa", "aabab", "cacabab",
|
mergeAndTestRewrite("aaa", "aabab", "cacabab",
|
||||||
{{"", 2, 0, "b"}, {"", 3, 0, "b"}},
|
toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
|
||||||
{{"", 0, 0, "c"}, {"", 1, 0, "c"}});
|
toReplacements({{"", 0, 0, "c"}, {"", 1, 0, "c"}}));
|
||||||
mergeAndTestRewrite("aaa", "babaa", "babacac",
|
mergeAndTestRewrite("aaa", "babaa", "babacac",
|
||||||
{{"", 0, 0, "b"}, {"", 1, 0, "b"}},
|
toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "b"}}),
|
||||||
{{"", 4, 0, "c"}, {"", 5, 0, "c"}});
|
toReplacements({{"", 4, 0, "c"}, {"", 5, 0, "c"}}));
|
||||||
mergeAndTestRewrite("aaaa", "aaa", "aac", {{"", 1, 1, ""}},
|
mergeAndTestRewrite("aaaa", "aaa", "aac", toReplacements({{"", 1, 1, ""}}),
|
||||||
{{"", 2, 1, "c"}});
|
toReplacements({{"", 2, 1, "c"}}));
|
||||||
|
|
||||||
mergeAndTestRewrite("aa", "bbabba", "bbabcba",
|
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) {
|
TEST_F(MergeReplacementsTest, Concatenations) {
|
||||||
// Basic concatenations. It is important to merge these into a single
|
// Basic concatenations. It is important to merge these into a single
|
||||||
// replacement to ensure the correct order.
|
// replacement to ensure the correct order.
|
||||||
EXPECT_EQ((Replacements{{"", 0, 0, "ab"}}),
|
{
|
||||||
mergeReplacements({{"", 0, 0, "a"}}, {{"", 1, 0, "b"}}));
|
auto First = toReplacements({{"", 0, 0, "a"}});
|
||||||
EXPECT_EQ((Replacements{{"", 0, 0, "ba"}}),
|
auto Second = toReplacements({{"", 1, 0, "b"}});
|
||||||
mergeReplacements({{"", 0, 0, "a"}}, {{"", 0, 0, "b"}}));
|
EXPECT_EQ(toReplacements({{"", 0, 0, "ab"}}), First.merge(Second));
|
||||||
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({{"", 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) {
|
TEST_F(MergeReplacementsTest, NotChangingLengths) {
|
||||||
mergeAndTestRewrite("aaaa", "abba", "acca", {{"", 1, 2, "bb"}},
|
mergeAndTestRewrite("aaaa", "abba", "acca",
|
||||||
{{"", 1, 2, "cc"}});
|
toReplacements({{"", 1, 2, "bb"}}),
|
||||||
mergeAndTestRewrite("aaaa", "abba", "abcc", {{"", 1, 2, "bb"}},
|
toReplacements({{"", 1, 2, "cc"}}));
|
||||||
{{"", 2, 2, "cc"}});
|
mergeAndTestRewrite("aaaa", "abba", "abcc",
|
||||||
mergeAndTestRewrite("aaaa", "abba", "ccba", {{"", 1, 2, "bb"}},
|
toReplacements({{"", 1, 2, "bb"}}),
|
||||||
{{"", 0, 2, "cc"}});
|
toReplacements({{"", 2, 2, "cc"}}));
|
||||||
|
mergeAndTestRewrite("aaaa", "abba", "ccba",
|
||||||
|
toReplacements({{"", 1, 2, "bb"}}),
|
||||||
|
toReplacements({{"", 0, 2, "cc"}}));
|
||||||
mergeAndTestRewrite("aaaaaa", "abbdda", "abccda",
|
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) {
|
TEST_F(MergeReplacementsTest, OverlappingRanges) {
|
||||||
mergeAndTestRewrite("aaa", "bbd", "bcbcd",
|
mergeAndTestRewrite("aaa", "bbd", "bcbcd",
|
||||||
{{"", 0, 1, "bb"}, {"", 1, 2, "d"}},
|
toReplacements({{"", 0, 1, "bb"}, {"", 1, 2, "d"}}),
|
||||||
{{"", 1, 0, "c"}, {"", 2, 0, "c"}});
|
toReplacements({{"", 1, 0, "c"}, {"", 2, 0, "c"}}));
|
||||||
|
|
||||||
mergeAndTestRewrite("aaaa", "aabbaa", "acccca", {{"", 2, 0, "bb"}},
|
mergeAndTestRewrite("aaaa", "aabbaa", "acccca",
|
||||||
{{"", 1, 4, "cccc"}});
|
toReplacements({{"", 2, 0, "bb"}}),
|
||||||
|
toReplacements({{"", 1, 4, "cccc"}}));
|
||||||
mergeAndTestRewrite("aaaa", "aababa", "acccca",
|
mergeAndTestRewrite("aaaa", "aababa", "acccca",
|
||||||
{{"", 2, 0, "b"}, {"", 3, 0, "b"}}, {{"", 1, 4, "cccc"}});
|
toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
|
||||||
mergeAndTestRewrite("aaaaaa", "abbbba", "abba", {{"", 1, 4, "bbbb"}},
|
toReplacements({{"", 1, 4, "cccc"}}));
|
||||||
{{"", 2, 2, ""}});
|
mergeAndTestRewrite("aaaaaa", "abbbba", "abba",
|
||||||
mergeAndTestRewrite("aaaa", "aa", "cc", {{"", 1, 1, ""}, {"", 2, 1, ""}},
|
toReplacements({{"", 1, 4, "bbbb"}}),
|
||||||
{{"", 0, 2, "cc"}});
|
toReplacements({{"", 2, 2, ""}}));
|
||||||
mergeAndTestRewrite("aa", "abbba", "abcbcba", {{"", 1, 0, "bbb"}},
|
mergeAndTestRewrite("aaaa", "aa", "cc",
|
||||||
{{"", 2, 0, "c"}, {"", 3, 0, "c"}});
|
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",
|
mergeAndTestRewrite(
|
||||||
{{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}},
|
"aaa", "abbab", "ccdd",
|
||||||
{{"", 0, 2, "cc"}, {"", 2, 3, "dd"}});
|
toReplacements({{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}}),
|
||||||
mergeAndTestRewrite("aa", "babbab", "ccdd",
|
toReplacements({{"", 0, 2, "cc"}, {"", 2, 3, "dd"}}));
|
||||||
{{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}},
|
mergeAndTestRewrite(
|
||||||
{{"", 0, 3, "cc"}, {"", 3, 3, "dd"}});
|
"aa", "babbab", "ccdd",
|
||||||
|
toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}}),
|
||||||
|
toReplacements({{"", 0, 3, "cc"}, {"", 3, 3, "dd"}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace tooling
|
} // 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) {
|
TEST(Rewriter, AdjacentInsertAndDelete) {
|
||||||
Replacements Replaces;
|
Replacements Replaces;
|
||||||
Replaces.insert(Replacement("<file>", 6, 6, ""));
|
auto Err = Replaces.add(Replacement("<file>", 6, 6, ""));
|
||||||
Replaces.insert(Replacement("<file>", 6, 0, "replaced\n"));
|
EXPECT_TRUE(!Err);
|
||||||
|
Replaces =
|
||||||
|
Replaces.merge(Replacements(Replacement("<file>", 6, 0, "replaced\n")));
|
||||||
|
|
||||||
auto Rewritten = applyAllReplacements("line1\nline2\nline3\nline4", Replaces);
|
auto Rewritten = applyAllReplacements("line1\nline2\nline3\nline4", Replaces);
|
||||||
EXPECT_TRUE(static_cast<bool>(Rewritten));
|
EXPECT_TRUE(static_cast<bool>(Rewritten));
|
||||||
EXPECT_EQ("line1\nreplaced\nline3\nline4", *Rewritten);
|
EXPECT_EQ("line1\nreplaced\nline3\nline4", *Rewritten);
|
||||||
|
|
Loading…
Reference in New Issue