[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-11-05 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 121656.
johannes added a comment.

update


https://reviews.llvm.org/D37005

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTPatch.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/ASTPatch.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  test/Tooling/clang-diff-patch.test
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp
  unittests/Tooling/ASTPatchTest.cpp
  unittests/Tooling/CMakeLists.txt

Index: unittests/Tooling/CMakeLists.txt
===
--- unittests/Tooling/CMakeLists.txt
+++ unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
 endif()
 
 add_clang_unittest(ToolingTests
+  ASTPatchTest.cpp
   ASTSelectionTest.cpp
   CastExprTest.cpp
   CommentHandlerTest.cpp
@@ -45,4 +46,5 @@
   clangTooling
   clangToolingCore
   clangToolingRefactor
+  clangToolingASTDiff
   )
Index: unittests/Tooling/ASTPatchTest.cpp
===
--- /dev/null
+++ unittests/Tooling/ASTPatchTest.cpp
@@ -0,0 +1,265 @@
+//===- unittest/Tooling/ASTPatchTest.cpp --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Program.h"
+#include "gtest/gtest.h"
+#include 
+
+using namespace clang;
+using namespace tooling;
+
+std::string ReadShellCommand(const Twine ) {
+  char Buffer[128];
+  std::string Result;
+  std::shared_ptr Pipe(popen(Command.str().data(), "r"), pclose);
+  if (!Pipe)
+return Result;
+  while (!feof(Pipe.get())) {
+if (fgets(Buffer, 128, Pipe.get()) != nullptr)
+  Result += Buffer;
+  }
+  return Result;
+}
+
+class ASTPatchTest : public ::testing::Test {
+  llvm::SmallString<256> TargetFile, ExpectedFile;
+  std::array TargetFileArray;
+
+public:
+  void SetUp() override {
+std::string Suffix = "cpp";
+ASSERT_FALSE(llvm::sys::fs::createTemporaryFile(
+"clang-libtooling-patch-target", Suffix, TargetFile));
+ASSERT_FALSE(llvm::sys::fs::createTemporaryFile(
+"clang-libtooling-patch-expected", Suffix, ExpectedFile));
+TargetFileArray[0] = TargetFile.str();
+  }
+  void TearDown() override {
+llvm::sys::fs::remove(TargetFile);
+llvm::sys::fs::remove(ExpectedFile);
+  }
+
+  void WriteFile(StringRef Filename, StringRef Contents) {
+std::ofstream OS(Filename);
+OS << Contents.str();
+assert(OS.good());
+  }
+
+  std::string ReadFile(StringRef Filename) {
+std::ifstream IS(Filename);
+std::stringstream OS;
+OS << IS.rdbuf();
+assert(IS.good());
+return OS.str();
+  }
+
+  std::string formatExpected(StringRef Code) {
+WriteFile(ExpectedFile, Code);
+return ReadShellCommand("clang-format " + ExpectedFile);
+  }
+
+  llvm::Expected patchResult(const char *SrcCode,
+  const char *DstCode,
+  const char *TargetCode) {
+std::unique_ptr SrcAST = buildASTFromCode(SrcCode),
+ DstAST = buildASTFromCode(DstCode);
+if (!SrcAST || !DstAST) {
+  if (!SrcAST)
+llvm::errs() << "Failed to build AST from code:\n" << SrcCode << "\n";
+  if (!DstAST)
+llvm::errs() << "Failed to build AST from code:\n" << DstCode << "\n";
+  return llvm::make_error(
+  diff::patching_error::failed_to_build_AST);
+}
+
+diff::SyntaxTree Src(*SrcAST);
+diff::SyntaxTree Dst(*DstAST);
+
+WriteFile(TargetFile, TargetCode);
+FixedCompilationDatabase Compilations(".", std::vector());
+RefactoringTool TargetTool(Compilations, TargetFileArray);
+diff::ComparisonOptions Options;
+
+if (auto Err = diff::patch(TargetTool, Src, Dst, Options, /*Debug=*/false))
+  return std::move(Err);
+return ReadShellCommand("clang-format " + TargetFile);
+  }
+
+#define APPEND_NEWLINE(x) x "\n"
+// use macros for this to make test failures have proper line numbers
+#define PATCH(Src, Dst, Target, ExpectedResult)\
+  {\
+llvm::Expected Result = patchResult(  \
+APPEND_NEWLINE(Src), APPEND_NEWLINE(Dst), APPEND_NEWLINE(Target)); \
+ASSERT_TRUE(bool(Result)); \
+EXPECT_EQ(Result.get(), formatExpected(APPEND_NEWLINE(ExpectedResult)));   \
+  }
+#define PATCH_ERROR(Src, Dst, Target, ErrorCode)   \
+  {

[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-29 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: include/clang/Tooling/ASTDiff/ASTDiff.h:73
 public:
+  /// Empty (invalid) SyntaxTree.
+  SyntaxTree();

arphaman wrote:
> Why do you need to create an empty tree? What about using llvm::Optional 
> instead?
ok, i use optional now instead in the unittest


https://reviews.llvm.org/D37005



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-29 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 113078.
johannes added a comment.

fixes


https://reviews.llvm.org/D37005

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTPatch.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/ASTPatch.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  test/Tooling/clang-diff-patch.test
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp
  unittests/Tooling/ASTDiffTest.cpp
  unittests/Tooling/CMakeLists.txt

Index: unittests/Tooling/CMakeLists.txt
===
--- unittests/Tooling/CMakeLists.txt
+++ unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
 endif()
 
 add_clang_unittest(ToolingTests
+  ASTDiffTest.cpp
   ASTSelectionTest.cpp
   CastExprTest.cpp
   CommentHandlerTest.cpp
@@ -43,4 +44,5 @@
   clangTooling
   clangToolingCore
   clangToolingRefactor
+  clangToolingASTDiff
   )
Index: unittests/Tooling/ASTDiffTest.cpp
===
--- /dev/null
+++ unittests/Tooling/ASTDiffTest.cpp
@@ -0,0 +1,85 @@
+//===- unittest/Tooling/ASTDiffTest.cpp ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+
+static std::string patchResult(std::array Codes) {
+  llvm::Optional Trees[3];
+  std::unique_ptr ASTs[3];
+  for (int I = 0; I < 3; I++) {
+ASTs[I] = buildASTFromCode(Codes[I]);
+if (!ASTs[I]) {
+  llvm::errs() << "Failed to build AST from code:\n" << Codes[I] << "\n";
+  return "";
+}
+Trees[I].emplace(*ASTs[I]);
+  }
+
+  diff::ComparisonOptions Options;
+  std::string TargetDstCode;
+  llvm::raw_string_ostream OS(TargetDstCode);
+  if (!diff::patch(/*ModelSrc=*/*Trees[0], /*ModelDst=*/*Trees[1],
+   /*TargetSrc=*/*Trees[2], Options, OS))
+return "";
+  return OS.str();
+}
+
+// abstract the EXPECT_EQ call so that the code snippets align properly
+// use macros for this to make test failures have proper line numbers
+#define PATCH(Preamble, ModelSrc, ModelDst, Target, Expected)  \
+  EXPECT_EQ(patchResult({{std::string(Preamble) + ModelSrc,\
+  std::string(Preamble) + ModelDst,\
+  std::string(Preamble) + Target}}),   \
+std::string(Preamble) + Expected)
+
+TEST(ASTDiff, TestDeleteArguments) {
+  PATCH(R"(void printf(const char *, ...);)",
+R"(void foo(int x) { printf("%d", x, x); })",
+R"(void foo(int x) { printf("%d", x); })",
+R"(void foo(int x) { printf("different string %d", x, x); })",
+R"(void foo(int x) { printf("different string %d", x); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo ( 1 + 1); })",
+R"(void test1() { foo ( ); })",
+R"(void test2() { foo ( 1 + 1 ); })",
+R"(void test2() { foo (  ); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2 + 2); })",
+R"(void test1() { foo (2 + 2); })",
+R"(void test2() { foo (/*L*/ 0 /*R*/ , 2 + 2); })",
+R"(void test2() { foo (/*L*/  2 + 2); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2); })",
+R"(void test1() { foo (1); })",
+R"(void test2() { foo (0, /*L*/ 0 /*R*/); })",
+R"(void test2() { foo (0 /*R*/); })");
+}
+
+TEST(ASTDiff, TestDeleteDecls) {
+  PATCH(R"()",
+R"()",
+R"()",
+R"()",
+R"()");
+
+  PATCH(R"()",
+R"(void foo(){})",
+R"()",
+R"(int x; void foo() {;;} int y;)",
+R"(int x;  int y;)");
+}
Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -13,6 +13,7 @@
 //===--===//
 
 #include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/CommandLine.h"
@@ -42,6 +43,12 @@
   cl::desc("Output a side-by-side diff in HTML."),
   cl::init(false), cl::cat(ClangDiffCategory));
 
+static cl::opt
+Patch("patch",
+  cl::desc("Try to apply the edit actions between the two input "
+   "files to the specified target."),
+  cl::desc(""), cl::cat(ClangDiffCategory));
+
 static cl::opt 

[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-29 Thread Alex Lorenz via Phabricator via cfe-commits
arphaman added inline comments.



Comment at: include/clang/Tooling/ASTDiff/ASTDiff.h:73
 public:
+  /// Empty (invalid) SyntaxTree.
+  SyntaxTree();

Why do you need to create an empty tree? What about using llvm::Optional 
instead?



Comment at: include/clang/Tooling/ASTDiff/ASTDiff.h:82
+  SyntaxTree(SyntaxTree &);
+  SyntaxTree =(SyntaxTree &);
+  explicit SyntaxTree(const SyntaxTree );

It's fine to have = default in the header


https://reviews.llvm.org/D37005



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-29 Thread Alex Lorenz via Phabricator via cfe-commits
arphaman added inline comments.



Comment at: include/clang/Tooling/ASTDiff/ASTPatch.h:1
+//===- ASTDiff.h - Structural patching based on ASTDiff ---*- C++ -*- 
-===//
+//

Please update the file name in the comment



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:656
+  if (N.ASTNode.get())
+Range = TemplateArgumentLocations.at( - [0]);
+  else {

You might as well return early here and avoid else entirely 


https://reviews.llvm.org/D37005



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112926.
johannes edited the summary of this revision.
johannes added a comment.

split to ASTDiff/ASTPatch


https://reviews.llvm.org/D37005

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTPatch.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/ASTPatch.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  test/Tooling/clang-diff-patch.test
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp
  unittests/Tooling/ASTDiffTest.cpp
  unittests/Tooling/CMakeLists.txt

Index: unittests/Tooling/CMakeLists.txt
===
--- unittests/Tooling/CMakeLists.txt
+++ unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
 endif()
 
 add_clang_unittest(ToolingTests
+  ASTDiffTest.cpp
   ASTSelectionTest.cpp
   CastExprTest.cpp
   CommentHandlerTest.cpp
@@ -43,4 +44,5 @@
   clangTooling
   clangToolingCore
   clangToolingRefactor
+  clangToolingASTDiff
   )
Index: unittests/Tooling/ASTDiffTest.cpp
===
--- /dev/null
+++ unittests/Tooling/ASTDiffTest.cpp
@@ -0,0 +1,86 @@
+//===- unittest/Tooling/ASTDiffTest.cpp ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+
+static std::string patchResult(std::array Codes) {
+  diff::SyntaxTree Trees[3];
+  std::unique_ptr ASTs[3];
+  std::vector Args = {};
+  for (int I = 0; I < 3; I++) {
+ASTs[I] = buildASTFromCode(Codes[I]);
+if (!ASTs[I]) {
+  llvm::errs() << "Failed to build AST from code:\n" << Codes[I] << "\n";
+  return "";
+}
+Trees[I] = diff::SyntaxTree(*ASTs[I]);
+  }
+
+  diff::ComparisonOptions Options;
+  std::string TargetDstCode;
+  llvm::raw_string_ostream OS(TargetDstCode);
+  if (!diff::patch(/*ModelSrc=*/Trees[0], /*ModelDst=*/Trees[1],
+   /*TargetSrc=*/Trees[2], Options, OS))
+return "";
+  return OS.str();
+}
+
+// abstract the EXPECT_EQ call so that the code snippets align properly
+// use macros for this to make test failures have proper line numbers
+#define PATCH(Preamble, ModelSrc, ModelDst, Target, Expected)  \
+  EXPECT_EQ(patchResult({{std::string(Preamble) + ModelSrc,\
+  std::string(Preamble) + ModelDst,\
+  std::string(Preamble) + Target}}),   \
+std::string(Preamble) + Expected)
+
+TEST(ASTDiff, TestDeleteArguments) {
+  PATCH(R"(void printf(const char *, ...);)",
+R"(void foo(int x) { printf("%d", x, x); })",
+R"(void foo(int x) { printf("%d", x); })",
+R"(void foo(int x) { printf("different string %d", x, x); })",
+R"(void foo(int x) { printf("different string %d", x); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo ( 1 + 1); })",
+R"(void test1() { foo ( ); })",
+R"(void test2() { foo ( 1 + 1 ); })",
+R"(void test2() { foo (  ); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2 + 2); })",
+R"(void test1() { foo (2 + 2); })",
+R"(void test2() { foo (/*L*/ 0 /*R*/ , 2 + 2); })",
+R"(void test2() { foo (/*L*/  2 + 2); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2); })",
+R"(void test1() { foo (1); })",
+R"(void test2() { foo (0, /*L*/ 0 /*R*/); })",
+R"(void test2() { foo (0 /*R*/); })");
+}
+
+TEST(ASTDiff, TestDeleteDecls) {
+  PATCH(R"()",
+R"()",
+R"()",
+R"()",
+R"()");
+
+  PATCH(R"()",
+R"(void foo(){})",
+R"()",
+R"(int x; void foo() {;;} int y;)",
+R"(int x;  int y;)");
+}
Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -13,6 +13,7 @@
 //===--===//
 
 #include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/CommandLine.h"
@@ -42,6 +43,12 @@
   cl::desc("Output a side-by-side diff in HTML."),
   cl::init(false), cl::cat(ClangDiffCategory));
 
+static cl::opt
+Patch("patch",
+  cl::desc("Try to apply the edit actions between the two input "
+   

[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-28 Thread Alex Lorenz via Phabricator via cfe-commits
arphaman added inline comments.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:658
+  SourceRange Range;
+  if (auto *Arg = N.ASTNode.get())
+Range = TemplateArgumentLocations.at( - [0]);

You can drop the `auto *Arg` since Arg is unused.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:1305
 
+struct Patcher {
+  SyntaxTree::Impl , , 

Please move the patcher into a new file.


https://reviews.llvm.org/D37005



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-27 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112845.
johannes retitled this revision from "Add 
include/clang/Tooling/ASTDiff/ASTPatch.h" to "[clang-diff] Initial 
implementation of patching".
johannes edited the summary of this revision.
johannes added a comment.

use rewriter to patch a third AST


https://reviews.llvm.org/D37005

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp
  unittests/Tooling/ASTDiffTest.cpp
  unittests/Tooling/CMakeLists.txt

Index: unittests/Tooling/CMakeLists.txt
===
--- unittests/Tooling/CMakeLists.txt
+++ unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
 endif()
 
 add_clang_unittest(ToolingTests
+  ASTDiffTest.cpp
   ASTSelectionTest.cpp
   CastExprTest.cpp
   CommentHandlerTest.cpp
@@ -43,4 +44,5 @@
   clangTooling
   clangToolingCore
   clangToolingRefactor
+  clangToolingASTDiff
   )
Index: unittests/Tooling/ASTDiffTest.cpp
===
--- /dev/null
+++ unittests/Tooling/ASTDiffTest.cpp
@@ -0,0 +1,85 @@
+//===- unittest/Tooling/ASTDiffTest.cpp ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+
+static std::string patchResult(std::array Codes) {
+  diff::SyntaxTree Trees[3];
+  std::unique_ptr ASTs[3];
+  std::vector Args = {};
+  for (int I = 0; I < 3; I++) {
+ASTs[I] = buildASTFromCode(Codes[I]);
+if (!ASTs[I]) {
+  llvm::errs() << "Failed to build AST from code:\n" << Codes[I] << "\n";
+  return "";
+}
+Trees[I] = diff::SyntaxTree(*ASTs[I]);
+  }
+
+  diff::ComparisonOptions Options;
+  std::string TargetDstCode;
+  llvm::raw_string_ostream OS(TargetDstCode);
+  if (!diff::patch(/*ModelSrc=*/Trees[0], /*ModelDst=*/Trees[1],
+   /*TargetSrc=*/Trees[2], Options, OS))
+return "";
+  return OS.str();
+}
+
+// abstract the EXPECT_EQ call so that the code snippets align properly
+// use macros for this to make test failures have proper line numbers
+#define PATCH(Preamble, ModelSrc, ModelDst, Target, Expected)  \
+  EXPECT_EQ(patchResult({{std::string(Preamble) + ModelSrc,\
+  std::string(Preamble) + ModelDst,\
+  std::string(Preamble) + Target}}),   \
+std::string(Preamble) + Expected)
+
+TEST(ASTDiff, TestDeleteArguments) {
+  PATCH(R"(void printf(const char *, ...);)",
+R"(void foo(int x) { printf("%d", x, x); })",
+R"(void foo(int x) { printf("%d", x); })",
+R"(void foo(int x) { printf("different string %d", x, x); })",
+R"(void foo(int x) { printf("different string %d", x); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo ( 1 + 1); })",
+R"(void test1() { foo ( ); })",
+R"(void test2() { foo ( 1 + 1 ); })",
+R"(void test2() { foo (  ); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2 + 2); })",
+R"(void test1() { foo (2 + 2); })",
+R"(void test2() { foo (/*L*/ 0 /*R*/ , 2 + 2); })",
+R"(void test2() { foo (/*L*/  2 + 2); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2); })",
+R"(void test1() { foo (1); })",
+R"(void test2() { foo (0, /*L*/ 0 /*R*/); })",
+R"(void test2() { foo (0 /*R*/); })");
+}
+
+TEST(ASTDiff, TestDeleteDecls) {
+  PATCH(R"()",
+R"()",
+R"()",
+R"()",
+R"()");
+
+  PATCH(R"()",
+R"(void foo(){})",
+R"()",
+R"(int x; void foo() {;;} int y;)",
+R"(int x;  int y;)");
+}
Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -42,6 +42,12 @@
   cl::desc("Output a side-by-side diff in HTML."),
   cl::init(false), cl::cat(ClangDiffCategory));
 
+static cl::opt
+Patch("patch",
+  cl::desc("Try to apply the edit actions between the two input "
+   "files to the specified target."),
+  cl::desc(""), cl::cat(ClangDiffCategory));
+
 static cl::opt SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -563,6 +569,16 @@
   }
   diff::SyntaxTree