<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>