[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2017-02-28 Thread Eric Liu via Phabricator via cfe-commits
ioeric updated this revision to Diff 90011.
ioeric added a comment.

- Addressed review comments.
- Make key customize-able.
- Added replace() and insert() to replace current replacement interfaces.
- s/EditList/AtomicChange/


https://reviews.llvm.org/D27054

Files:
  include/clang/Tooling/Refactoring/AtomicChange.h
  lib/Tooling/CMakeLists.txt
  lib/Tooling/Refactoring/AtomicChange.cpp
  lib/Tooling/Refactoring/CMakeLists.txt
  unittests/Tooling/CMakeLists.txt
  unittests/Tooling/RefactoringTest.cpp

Index: unittests/Tooling/RefactoringTest.cpp
===
--- unittests/Tooling/RefactoringTest.cpp
+++ unittests/Tooling/RefactoringTest.cpp
@@ -26,6 +26,7 @@
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/SmallString.h"
 #include "gtest/gtest.h"
@@ -102,10 +103,10 @@
 
 // Checks that an llvm::Error instance contains a ReplacementError with expected
 // error code, expected new replacement, and expected existing replacement.
-static bool checkReplacementError(
-llvm::Error&& Error, replacement_error ExpectedErr,
-llvm::Optional ExpectedExisting,
-llvm::Optional ExpectedNew) {
+static bool checkReplacementError(llvm::Error &,
+  replacement_error ExpectedErr,
+  llvm::Optional ExpectedExisting,
+  llvm::Optional ExpectedNew) {
   if (!Error) {
 llvm::errs() << "Error is a success.";
 return false;
@@ -1089,5 +1090,187 @@
   EXPECT_TRUE(FileToReplaces.empty());
 }
 
+class AtomicChangeTest : public ::testing::Test {
+  protected:
+void setUp() {
+  DefaultFileID = Context.createInMemoryFile("input.cpp", DefaultCode);
+  DefaultLoc = Context.Sources.getLocForStartOfFile(DefaultFileID)
+   .getLocWithOffset(20);
+  assert(DefaultLoc.isValid() && "Default location must be valid.");
+}
+
+RewriterTestContext Context;
+std::string DefaultCode = std::string(100, 'a');
+unsigned DefaultOffset = 20;
+SourceLocation DefaultLoc;
+FileID DefaultFileID;
+};
+
+TEST_F(AtomicChangeTest, AtomicChangeToYAML) {
+  setUp();
+  AtomicChange Change(Context.Sources, DefaultLoc);
+  llvm::Error Err =
+  Change.insert(Context.Sources, DefaultLoc, "aa", /*InsertAfter=*/false);
+  ASSERT_TRUE(!Err);
+  Err = Change.insert(Context.Sources, DefaultLoc.getLocWithOffset(10), "bb",
+/*InsertAfter=*/false);
+  ASSERT_TRUE(!Err);
+  Change.addHeader("a.h");
+  Change.removeHeader("b.h");
+  std::string YAMLString = Change.toYAMLString();
+
+  // NOTE: If this test starts to fail for no obvious reason, check whitespace.
+  ASSERT_STREQ("---\n"
+   "Key: 'input.cpp:20'\n"
+   "FilePath:input.cpp\n"
+   "Error:   ''\n"
+   "InsertedHeaders: [ a.h ]\n"
+   "RemovedHeaders:  [ b.h ]\n"
+   "Replacements:\n" // Extra whitespace here!
+   "  - FilePath:input.cpp\n"
+   "Offset:  20\n"
+   "Length:  0\n"
+   "ReplacementText: aa\n"
+   "  - FilePath:input.cpp\n"
+   "Offset:  30\n"
+   "Length:  0\n"
+   "ReplacementText: bb\n"
+   "...\n",
+   YAMLString.c_str());
+}
+
+TEST_F(AtomicChangeTest, YAMLToAtomicChange) {
+  setUp();
+  std::string YamlContent = "---\n"
+"Key: 'input.cpp:20'\n"
+"FilePath:input.cpp\n"
+"Error:   'ok'\n"
+"InsertedHeaders: [ a.h ]\n"
+"RemovedHeaders:  [ b.h ]\n"
+"Replacements:\n" // Extra whitespace here!
+"  - FilePath:input.cpp\n"
+"Offset:  20\n"
+"Length:  0\n"
+"ReplacementText: aa\n"
+"  - FilePath:input.cpp\n"
+"Offset:  30\n"
+"Length:  0\n"
+"ReplacementText: bb\n"
+"...\n";
+  AtomicChange ExpectedChange(Context.Sources, DefaultLoc);
+  llvm::Error Err = ExpectedChange.insert(Context.Sources, DefaultLoc, "aa",
+/*InsertAfter=*/false);
+  ASSERT_TRUE(!Err);
+  Err = ExpectedChange.insert(Context.Sources, DefaultLoc.getLocWithOffset(10),
+"bb", /*InsertAfter=*/false);
+  ASSERT_TRUE(!Err);
+
+  

[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Eric Liu via Phabricator via cfe-commits
ioeric updated this revision to Diff 79736.
ioeric added a comment.

- Added replace() and insert() to replace current replacement interfaces.


https://reviews.llvm.org/D27054

Files:
  include/clang/Tooling/Refactoring/EditList.h
  lib/Tooling/CMakeLists.txt
  lib/Tooling/Refactoring/CMakeLists.txt
  lib/Tooling/Refactoring/EditList.cpp
  unittests/Tooling/CMakeLists.txt
  unittests/Tooling/RefactoringTest.cpp

Index: unittests/Tooling/RefactoringTest.cpp
===
--- unittests/Tooling/RefactoringTest.cpp
+++ unittests/Tooling/RefactoringTest.cpp
@@ -26,6 +26,7 @@
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/EditList.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/SmallString.h"
 #include "gtest/gtest.h"
@@ -102,10 +103,10 @@
 
 // Checks that an llvm::Error instance contains a ReplacementError with expected
 // error code, expected new replacement, and expected existing replacement.
-static bool checkReplacementError(
-llvm::Error&& Error, replacement_error ExpectedErr,
-llvm::Optional ExpectedExisting,
-llvm::Optional ExpectedNew) {
+static bool checkReplacementError(llvm::Error &,
+  replacement_error ExpectedErr,
+  llvm::Optional ExpectedExisting,
+  llvm::Optional ExpectedNew) {
   if (!Error) {
 llvm::errs() << "Error is a success.";
 return false;
@@ -1089,5 +1090,187 @@
   EXPECT_TRUE(FileToReplaces.empty());
 }
 
+class EditListTest : public ::testing::Test {
+  protected:
+void setUp() {
+  DefaultFileID = Context.createInMemoryFile("input.cpp", DefaultCode);
+  DefaultLoc = Context.Sources.getLocForStartOfFile(DefaultFileID)
+   .getLocWithOffset(20);
+  assert(DefaultLoc.isValid() && "Default location must be valid.");
+}
+
+RewriterTestContext Context;
+std::string DefaultCode = std::string(100, 'a');
+unsigned DefaultOffset = 20;
+SourceLocation DefaultLoc;
+FileID DefaultFileID;
+};
+
+TEST_F(EditListTest, EditListToYAML) {
+  setUp();
+  EditList Edit(Context.Sources, DefaultLoc);
+  llvm::Error Err =
+  Edit.insert(Context.Sources, DefaultLoc, "aa", /*InsertAfter=*/false);
+  ASSERT_TRUE(!Err);
+  Err = Edit.insert(Context.Sources, DefaultLoc.getLocWithOffset(10), "bb",
+/*InsertAfter=*/false);
+  ASSERT_TRUE(!Err);
+  Edit.addHeader("a.h");
+  Edit.removeHeader("b.h");
+  std::string YAMLString = Edit.toYAMLString();
+
+  // NOTE: If this test starts to fail for no obvious reason, check whitespace.
+  ASSERT_STREQ("---\n"
+   "Key: 'input.cpp:20'\n"
+   "FilePath:input.cpp\n"
+   "Error:   ''\n"
+   "InsertedHeaders: [ a.h ]\n"
+   "RemovedHeaders:  [ b.h ]\n"
+   "Replacements:\n" // Extra whitespace here!
+   "  - FilePath:input.cpp\n"
+   "Offset:  20\n"
+   "Length:  0\n"
+   "ReplacementText: aa\n"
+   "  - FilePath:input.cpp\n"
+   "Offset:  30\n"
+   "Length:  0\n"
+   "ReplacementText: bb\n"
+   "...\n",
+   YAMLString.c_str());
+}
+
+TEST_F(EditListTest, YAMLToEditList) {
+  setUp();
+  std::string YamlContent = "---\n"
+"Key: 'input.cpp:20'\n"
+"FilePath:input.cpp\n"
+"Error:   'ok'\n"
+"InsertedHeaders: [ a.h ]\n"
+"RemovedHeaders:  [ b.h ]\n"
+"Replacements:\n" // Extra whitespace here!
+"  - FilePath:input.cpp\n"
+"Offset:  20\n"
+"Length:  0\n"
+"ReplacementText: aa\n"
+"  - FilePath:input.cpp\n"
+"Offset:  30\n"
+"Length:  0\n"
+"ReplacementText: bb\n"
+"...\n";
+  EditList ExpectedEdit(Context.Sources, DefaultLoc);
+  llvm::Error Err = ExpectedEdit.insert(Context.Sources, DefaultLoc, "aa",
+/*InsertAfter=*/false);
+  ASSERT_TRUE(!Err);
+  Err = ExpectedEdit.insert(Context.Sources, DefaultLoc.getLocWithOffset(10),
+"bb", /*InsertAfter=*/false);
+  ASSERT_TRUE(!Err);
+
+  ExpectedEdit.addHeader("a.h");
+  ExpectedEdit.removeHeader("b.h");
+  ExpectedEdit.setError("ok");
+
+  EditList ActualEdit = 

[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Daniel Jasper via Phabricator via cfe-commits
djasper added inline comments.



Comment at: include/clang/Tooling/Refactoring/EditList.h:82-94
+  /// \brief Adds a replacement that inserts \p Text at \p Loc. If this
+  /// insertion conflicts with an existing insertion (at the same position),
+  /// this will be inserted before the existing insertion. If the conflicting
+  /// replacement is not an insertion, an error is returned.
+  ///
+  /// \returns An llvm::Error carrying ReplacementError on error.
+  llvm::Error insertBefore(const SourceManager , SourceLocation Loc,

ioeric wrote:
> djasper wrote:
> > ioeric wrote:
> > > djasper wrote:
> > > > Do we currently actually need these functions? They seem a bit 
> > > > dangerous.
> > > I think these functions are helpful. Insertion conflict is by far the 
> > > most common conflict I've seen, and several users have asked for ways to 
> > > resolve this. And generally, resolving such conflict is not 
> > > straight-forward. Also notice that these interfaces only resolve 
> > > insertion conflict, other conflicts (e.g. overlaps) will still raise 
> > > errors.
> > > 
> > > Although inserting all texts at one location as a single replacement is 
> > > preferred, I've seen several tools that use these in some way, i.e. 
> > > generating all insertions at the same location is hard or impossible. 
> > I still have several concerns:
> > 1. This interface actually exposes two different layers: It has special 
> > function to insert text and headers and also the functions that add or 
> > merge replacements. IMO, that's not ideal.
> > 2. There is now no function insert() if users actually want to get the 
> > error for a conflict. Yes, they can then instead use addReplacement, but 
> > again, that's a completely separate layer of the interface.
> > 3. The logic of before after AND that this only applies to two conflicting 
> > insertions is a bit hard to explain.
> > 
> > Not sure what the right trade-off for all of these is. Maybe we should just 
> > remove addReplacement and mergeReplacements and instead add a replace() 
> > function? Maybe we should just have a single insert() function with an 
> > optional fourth parameter that can be used to control conflict resolution.
> `replace` + `insert` sounds like a good way to go. Do we want to support 
> resolving general conflicts besides insertions though?
I think that might not be necessary. If so, we might need to see a few use 
cases to be sure to get it right.


https://reviews.llvm.org/D27054



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


[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Eric Liu via Phabricator via cfe-commits
ioeric added inline comments.



Comment at: include/clang/Tooling/Refactoring/EditList.h:82-94
+  /// \brief Adds a replacement that inserts \p Text at \p Loc. If this
+  /// insertion conflicts with an existing insertion (at the same position),
+  /// this will be inserted before the existing insertion. If the conflicting
+  /// replacement is not an insertion, an error is returned.
+  ///
+  /// \returns An llvm::Error carrying ReplacementError on error.
+  llvm::Error insertBefore(const SourceManager , SourceLocation Loc,

djasper wrote:
> ioeric wrote:
> > djasper wrote:
> > > Do we currently actually need these functions? They seem a bit dangerous.
> > I think these functions are helpful. Insertion conflict is by far the most 
> > common conflict I've seen, and several users have asked for ways to resolve 
> > this. And generally, resolving such conflict is not straight-forward. Also 
> > notice that these interfaces only resolve insertion conflict, other 
> > conflicts (e.g. overlaps) will still raise errors.
> > 
> > Although inserting all texts at one location as a single replacement is 
> > preferred, I've seen several tools that use these in some way, i.e. 
> > generating all insertions at the same location is hard or impossible. 
> I still have several concerns:
> 1. This interface actually exposes two different layers: It has special 
> function to insert text and headers and also the functions that add or merge 
> replacements. IMO, that's not ideal.
> 2. There is now no function insert() if users actually want to get the error 
> for a conflict. Yes, they can then instead use addReplacement, but again, 
> that's a completely separate layer of the interface.
> 3. The logic of before after AND that this only applies to two conflicting 
> insertions is a bit hard to explain.
> 
> Not sure what the right trade-off for all of these is. Maybe we should just 
> remove addReplacement and mergeReplacements and instead add a replace() 
> function? Maybe we should just have a single insert() function with an 
> optional fourth parameter that can be used to control conflict resolution.
`replace` + `insert` sounds like a good way to go. Do we want to support 
resolving general conflicts besides insertions though?


https://reviews.llvm.org/D27054



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


[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Daniel Jasper via Phabricator via cfe-commits
djasper added inline comments.



Comment at: include/clang/Tooling/Refactoring/EditList.h:41
+  /// \brief Creates an edit list for a key position.
+  EditList(const SourceManager , SourceLocation KeyPosition);
+

ioeric wrote:
> ioeric wrote:
> > klimek wrote:
> > > ioeric wrote:
> > > > djasper wrote:
> > > > > I wonder whether we should always use a SourceLocation as key or 
> > > > > whether we want to leave this up to the users. E.g. we could make 
> > > > > this take a string parameter and provide a
> > > > > 
> > > > >   string getKeyForLocation(const SourceManager , SourceLocation 
> > > > > KeyPosition);
> > > > I think the key idea of EditList is that it groups a set of edits 
> > > > around a key position. For refactoring, using position as key makes 
> > > > sense to me (since all edits will be around some position), and I 
> > > > couldn't think of other things that are good to be a key here.
> > > An idea would be an edit that just inserts multiple headers, but does not 
> > > directly touch a source location (thus, we might want to key things off 
> > > of files?).
> > We could make the key position the start of the file in this case. But 
> > looks like a customize-able key could potentially enable more 
> > opportunities, I'll go with Daniel's suggestion. Thanks!
> So I tried the `getKeyForLocation` way, but we still need `FilePath` in the 
> constructor to fully initialize an EditList. 
> 
> I think it might be better if we keep the current constructor (since this 
> will be the most-commonly used one IMO) and adds another constructor that 
> takes `FilePath` and `Key`.
Ah, but you might want to include more details in the Key, e.g. the branch or 
source revision. My point is that the user might be better equipped to know 
what a valid key might be. But I am happy to keep it as is and add another 
constructor where we pass in a key when needed.



Comment at: include/clang/Tooling/Refactoring/EditList.h:82-94
+  /// \brief Adds a replacement that inserts \p Text at \p Loc. If this
+  /// insertion conflicts with an existing insertion (at the same position),
+  /// this will be inserted before the existing insertion. If the conflicting
+  /// replacement is not an insertion, an error is returned.
+  ///
+  /// \returns An llvm::Error carrying ReplacementError on error.
+  llvm::Error insertBefore(const SourceManager , SourceLocation Loc,

ioeric wrote:
> djasper wrote:
> > Do we currently actually need these functions? They seem a bit dangerous.
> I think these functions are helpful. Insertion conflict is by far the most 
> common conflict I've seen, and several users have asked for ways to resolve 
> this. And generally, resolving such conflict is not straight-forward. Also 
> notice that these interfaces only resolve insertion conflict, other conflicts 
> (e.g. overlaps) will still raise errors.
> 
> Although inserting all texts at one location as a single replacement is 
> preferred, I've seen several tools that use these in some way, i.e. 
> generating all insertions at the same location is hard or impossible. 
I still have several concerns:
1. This interface actually exposes two different layers: It has special 
function to insert text and headers and also the functions that add or merge 
replacements. IMO, that's not ideal.
2. There is now no function insert() if users actually want to get the error 
for a conflict. Yes, they can then instead use addReplacement, but again, 
that's a completely separate layer of the interface.
3. The logic of before after AND that this only applies to two conflicting 
insertions is a bit hard to explain.

Not sure what the right trade-off for all of these is. Maybe we should just 
remove addReplacement and mergeReplacements and instead add a replace() 
function? Maybe we should just have a single insert() function with an optional 
fourth parameter that can be used to control conflict resolution.



Comment at: lib/Tooling/Refactoring/EditList.cpp:164
+
+llvm::Error EditList::insertAfter(const SourceManager , SourceLocation Loc,
+  llvm::StringRef Text) {

Don't duplicate this code. Add an internal function that takes an extra 
parameter that controls conflict resolution.


https://reviews.llvm.org/D27054



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


[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Eric Liu via Phabricator via cfe-commits
ioeric added inline comments.



Comment at: include/clang/Tooling/Refactoring/EditList.h:41
+  /// \brief Creates an edit list for a key position.
+  EditList(const SourceManager , SourceLocation KeyPosition);
+

ioeric wrote:
> klimek wrote:
> > ioeric wrote:
> > > djasper wrote:
> > > > I wonder whether we should always use a SourceLocation as key or 
> > > > whether we want to leave this up to the users. E.g. we could make this 
> > > > take a string parameter and provide a
> > > > 
> > > >   string getKeyForLocation(const SourceManager , SourceLocation 
> > > > KeyPosition);
> > > I think the key idea of EditList is that it groups a set of edits around 
> > > a key position. For refactoring, using position as key makes sense to me 
> > > (since all edits will be around some position), and I couldn't think of 
> > > other things that are good to be a key here.
> > An idea would be an edit that just inserts multiple headers, but does not 
> > directly touch a source location (thus, we might want to key things off of 
> > files?).
> We could make the key position the start of the file in this case. But looks 
> like a customize-able key could potentially enable more opportunities, I'll 
> go with Daniel's suggestion. Thanks!
So I tried the `getKeyForLocation` way, but we still need `FilePath` in the 
constructor to fully initialize an EditList. 

I think it might be better if we keep the current constructor (since this will 
be the most-commonly used one IMO) and adds another constructor that takes 
`FilePath` and `Key`.


https://reviews.llvm.org/D27054



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


[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Eric Liu via Phabricator via cfe-commits
ioeric updated this revision to Diff 79728.
ioeric added a comment.

- Make key customize-able.


https://reviews.llvm.org/D27054

Files:
  include/clang/Tooling/Refactoring/EditList.h
  lib/Tooling/CMakeLists.txt
  lib/Tooling/Refactoring/CMakeLists.txt
  lib/Tooling/Refactoring/EditList.cpp
  unittests/Tooling/CMakeLists.txt
  unittests/Tooling/RefactoringTest.cpp

Index: unittests/Tooling/RefactoringTest.cpp
===
--- unittests/Tooling/RefactoringTest.cpp
+++ unittests/Tooling/RefactoringTest.cpp
@@ -26,6 +26,7 @@
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/EditList.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/SmallString.h"
 #include "gtest/gtest.h"
@@ -102,10 +103,10 @@
 
 // Checks that an llvm::Error instance contains a ReplacementError with expected
 // error code, expected new replacement, and expected existing replacement.
-static bool checkReplacementError(
-llvm::Error&& Error, replacement_error ExpectedErr,
-llvm::Optional ExpectedExisting,
-llvm::Optional ExpectedNew) {
+static bool checkReplacementError(llvm::Error &,
+  replacement_error ExpectedErr,
+  llvm::Optional ExpectedExisting,
+  llvm::Optional ExpectedNew) {
   if (!Error) {
 llvm::errs() << "Error is a success.";
 return false;
@@ -1089,5 +1090,183 @@
   EXPECT_TRUE(FileToReplaces.empty());
 }
 
+class EditListTest : public ::testing::Test {
+  protected:
+void setUp() {
+  DefaultFileID = Context.createInMemoryFile("input.cpp", DefaultCode);
+  DefaultLoc = Context.Sources.getLocForStartOfFile(DefaultFileID)
+   .getLocWithOffset(20);
+  assert(DefaultLoc.isValid() && "Default location must be valid.");
+}
+
+RewriterTestContext Context;
+std::string DefaultCode = std::string(100, 'a');
+unsigned DefaultOffset = 20;
+SourceLocation DefaultLoc;
+FileID DefaultFileID;
+};
+
+TEST_F(EditListTest, EditListToYAML) {
+  setUp();
+  EditList Edit(Context.Sources, DefaultLoc);
+  llvm::Error Err = Edit.insertBefore(Context.Sources, DefaultLoc, "aa");
+  ASSERT_TRUE(!Err);
+  Err =
+  Edit.insertBefore(Context.Sources, DefaultLoc.getLocWithOffset(10), "bb");
+  ASSERT_TRUE(!Err);
+  Edit.addHeader("a.h");
+  Edit.removeHeader("b.h");
+  std::string YAMLString = Edit.toYAMLString();
+
+  // NOTE: If this test starts to fail for no obvious reason, check whitespace.
+  ASSERT_STREQ("---\n"
+   "Key: 'input.cpp:20'\n"
+   "FilePath:input.cpp\n"
+   "Error:   ''\n"
+   "InsertedHeaders: [ a.h ]\n"
+   "RemovedHeaders:  [ b.h ]\n"
+   "Replacements:\n" // Extra whitespace here!
+   "  - FilePath:input.cpp\n"
+   "Offset:  20\n"
+   "Length:  0\n"
+   "ReplacementText: aa\n"
+   "  - FilePath:input.cpp\n"
+   "Offset:  30\n"
+   "Length:  0\n"
+   "ReplacementText: bb\n"
+   "...\n",
+   YAMLString.c_str());
+}
+
+TEST_F(EditListTest, YAMLToEditList) {
+  setUp();
+  std::string YamlContent = "---\n"
+"Key: 'input.cpp:20'\n"
+"FilePath:input.cpp\n"
+"Error:   'ok'\n"
+"InsertedHeaders: [ a.h ]\n"
+"RemovedHeaders:  [ b.h ]\n"
+"Replacements:\n" // Extra whitespace here!
+"  - FilePath:input.cpp\n"
+"Offset:  20\n"
+"Length:  0\n"
+"ReplacementText: aa\n"
+"  - FilePath:input.cpp\n"
+"Offset:  30\n"
+"Length:  0\n"
+"ReplacementText: bb\n"
+"...\n";
+  EditList ExpectedEdit(Context.Sources, DefaultLoc);
+  llvm::Error Err =
+  ExpectedEdit.insertBefore(Context.Sources, DefaultLoc, "aa");
+  ASSERT_TRUE(!Err);
+  Err = ExpectedEdit.insertBefore(Context.Sources,
+  DefaultLoc.getLocWithOffset(10), "bb");
+  ASSERT_TRUE(!Err);
+
+  ExpectedEdit.addHeader("a.h");
+  ExpectedEdit.removeHeader("b.h");
+  ExpectedEdit.setError("ok");
+
+  EditList ActualEdit = EditList::convertFromYAML(YamlContent);
+  EXPECT_EQ(ExpectedEdit.getKey(), ActualEdit.getKey());
+  EXPECT_EQ(ExpectedEdit.getFilePath(), ActualEdit.getFilePath());
+  

[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Eric Liu via Phabricator via cfe-commits
ioeric added inline comments.



Comment at: include/clang/Tooling/Refactoring/EditList.h:41
+  /// \brief Creates an edit list for a key position.
+  EditList(const SourceManager , SourceLocation KeyPosition);
+

klimek wrote:
> ioeric wrote:
> > djasper wrote:
> > > I wonder whether we should always use a SourceLocation as key or whether 
> > > we want to leave this up to the users. E.g. we could make this take a 
> > > string parameter and provide a
> > > 
> > >   string getKeyForLocation(const SourceManager , SourceLocation 
> > > KeyPosition);
> > I think the key idea of EditList is that it groups a set of edits around a 
> > key position. For refactoring, using position as key makes sense to me 
> > (since all edits will be around some position), and I couldn't think of 
> > other things that are good to be a key here.
> An idea would be an edit that just inserts multiple headers, but does not 
> directly touch a source location (thus, we might want to key things off of 
> files?).
We could make the key position the start of the file in this case. But looks 
like a customize-able key could potentially enable more opportunities, I'll go 
with Daniel's suggestion. Thanks!


https://reviews.llvm.org/D27054



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


[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Eric Liu via Phabricator via cfe-commits
ioeric added inline comments.



Comment at: include/clang/Tooling/Refactoring/EditList.h:41
+  /// \brief Creates an edit list for a key position.
+  EditList(const SourceManager , SourceLocation KeyPosition);
+

djasper wrote:
> I wonder whether we should always use a SourceLocation as key or whether we 
> want to leave this up to the users. E.g. we could make this take a string 
> parameter and provide a
> 
>   string getKeyForLocation(const SourceManager , SourceLocation 
> KeyPosition);
I think the key idea of EditList is that it groups a set of edits around a key 
position. For refactoring, using position as key makes sense to me (since all 
edits will be around some position), and I couldn't think of other things that 
are good to be a key here.



Comment at: include/clang/Tooling/Refactoring/EditList.h:43
+
+  EditList(std::string Key, std::string FilePath, std::string Error,
+   std::vector InsertedHeaders,

djasper wrote:
> Does this need to be public? Specifically, couldn't the YamlTraits create a 
> NormalizedEditList and then convertFromYAML can create the EditList object, 
> which should have access to a private constructor?
You are right! Make this private now.



Comment at: include/clang/Tooling/Refactoring/EditList.h:82-94
+  /// \brief Adds a replacement that inserts \p Text at \p Loc. If this
+  /// insertion conflicts with an existing insertion (at the same position),
+  /// this will be inserted before the existing insertion. If the conflicting
+  /// replacement is not an insertion, an error is returned.
+  ///
+  /// \returns An llvm::Error carrying ReplacementError on error.
+  llvm::Error insertBefore(const SourceManager , SourceLocation Loc,

djasper wrote:
> Do we currently actually need these functions? They seem a bit dangerous.
I think these functions are helpful. Insertion conflict is by far the most 
common conflict I've seen, and several users have asked for ways to resolve 
this. And generally, resolving such conflict is not straight-forward. Also 
notice that these interfaces only resolve insertion conflict, other conflicts 
(e.g. overlaps) will still raise errors.

Although inserting all texts at one location as a single replacement is 
preferred, I've seen several tools that use these in some way, i.e. generating 
all insertions at the same location is hard or impossible. 


https://reviews.llvm.org/D27054



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


[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Eric Liu via Phabricator via cfe-commits
ioeric updated this revision to Diff 79716.
ioeric marked 4 inline comments as done.
ioeric added a comment.

- Addressed review comments.


https://reviews.llvm.org/D27054

Files:
  include/clang/Tooling/Refactoring/EditList.h
  lib/Tooling/CMakeLists.txt
  lib/Tooling/Refactoring/CMakeLists.txt
  lib/Tooling/Refactoring/EditList.cpp
  unittests/Tooling/CMakeLists.txt
  unittests/Tooling/RefactoringTest.cpp

Index: unittests/Tooling/RefactoringTest.cpp
===
--- unittests/Tooling/RefactoringTest.cpp
+++ unittests/Tooling/RefactoringTest.cpp
@@ -26,6 +26,7 @@
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/EditList.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/SmallString.h"
 #include "gtest/gtest.h"
@@ -102,10 +103,10 @@
 
 // Checks that an llvm::Error instance contains a ReplacementError with expected
 // error code, expected new replacement, and expected existing replacement.
-static bool checkReplacementError(
-llvm::Error&& Error, replacement_error ExpectedErr,
-llvm::Optional ExpectedExisting,
-llvm::Optional ExpectedNew) {
+static bool checkReplacementError(llvm::Error &,
+  replacement_error ExpectedErr,
+  llvm::Optional ExpectedExisting,
+  llvm::Optional ExpectedNew) {
   if (!Error) {
 llvm::errs() << "Error is a success.";
 return false;
@@ -1089,5 +1090,183 @@
   EXPECT_TRUE(FileToReplaces.empty());
 }
 
+class EditListTest : public ::testing::Test {
+  protected:
+void setUp() {
+  DefaultFileID = Context.createInMemoryFile("input.cpp", DefaultCode);
+  DefaultLoc = Context.Sources.getLocForStartOfFile(DefaultFileID)
+   .getLocWithOffset(20);
+  assert(DefaultLoc.isValid() && "Default location must be valid.");
+}
+
+RewriterTestContext Context;
+std::string DefaultCode = std::string(100, 'a');
+unsigned DefaultOffset = 20;
+SourceLocation DefaultLoc;
+FileID DefaultFileID;
+};
+
+TEST_F(EditListTest, EditListToYAML) {
+  setUp();
+  EditList Edit(Context.Sources, DefaultLoc);
+  llvm::Error Err = Edit.insertBefore(Context.Sources, DefaultLoc, "aa");
+  ASSERT_TRUE(!Err);
+  Err =
+  Edit.insertBefore(Context.Sources, DefaultLoc.getLocWithOffset(10), "bb");
+  ASSERT_TRUE(!Err);
+  Edit.addHeader("a.h");
+  Edit.removeHeader("b.h");
+  std::string YAMLString = Edit.toYAMLString();
+
+  // NOTE: If this test starts to fail for no obvious reason, check whitespace.
+  ASSERT_STREQ("---\n"
+   "Key: 'input.cpp:20'\n"
+   "FilePath:input.cpp\n"
+   "Error:   ''\n"
+   "InsertedHeaders: [ a.h ]\n"
+   "RemovedHeaders:  [ b.h ]\n"
+   "Replacements:\n" // Extra whitespace here!
+   "  - FilePath:input.cpp\n"
+   "Offset:  20\n"
+   "Length:  0\n"
+   "ReplacementText: aa\n"
+   "  - FilePath:input.cpp\n"
+   "Offset:  30\n"
+   "Length:  0\n"
+   "ReplacementText: bb\n"
+   "...\n",
+   YAMLString.c_str());
+}
+
+TEST_F(EditListTest, YAMLToEditList) {
+  setUp();
+  std::string YamlContent = "---\n"
+"Key: 'input.cpp:20'\n"
+"FilePath:input.cpp\n"
+"Error:   'ok'\n"
+"InsertedHeaders: [ a.h ]\n"
+"RemovedHeaders:  [ b.h ]\n"
+"Replacements:\n" // Extra whitespace here!
+"  - FilePath:input.cpp\n"
+"Offset:  20\n"
+"Length:  0\n"
+"ReplacementText: aa\n"
+"  - FilePath:input.cpp\n"
+"Offset:  30\n"
+"Length:  0\n"
+"ReplacementText: bb\n"
+"...\n";
+  EditList ExpectedEdit(Context.Sources, DefaultLoc);
+  llvm::Error Err =
+  ExpectedEdit.insertBefore(Context.Sources, DefaultLoc, "aa");
+  ASSERT_TRUE(!Err);
+  Err = ExpectedEdit.insertBefore(Context.Sources,
+  DefaultLoc.getLocWithOffset(10), "bb");
+  ASSERT_TRUE(!Err);
+
+  ExpectedEdit.addHeader("a.h");
+  ExpectedEdit.removeHeader("b.h");
+  ExpectedEdit.setError("ok");
+
+  EditList ActualEdit = EditList::convertFromYAML(YamlContent);
+  EXPECT_EQ(ExpectedEdit.getKey(), ActualEdit.getKey());
+  

[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Daniel Jasper via Phabricator via cfe-commits
djasper added inline comments.



Comment at: include/clang/Tooling/Refactoring/EditList.h:41
+  /// \brief Creates an edit list for a key position.
+  EditList(const SourceManager , SourceLocation KeyPosition);
+

I wonder whether we should always use a SourceLocation as key or whether we 
want to leave this up to the users. E.g. we could make this take a string 
parameter and provide a

  string getKeyForLocation(const SourceManager , SourceLocation KeyPosition);



Comment at: include/clang/Tooling/Refactoring/EditList.h:43
+
+  EditList(std::string Key, std::string FilePath, std::string Error,
+   std::vector InsertedHeaders,

Does this need to be public? Specifically, couldn't the YamlTraits create a 
NormalizedEditList and then convertFromYAML can create the EditList object, 
which should have access to a private constructor?



Comment at: include/clang/Tooling/Refactoring/EditList.h:82-94
+  /// \brief Adds a replacement that inserts \p Text at \p Loc. If this
+  /// insertion conflicts with an existing insertion (at the same position),
+  /// this will be inserted before the existing insertion. If the conflicting
+  /// replacement is not an insertion, an error is returned.
+  ///
+  /// \returns An llvm::Error carrying ReplacementError on error.
+  llvm::Error insertBefore(const SourceManager , SourceLocation Loc,

Do we currently actually need these functions? They seem a bit dangerous.



Comment at: include/clang/Tooling/Refactoring/EditList.h:117
+
+  std::string Key;
+  std::string FilePath;

Maybe add a short description of what "Key" contains. The others are pretty 
intuitive.



Comment at: lib/Tooling/Refactoring/EditList.cpp:25
+  /// access to its data members.
+  struct NormalizedEditList {
+NormalizedEditList(const IO &) {}

I don't understand (without more comments) why this is normalized/denormalized.



Comment at: unittests/Tooling/RefactoringTest.cpp:1113
+  llvm::Error Err = Edit.insertBefore(Context.Sources, DefaultLoc, "aa");
+  EXPECT_TRUE(!Err);
+  Err =

I think these need to be ASSERT_TRUE.


https://reviews.llvm.org/D27054



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


[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-30 Thread Eric Liu via Phabricator via cfe-commits
ioeric added a comment.

Ping.

Daniel, could you take a look? Manuel and I would like your opinion on this.


https://reviews.llvm.org/D27054



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


[PATCH] D27054: Introducing clang::tooling::EditList for refactoring tools.

2016-11-23 Thread Eric Liu via cfe-commits
ioeric created this revision.
ioeric added a reviewer: klimek.
ioeric added subscribers: djasper, cfe-commits.
Herald added a subscriber: mgorny.

An edit list is used to create and group a set of source edits, e.g.
replacements or header insertions. Edits in an EditList should be related,
e.g. replacements for the same type reference and the corresponding header
insertion/deletion.

An EditList is uniquely identified by a key position and will either be
fully applied or not applied at all. The key position should be the location
of the key syntactical element that is being changed, e.g. the call to a
refactored method.


https://reviews.llvm.org/D27054

Files:
  include/clang/Tooling/Refactoring/EditList.h
  lib/Tooling/CMakeLists.txt
  lib/Tooling/Refactoring/CMakeLists.txt
  lib/Tooling/Refactoring/EditList.cpp
  unittests/Tooling/CMakeLists.txt
  unittests/Tooling/RefactoringTest.cpp

Index: unittests/Tooling/RefactoringTest.cpp
===
--- unittests/Tooling/RefactoringTest.cpp
+++ unittests/Tooling/RefactoringTest.cpp
@@ -26,6 +26,7 @@
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/EditList.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/SmallString.h"
 #include "gtest/gtest.h"
@@ -102,10 +103,10 @@
 
 // Checks that an llvm::Error instance contains a ReplacementError with expected
 // error code, expected new replacement, and expected existing replacement.
-static bool checkReplacementError(
-llvm::Error&& Error, replacement_error ExpectedErr,
-llvm::Optional ExpectedExisting,
-llvm::Optional ExpectedNew) {
+static bool checkReplacementError(llvm::Error &,
+  replacement_error ExpectedErr,
+  llvm::Optional ExpectedExisting,
+  llvm::Optional ExpectedNew) {
   if (!Error) {
 llvm::errs() << "Error is a success.";
 return false;
@@ -1089,5 +1090,183 @@
   EXPECT_TRUE(FileToReplaces.empty());
 }
 
+class EditListTest : public ::testing::Test {
+  protected:
+void setUp() {
+  DefaultFileID = Context.createInMemoryFile("input.cpp", DefaultCode);
+  DefaultLoc = Context.Sources.getLocForStartOfFile(DefaultFileID)
+   .getLocWithOffset(20);
+  assert(DefaultLoc.isValid() && "Default location must be valid.");
+}
+
+RewriterTestContext Context;
+std::string DefaultCode = std::string(100, 'a');
+unsigned DefaultOffset = 20;
+SourceLocation DefaultLoc;
+FileID DefaultFileID;
+};
+
+TEST_F(EditListTest, EditListToYAML) {
+  setUp();
+  EditList Edit(Context.Sources, DefaultLoc);
+  llvm::Error Err = Edit.insertBefore(Context.Sources, DefaultLoc, "aa");
+  EXPECT_TRUE(!Err);
+  Err =
+  Edit.insertBefore(Context.Sources, DefaultLoc.getLocWithOffset(10), "bb");
+  EXPECT_TRUE(!Err);
+  Edit.addHeader("a.h");
+  Edit.removeHeader("b.h");
+  std::string YAMLString = Edit.toYAMLString();
+
+  // NOTE: If this test starts to fail for no obvious reason, check whitespace.
+  ASSERT_STREQ("---\n"
+   "Key: 'input.cpp:20'\n"
+   "FilePath:input.cpp\n"
+   "Error:   ''\n"
+   "InsertedHeaders: [ a.h ]\n"
+   "RemovedHeaders:  [ b.h ]\n"
+   "Replacements:\n" // Extra whitespace here!
+   "  - FilePath:input.cpp\n"
+   "Offset:  20\n"
+   "Length:  0\n"
+   "ReplacementText: aa\n"
+   "  - FilePath:input.cpp\n"
+   "Offset:  30\n"
+   "Length:  0\n"
+   "ReplacementText: bb\n"
+   "...\n",
+   YAMLString.c_str());
+}
+
+TEST_F(EditListTest, YAMLToEditList) {
+  setUp();
+  std::string YamlContent = "---\n"
+"Key: 'input.cpp:20'\n"
+"FilePath:input.cpp\n"
+"Error:   'ok'\n"
+"InsertedHeaders: [ a.h ]\n"
+"RemovedHeaders:  [ b.h ]\n"
+"Replacements:\n" // Extra whitespace here!
+"  - FilePath:input.cpp\n"
+"Offset:  20\n"
+"Length:  0\n"
+"ReplacementText: aa\n"
+"  - FilePath:input.cpp\n"
+"Offset:  30\n"
+"Length:  0\n"
+"ReplacementText: bb\n"
+"...\n";
+  EditList ExpectedEdit(Context.Sources, DefaultLoc);
+  llvm::Error Err =
+