<div dir="ltr">I reverted the commit in r298967. Please fix the cyclic dependency issue found here:<div><a href="http://lab.llvm.org:8080/green/job/clang-stage2-cmake-modulesRDA_build/4776/">http://lab.llvm.org:8080/green/job/clang-stage2-cmake-modulesRDA_build/4776/</a><br></div><div><br></div><div>Cheers,</div><div>Juergen</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Mar 28, 2017 at 6:05 AM, Eric Liu via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: ioeric<br>
Date: Tue Mar 28 08:05:32 2017<br>
New Revision: 298913<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=298913&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project?rev=298913&view=rev</a><br>
Log:<br>
Added `applyAtomicChanges` function.<br>
<br>
Summary: ... which applies a set of `AtomicChange`s on code.<br>
<br>
Reviewers: klimek, djasper<br>
<br>
Reviewed By: djasper<br>
<br>
Subscribers: cfe-commits<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D30777" rel="noreferrer" target="_blank">https://reviews.llvm.org/<wbr>D30777</a><br>
<br>
Modified:<br>
cfe/trunk/include/clang/<wbr>Tooling/Refactoring/<wbr>AtomicChange.h<br>
cfe/trunk/lib/Tooling/<wbr>Refactoring/AtomicChange.cpp<br>
cfe/trunk/unittests/Tooling/<wbr>RefactoringTest.cpp<br>
<br>
Modified: cfe/trunk/include/clang/<wbr>Tooling/Refactoring/<wbr>AtomicChange.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h?rev=298913&r1=298912&r2=298913&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/include/<wbr>clang/Tooling/Refactoring/<wbr>AtomicChange.h?rev=298913&r1=<wbr>298912&r2=298913&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/include/clang/<wbr>Tooling/Refactoring/<wbr>AtomicChange.h (original)<br>
+++ cfe/trunk/include/clang/<wbr>Tooling/Refactoring/<wbr>AtomicChange.h Tue Mar 28 08:05:32 2017<br>
@@ -16,6 +16,7 @@<br>
#define LLVM_CLANG_TOOLING_REFACTOR_<wbr>ATOMICCHANGE_H<br>
<br>
#include "clang/Basic/SourceManager.h"<br>
+#include "clang/Format/Format.h"<br>
#include "clang/Tooling/Core/<wbr>Replacement.h"<br>
#include "llvm/ADT/StringRef.h"<br>
#include "llvm/Support/Error.h"<br>
@@ -123,6 +124,39 @@ private:<br>
tooling::Replacements Replaces;<br>
};<br>
<br>
+// Defines specs for applying changes.<br>
+struct ApplyChangesSpec {<br>
+ // If true, cleans up redundant/erroneous code around changed code with<br>
+ // clang-format's cleanup functionality, e.g. redundant commas around deleted<br>
+ // parameter or empty namespaces introduced by deletions.<br>
+ bool Cleanup = true;<br>
+<br>
+ format::FormatStyle Style = format::getNoStyle();<br>
+<br>
+ // Options for selectively formatting changes with clang-format:<br>
+ // kAll: Format all changed lines.<br>
+ // kNone: Don't format anything.<br>
+ // kViolations: Format lines exceeding the `ColumnLimit` in `Style`.<br>
+ enum FormatOption { kAll, kNone, kViolations };<br>
+<br>
+ FormatOption Format = kNone;<br>
+};<br>
+<br>
+/// \brief Applies all AtomicChanges in \p Changes to the \p Code.<br>
+///<br>
+/// This completely ignores the file path in each change and replaces them with<br>
+/// \p FilePath, i.e. callers are responsible for ensuring all changes are for<br>
+/// the same file.<br>
+///<br>
+/// \returns The changed code if all changes are applied successfully;<br>
+/// otherwise, an llvm::Error carrying llvm::StringError is returned (the Error<br>
+/// message can be converted to string with `llvm::toString()` and the<br>
+/// error_code should be ignored).<br>
+llvm::Expected<std::string><br>
+applyAtomicChanges(llvm::<wbr>StringRef FilePath, llvm::StringRef Code,<br>
+ llvm::ArrayRef<AtomicChange> Changes,<br>
+ const ApplyChangesSpec &Spec);<br>
+<br>
} // end namespace tooling<br>
} // end namespace clang<br>
<br>
<br>
Modified: cfe/trunk/lib/Tooling/<wbr>Refactoring/AtomicChange.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/AtomicChange.cpp?rev=298913&r1=298912&r2=298913&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/Tooling/<wbr>Refactoring/AtomicChange.cpp?<wbr>rev=298913&r1=298912&r2=<wbr>298913&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Tooling/<wbr>Refactoring/AtomicChange.cpp (original)<br>
+++ cfe/trunk/lib/Tooling/<wbr>Refactoring/AtomicChange.cpp Tue Mar 28 08:05:32 2017<br>
@@ -84,6 +84,116 @@ template <> struct MappingTraits<clang::<br>
<br>
namespace clang {<br>
namespace tooling {<br>
+namespace {<br>
+<br>
+// Returns true if there is any line that violates \p ColumnLimit in range<br>
+// [Start, End].<br>
+bool violatesColumnLimit(llvm::<wbr>StringRef Code, unsigned ColumnLimit,<br>
+ unsigned Start, unsigned End) {<br>
+ auto StartPos = Code.rfind('\n', Start);<br>
+ StartPos = (StartPos == llvm::StringRef::npos) ? 0 : StartPos + 1;<br>
+<br>
+ auto EndPos = Code.find("\n", End);<br>
+ if (EndPos == llvm::StringRef::npos)<br>
+ EndPos = Code.size();<br>
+<br>
+ llvm::SmallVector<llvm::<wbr>StringRef, 8> Lines;<br>
+ Code.substr(StartPos, EndPos - StartPos).split(Lines, '\n');<br>
+ for (llvm::StringRef Line : Lines)<br>
+ if (Line.size() > ColumnLimit)<br>
+ return true;<br>
+ return false;<br>
+}<br>
+<br>
+std::vector<Range><br>
+getRangesForFormating(llvm::<wbr>StringRef Code, unsigned ColumnLimit,<br>
+ ApplyChangesSpec::FormatOption Format,<br>
+ const clang::tooling::Replacements &Replaces) {<br>
+ // kNone suppresses formatting entirely.<br>
+ if (Format == ApplyChangesSpec::kNone)<br>
+ return {};<br>
+ std::vector<clang::tooling::<wbr>Range> Ranges;<br>
+ // This works assuming that replacements are ordered by offset.<br>
+ // FIXME: use `getAffectedRanges()` to calculate when it does not include '\n'<br>
+ // at the end of an insertion in affected ranges.<br>
+ int Offset = 0;<br>
+ for (const clang::tooling::Replacement &R : Replaces) {<br>
+ int Start = R.getOffset() + Offset;<br>
+ int End = Start + R.getReplacementText().size();<br>
+ if (!R.getReplacementText().<wbr>empty() &&<br>
+ R.getReplacementText().back() == '\n' && R.getLength() == 0 &&<br>
+ R.getOffset() > 0 && R.getOffset() <= Code.size() &&<br>
+ Code[R.getOffset() - 1] == '\n')<br>
+ // If we are inserting at the start of a line and the replacement ends in<br>
+ // a newline, we don't need to format the subsequent line.<br>
+ --End;<br>
+ Offset += R.getReplacementText().size() - R.getLength();<br>
+<br>
+ if (Format == ApplyChangesSpec::kAll ||<br>
+ violatesColumnLimit(Code, ColumnLimit, Start, End))<br>
+ Ranges.emplace_back(Start, End - Start);<br>
+ }<br>
+ return Ranges;<br>
+}<br>
+<br>
+inline llvm::Error make_string_error(const llvm::Twine &Message) {<br>
+ return llvm::make_error<llvm::<wbr>StringError>(Message,<br>
+ llvm::inconvertibleErrorCode()<wbr>);<br>
+}<br>
+<br>
+// Creates replacements for inserting/deleting #include headers.<br>
+llvm::Expected<Replacements><br>
+createReplacementsForHeaders(<wbr>llvm::StringRef FilePath, llvm::StringRef Code,<br>
+ llvm::ArrayRef<AtomicChange> Changes,<br>
+ const format::FormatStyle &Style) {<br>
+ // Create header insertion/deletion replacements to be cleaned up<br>
+ // (i.e. converted to real insertion/deletion replacements).<br>
+ Replacements HeaderReplacements;<br>
+ for (const auto &Change : Changes) {<br>
+ for (llvm::StringRef Header : Change.getInsertedHeaders()) {<br>
+ std::string EscapedHeader =<br>
+ Header.startswith("<") || Header.startswith("\"")<br>
+ ? Header.str()<br>
+ : ("\"" + Header + "\"").str();<br>
+ std::string ReplacementText = "#include " + EscapedHeader;<br>
+ // Offset UINT_MAX and length 0 indicate that the replacement is a header<br>
+ // insertion.<br>
+ llvm::Error Err = HeaderReplacements.add(<br>
+ tooling::Replacement(FilePath, UINT_MAX, 0, ReplacementText));<br>
+ if (Err)<br>
+ return std::move(Err);<br>
+ }<br>
+ for (const std::string &Header : Change.getRemovedHeaders()) {<br>
+ // Offset UINT_MAX and length 1 indicate that the replacement is a header<br>
+ // deletion.<br>
+ llvm::Error Err =<br>
+ HeaderReplacements.add(<wbr>Replacement(FilePath, UINT_MAX, 1, Header));<br>
+ if (Err)<br>
+ return std::move(Err);<br>
+ }<br>
+ }<br>
+<br>
+ // cleanupAroundReplacements() converts header insertions/deletions into<br>
+ // actual replacements that add/remove headers at the right location.<br>
+ return clang::format::<wbr>cleanupAroundReplacements(<wbr>Code, HeaderReplacements,<br>
+ Style);<br>
+}<br>
+<br>
+// Combine replacements in all Changes as a `Replacements`. This ignores the<br>
+// file path in all replacements and replaces them with \p FilePath.<br>
+llvm::Expected<Replacements><br>
+combineReplacementsInChanges(<wbr>llvm::StringRef FilePath,<br>
+ llvm::ArrayRef<AtomicChange> Changes) {<br>
+ Replacements Replaces;<br>
+ for (const auto &Change : Changes)<br>
+ for (const auto &R : Change.getReplacements())<br>
+ if (auto Err = Replaces.add(Replacement(<br>
+ FilePath, R.getOffset(), R.getLength(), R.getReplacementText())))<br>
+ return std::move(Err);<br>
+ return Replaces;<br>
+}<br>
+<br>
+} // end namespace<br>
<br>
AtomicChange::AtomicChange(<wbr>const SourceManager &SM,<br>
SourceLocation KeyPosition) {<br>
@@ -168,5 +278,74 @@ void AtomicChange::removeHeader(<wbr>llvm::St<br>
RemovedHeaders.push_back(<wbr>Header);<br>
}<br>
<br>
+llvm::Expected<std::string><br>
+applyAtomicChanges(llvm::<wbr>StringRef FilePath, llvm::StringRef Code,<br>
+ llvm::ArrayRef<AtomicChange> Changes,<br>
+ const ApplyChangesSpec &Spec) {<br>
+ llvm::Expected<Replacements> HeaderReplacements =<br>
+ createReplacementsForHeaders(<wbr>FilePath, Code, Changes, Spec.Style);<br>
+ if (!HeaderReplacements)<br>
+ return make_string_error(<br>
+ "Failed to create replacements for header changes: " +<br>
+ llvm::toString(<wbr>HeaderReplacements.takeError()<wbr>));<br>
+<br>
+ llvm::Expected<Replacements> Replaces =<br>
+ combineReplacementsInChanges(<wbr>FilePath, Changes);<br>
+ if (!Replaces)<br>
+ return make_string_error("Failed to combine replacements in all changes: " +<br>
+ llvm::toString(Replaces.<wbr>takeError()));<br>
+<br>
+ Replacements AllReplaces = std::move(*Replaces);<br>
+ for (const auto &R : *HeaderReplacements) {<br>
+ llvm::Error Err = AllReplaces.add(R);<br>
+ if (Err)<br>
+ return make_string_error(<br>
+ "Failed to combine existing replacements with header replacements: " +<br>
+ llvm::toString(std::move(Err))<wbr>);<br>
+ }<br>
+<br>
+ if (Spec.Cleanup) {<br>
+ llvm::Expected<Replacements> CleanReplaces =<br>
+ format::<wbr>cleanupAroundReplacements(<wbr>Code, AllReplaces, Spec.Style);<br>
+ if (!CleanReplaces)<br>
+ return make_string_error("Failed to cleanup around replacements: " +<br>
+ llvm::toString(CleanReplaces.<wbr>takeError()));<br>
+ AllReplaces = std::move(*CleanReplaces);<br>
+ }<br>
+<br>
+ // Apply all replacements.<br>
+ llvm::Expected<std::string> ChangedCode =<br>
+ applyAllReplacements(Code, AllReplaces);<br>
+ if (!ChangedCode)<br>
+ return make_string_error("Failed to apply all replacements: " +<br>
+ llvm::toString(ChangedCode.<wbr>takeError()));<br>
+<br>
+ // Sort inserted headers. This is done even if other formatting is turned off<br>
+ // as incorrectly sorted headers are always just wrong, it's not a matter of<br>
+ // taste.<br>
+ Replacements HeaderSortingReplacements = format::sortIncludes(<br>
+ Spec.Style, *ChangedCode, AllReplaces.getAffectedRanges(<wbr>), FilePath);<br>
+ ChangedCode = applyAllReplacements(*<wbr>ChangedCode, HeaderSortingReplacements);<br>
+ if (!ChangedCode)<br>
+ return make_string_error(<br>
+ "Failed to apply replacements for sorting includes: " +<br>
+ llvm::toString(ChangedCode.<wbr>takeError()));<br>
+<br>
+ AllReplaces = AllReplaces.merge(<wbr>HeaderSortingReplacements);<br>
+<br>
+ std::vector<Range> FormatRanges = getRangesForFormating(<br>
+ *ChangedCode, Spec.Style.ColumnLimit, Spec.Format, AllReplaces);<br>
+ if (!FormatRanges.empty()) {<br>
+ Replacements FormatReplacements =<br>
+ format::reformat(Spec.Style, *ChangedCode, FormatRanges, FilePath);<br>
+ ChangedCode = applyAllReplacements(*<wbr>ChangedCode, FormatReplacements);<br>
+ if (!ChangedCode)<br>
+ return make_string_error(<br>
+ "Failed to apply replacements for formatting changed code: " +<br>
+ llvm::toString(ChangedCode.<wbr>takeError()));<br>
+ }<br>
+ return ChangedCode;<br>
+}<br>
+<br>
} // end namespace tooling<br>
} // end namespace clang<br>
<br>
Modified: cfe/trunk/unittests/Tooling/<wbr>RefactoringTest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RefactoringTest.cpp?rev=298913&r1=298912&r2=298913&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/unittests/<wbr>Tooling/RefactoringTest.cpp?<wbr>rev=298913&r1=298912&r2=<wbr>298913&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/unittests/Tooling/<wbr>RefactoringTest.cpp (original)<br>
+++ cfe/trunk/unittests/Tooling/<wbr>RefactoringTest.cpp Tue Mar 28 08:05:32 2017<br>
@@ -1290,5 +1290,435 @@ TEST_F(AtomicChangeTest, InsertAfterWith<br>
Replacement(Context.Sources, SourceLocation(), 0, "b")));<br>
}<br>
<br>
+class ApplyAtomicChangesTest : public ::testing::Test {<br>
+protected:<br>
+ ApplyAtomicChangesTest() : FilePath("file.cc") {<br>
+ Spec.Cleanup = true;<br>
+ Spec.Format = ApplyChangesSpec::kAll;<br>
+ Spec.Style = format::getLLVMStyle();<br>
+ }<br>
+<br>
+ ~ApplyAtomicChangesTest() override {}<br>
+<br>
+ void setInput(llvm::StringRef Input) {<br>
+ Code = Input;<br>
+ FID = Context.createInMemoryFile(<wbr>FilePath, Code);<br>
+ }<br>
+<br>
+ SourceLocation getLoc(unsigned Offset) const {<br>
+ return Context.Sources.<wbr>getLocForStartOfFile(FID).<wbr>getLocWithOffset(Offset);<br>
+ }<br>
+<br>
+ AtomicChange replacementToAtomicChange(<wbr>llvm::StringRef Key, unsigned Offset,<br>
+ unsigned Length,<br>
+ llvm::StringRef Text) {<br>
+ AtomicChange Change(FilePath, Key);<br>
+ llvm::Error Err =<br>
+ Change.replace(Context.<wbr>Sources, getLoc(Offset), Length, Text);<br>
+ EXPECT_FALSE(Err);<br>
+ return Change;<br>
+ }<br>
+<br>
+ std::string rewrite(bool FailureExpected = false) {<br>
+ llvm::Expected<std::string> ChangedCode =<br>
+ applyAtomicChanges(FilePath, Code, Changes, Spec);<br>
+ EXPECT_EQ(FailureExpected, !ChangedCode);<br>
+ if (!ChangedCode) {<br>
+ llvm::errs() << "Failed to apply changes: "<br>
+ << llvm::toString(ChangedCode.<wbr>takeError()) << "\n";<br>
+ return "";<br>
+ }<br>
+ return *ChangedCode;<br>
+ }<br>
+<br>
+ RewriterTestContext Context;<br>
+ FileID FID;<br>
+ ApplyChangesSpec Spec;<br>
+ std::string Code;<br>
+ std::string FilePath;<br>
+ llvm::SmallVector<<wbr>AtomicChange, 8> Changes;<br>
+};<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, BasicRefactoring) {<br>
+ setInput("int a;");<br>
+ AtomicChange Change(FilePath, "key1");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 4, 1, "b"));<br>
+ EXPECT_EQ("int b;", rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, SeveralRefactorings) {<br>
+ setInput("int a;\n"<br>
+ "int b;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 0, 3, "float"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key2", 4, 1, "f"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key3", 11, 1, "g"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key4", 7, 3, "float"));<br>
+ EXPECT_EQ("float f;\n"<br>
+ "float g;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, IgnorePathsInRefactorings) {<br>
+ setInput("int a;\n"<br>
+ "int b;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 4, 1, "aa"));<br>
+<br>
+ FileID ID = Context.createInMemoryFile("<wbr>AnotherFile", "12345678912345");<br>
+ Changes.emplace_back("<wbr>AnotherFile", "key2");<br>
+ auto Err = Changes.back().replace(<br>
+ Context.Sources,<br>
+ Context.Sources.<wbr>getLocForStartOfFile(ID).<wbr>getLocWithOffset(11), 1, "bb");<br>
+ ASSERT_TRUE(!Err);<br>
+ EXPECT_EQ("int aa;\n"<br>
+ "int bb;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, AppliesDuplicateInsertions) {<br>
+ setInput("int a;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 5, 0, "b"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key2", 5, 0, "b"));<br>
+ EXPECT_EQ("int abb;", rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, BailsOnOverlappingRefactorings<wbr>) {<br>
+ setInput("int a;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 0, 5, "float f"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key2", 4, 1, "b"));<br>
+ EXPECT_EQ("", rewrite(/*FailureExpected=*/<wbr>true));<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, BasicReformatting) {<br>
+ setInput("int a;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 5, 1, "b"));<br>
+ EXPECT_EQ("int b;", rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, OnlyFormatWhenViolateColumnLim<wbr>its) {<br>
+ Spec.Format = ApplyChangesSpec::kViolations;<br>
+ Spec.Style.ColumnLimit = 8;<br>
+ setInput("int a;\n"<br>
+ "int a;\n"<br>
+ "int aaaaaaaa;\n");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 5, 1, "x"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key2", 15, 1, "x"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key3", 23, 8, "xx"));<br>
+ EXPECT_EQ("int x;\n"<br>
+ "int x;\n"<br>
+ "int xx;\n",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, LastLineViolateColumnLimits) {<br>
+ Spec.Format = ApplyChangesSpec::kViolations;<br>
+ Spec.Style.ColumnLimit = 8;<br>
+ setInput("int a;\n"<br>
+ "int a;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 0, 1, "i"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key2", 15, 2, "y;"));<br>
+ EXPECT_EQ("int a;\n"<br>
+ "int y;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, LastLineWithNewlineViolateColu<wbr>mnLimits) {<br>
+ Spec.Format = ApplyChangesSpec::kViolations;<br>
+ Spec.Style.ColumnLimit = 8;<br>
+ setInput("int a;\n"<br>
+ "int a;\n");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 0, 1, "i"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key2", 14, 3, "y;\n"));<br>
+ EXPECT_EQ("int a;\n"<br>
+ "int y;\n",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, Longer) {<br>
+ setInput("int a;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 5, 1, "bbb"));<br>
+ EXPECT_EQ("int bbb;", rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, Shorter) {<br>
+ setInput("int aaa;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 5, 3, "b"));<br>
+ EXPECT_EQ("int b;", rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, OnlyFormatChangedLines) {<br>
+ setInput("int aaa;\n"<br>
+ "int a = b;\n"<br>
+ "int bbb;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 14, 1, "b"));<br>
+ EXPECT_EQ("int aaa;\n"<br>
+ "int b = b;\n"<br>
+ "int bbb;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, DisableFormatting) {<br>
+ Spec.Format = ApplyChangesSpec::kNone;<br>
+ setInput("int aaa;\n"<br>
+ "int a = b;\n"<br>
+ "int bbb;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 14, 1, "b"));<br>
+ EXPECT_EQ("int aaa;\n"<br>
+ "int b = b;\n"<br>
+ "int bbb;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, AdaptsToLocalPointerStyle) {<br>
+ setInput("int *aaa;\n"<br>
+ "int *bbb;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 0, 0, "int* ccc;\n"));<br>
+ EXPECT_EQ("int *ccc;\n"<br>
+ "int *aaa;\n"<br>
+ "int *bbb;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, AcceptsSurroundingFormatting) {<br>
+ setInput(" int aaa;\n"<br>
+ " int a = b;\n"<br>
+ " int bbb;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 20, 1, "b"));<br>
+ EXPECT_EQ(" int aaa;\n"<br>
+ " int b = b;\n"<br>
+ " int bbb;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, BailsOutOnConflictingChanges) {<br>
+ setInput("int c;\n"<br>
+ "int f;");<br>
+ // Insertions at the same offset are only allowed in the same AtomicChange.<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 0, 0, "int a;\n"));<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key2", 0, 0, "int b;\n"));<br>
+ EXPECT_EQ("", rewrite(/*FailureExpected=*/<wbr>true));<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, InsertsNewIncludesInRightOrder<wbr>) {<br>
+ setInput("int a;");<br>
+ Changes.emplace_back(FilePath, "key1");<br>
+ Changes.back().addHeader("b");<br>
+ Changes.back().addHeader("c");<br>
+ Changes.emplace_back(FilePath, "key2");<br>
+ Changes.back().addHeader("a");<br>
+ EXPECT_EQ("#include \"a\"\n"<br>
+ "#include \"b\"\n"<br>
+ "#include \"c\"\n"<br>
+ "int a;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, RemoveAndSortIncludes) {<br>
+ setInput(R"(<br>
+#include "a"<br>
+#include "b"<br>
+#include "c"<br>
+<br>
+int a;<br>
+ )");<br>
+ Changes.emplace_back(FilePath, "key1");<br>
+ Changes.back().removeHeader("<wbr>b");<br>
+ EXPECT_EQ(R"(<br>
+#include "a"<br>
+#include "c"<br>
+<br>
+int a;<br>
+ )",<br>
+ rewrite());<br>
+}<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, InsertsSystemIncludes) {<br>
+ setInput("#include <asys>\n"<br>
+ "#include <csys>\n"<br>
+ "\n"<br>
+ "#include \"a\"\n"<br>
+ "#include \"c\"\n");<br>
+ Changes.emplace_back(FilePath, "key1");<br>
+ Changes.back().addHeader("<<wbr>asys>"); // Already exists.<br>
+ Changes.back().addHeader("<b>"<wbr>);<br>
+ Changes.back().addHeader("<d>"<wbr>);<br>
+ Changes.back().addHeader("\"b-<wbr>already-escaped\"");<br>
+ EXPECT_EQ("#include <asys>\n"<br>
+ "#include <b>\n"<br>
+ "#include <csys>\n"<br>
+ "#include <d>\n"<br>
+ "\n"<br>
+ "#include \"a\"\n"<br>
+ "#include \"b-already-escaped\"\n"<br>
+ "#include \"c\"\n",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, RemoveSystemIncludes) {<br>
+ setInput(R"(<br>
+#include <a><br>
+#include <b><br>
+<br>
+#include "c"<br>
+<br>
+int a;<br>
+ )");<br>
+ Changes.emplace_back(FilePath, "key1");<br>
+ Changes.back().removeHeader("<<wbr>a>");<br>
+ EXPECT_EQ(R"(<br>
+#include <b><br>
+<br>
+#include "c"<br>
+<br>
+int a;<br>
+ )",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest,<br>
+ DoNotFormatFollowingLinesIfSep<wbr>aratedWithNewline) {<br>
+ setInput("#ifndef __H__\n"<br>
+ "#define __H__\n"<br>
+ "#include \"b\"\n"<br>
+ "\n"<br>
+ "int a;\n"<br>
+ "int a;\n"<br>
+ "int a;\n"<br>
+ "#endif // __H__\n");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1",<br>
+ llvm::StringRef("#ifndef __H__\n"<br>
+ "#define __H__\n"<br>
+ "\n"<br>
+ "#include \"b\"\n"<br>
+ "int a;\n"<br>
+ "int ")<br>
+ .size(),<br>
+ 1, "b"));<br>
+ Changes.back().addHeader("a");<br>
+ EXPECT_EQ("#ifndef __H__\n"<br>
+ "#define __H__\n"<br>
+ "#include \"a\"\n"<br>
+ "#include \"b\"\n"<br>
+ "\n"<br>
+ "int a;\n"<br>
+ "int b;\n"<br>
+ "int a;\n"<br>
+ "#endif // __H__\n",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, FormatsCorrectLineWhenHeaderIs<wbr>Removed) {<br>
+ setInput("#include \"a\"\n"<br>
+ "\n"<br>
+ "int a;\n"<br>
+ "int a;\n"<br>
+ "int a;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 27, 1, "b"));<br>
+ Changes.back().removeHeader("<wbr>a");<br>
+ EXPECT_EQ("\n"<br>
+ "int a;\n"<br>
+ "int b;\n"<br>
+ "int a;",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, CleansUpCtorInitializers) {<br>
+ setInput("A::A() : a(), b() {}\n"<br>
+ "A::A() : a(), b() {}\n"<br>
+ "A::A() : a(), b() {}\n"<br>
+ "A::A() : a()/**/, b() {}\n"<br>
+ "A::A() : a() ,// \n"<br>
+ " /**/ b() {}");<br>
+ Changes.emplace_back(FilePath, "key1");<br>
+ auto Err = Changes.back().replace(<wbr>Context.Sources, getLoc(9), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(35), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(51), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(56), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(72), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(97), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(118), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ EXPECT_EQ("A::A() : b() {}\n"<br>
+ "A::A() : a() {}\n"<br>
+ "A::A() {}\n"<br>
+ "A::A() : b() {}\n"<br>
+ "A::A() {}",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, CleansUpParameterLists) {<br>
+ setInput("void f(int i, float f, string s);\n"<br>
+ "f(1, 2.0f, \"a\");\n"<br>
+ "g(1, 1);");<br>
+ Changes.emplace_back(FilePath, "key1");<br>
+ auto Err = Changes.back().replace(<wbr>Context.Sources, getLoc(7), 5, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(23), 8, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(36), 1, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(45), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(53), 1, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(56), 1, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ EXPECT_EQ("void f(float f);\n"<br>
+ "f(2.0f);\n"<br>
+ "g();",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, DisableCleanup) {<br>
+ Spec.Cleanup = false;<br>
+ setInput("void f(int i, float f, string s);\n"<br>
+ "f(1, 2.0f, \"a\");\n"<br>
+ "g(1, 1);");<br>
+ Changes.emplace_back(FilePath, "key1");<br>
+ auto Err = Changes.back().replace(<wbr>Context.Sources, getLoc(7), 5, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(23), 8, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(36), 1, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(45), 3, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(53), 1, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(56), 1, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ EXPECT_EQ("void f(, float f, );\n"<br>
+ "f(, 2.0f, );\n"<br>
+ "g(, );",<br>
+ rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, EverythingDeleted) {<br>
+ setInput("int a;");<br>
+ Changes.push_back(<wbr>replacementToAtomicChange("<wbr>key1", 0, 6, ""));<br>
+ EXPECT_EQ("", rewrite());<br>
+}<br>
+<br>
+TEST_F(<wbr>ApplyAtomicChangesTest, DoesNotDeleteInserts) {<br>
+ setInput("int a;\n"<br>
+ "int b;");<br>
+ Changes.emplace_back(FilePath, "key1");<br>
+ auto Err = Changes.back().replace(<wbr>Context.Sources, getLoc(4), 1, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(4), 0, "b");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(11), 0, "a");<br>
+ ASSERT_TRUE(!Err);<br>
+ Err = Changes.back().replace(<wbr>Context.Sources, getLoc(11), 1, "");<br>
+ ASSERT_TRUE(!Err);<br>
+ EXPECT_EQ("int b;\n"<br>
+ "int a;",<br>
+ rewrite());<br>
+}<br>
+<br>
} // end namespace tooling<br>
} // end namespace clang<br>
<br>
<br>
______________________________<wbr>_________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>