[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-11-05 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG23657d9cc332: [SyntaxTree] Add reverse links to syntax 
Nodes. (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -57,8 +57,9 @@
 }
 
 syntax::Node::Node(NodeKind Kind)
-: Parent(nullptr), NextSibling(nullptr), Kind(static_cast(Kind)),
-  Role(0), Original(false), CanModify(false) {
+: Parent(nullptr), NextSibling(nullptr), PreviousSibling(nullptr),
+  Kind(static_cast(Kind)), Role(0), Original(false),
+  CanModify(false) {
   this->setRole(NodeRole::Detached);
 }
 
@@ -74,6 +75,30 @@
   return N->getKind() > NodeKind::Leaf;
 }
 
+void syntax::Tree::appendChildLowLevel(Node *Child, NodeRole Role) {
+  assert(Child->getRole() == NodeRole::Detached);
+  assert(Role != NodeRole::Detached);
+
+  Child->setRole(Role);
+  appendChildLowLevel(Child);
+}
+
+void syntax::Tree::appendChildLowLevel(Node *Child) {
+  assert(Child->Parent == nullptr);
+  assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
+  assert(Child->getRole() != NodeRole::Detached);
+
+  Child->Parent = this;
+  if (this->LastChild) {
+Child->PreviousSibling = this->LastChild;
+this->LastChild->NextSibling = Child;
+  } else
+this->FirstChild = Child;
+
+  this->LastChild = Child;
+}
+
 void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) {
   assert(Child->getRole() == NodeRole::Detached);
   assert(Role != NodeRole::Detached);
@@ -85,22 +110,26 @@
 void syntax::Tree::prependChildLowLevel(Node *Child) {
   assert(Child->Parent == nullptr);
   assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
   assert(Child->getRole() != NodeRole::Detached);
 
   Child->Parent = this;
-  Child->NextSibling = this->FirstChild;
+  if (this->FirstChild) {
+Child->NextSibling = this->FirstChild;
+this->FirstChild->PreviousSibling = Child;
+  } else
+this->LastChild = Child;
+
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
+void syntax::Tree::replaceChildRangeLowLevel(Node *Begin, Node *End,
  Node *New) {
-  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
- "`BeforeBegin` is not a child of `this`.");
+  assert((!Begin || Begin->Parent == this) &&
+ "`Begin` is not a child of `this`.");
   assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
   assert(canModify() && "Cannot modify `this`.");
 
-  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
-
 #ifndef NDEBUG
   for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
@@ -116,9 +145,8 @@
 return true;
 return false;
   };
-  assert(Reachable(FirstChild, BeforeBegin) &&
- "`BeforeBegin` is not reachable.");
-  assert(Reachable(Begin, End) && "`End` is not after `BeforeBegin`.");
+  assert(Reachable(FirstChild, Begin) && "`Begin` is not reachable.");
+  assert(Reachable(Begin, End) && "`End` is not after `Begin`.");
 #endif
 
   if (!New && Begin == End)
@@ -128,6 +156,10 @@
   for (auto *T = this; T && T->Original; T = T->Parent)
 T->Original = false;
 
+  // Save the node before the range to be removed. Later we insert the `New`
+  // range after this node.
+  auto *BeforeBegin = Begin ? Begin->PreviousSibling : LastChild;
+
   // Detach old nodes.
   for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
@@ -135,24 +167,33 @@
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+N->PreviousSibling = nullptr;
 if (N->Original)
   traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  // Attach new range.
+  auto * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  auto * = End ? End->PreviousSibling : LastChild;
+
   if (!New) {
-Begin = End;
+NewFirst = End;
+NewLast = BeforeBegin;
 return;
   }
-  // Attach new nodes.
-  Begin = New;
-  auto *Last = New;
+
+  New->PreviousSibling = BeforeBegin;
+  NewFirst = New;
+
+  Node *LastInNew;
   for (auto *N = New; N != nullptr; N = N->NextSibling) {
-Last = N;
+LastInNew = N;
 N->Parent = this;
   }
-  Last->NextSibling = End;
+  LastInNew->NextSibling = End;
+  NewLast = LastInNew;
 }
 
 namespace {
@@ -248,6 +289,11 @@
   assert(C.isOriginal());
 

[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-11-04 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 302879.
eduucaldas added a comment.

Rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -57,8 +57,9 @@
 }
 
 syntax::Node::Node(NodeKind Kind)
-: Parent(nullptr), NextSibling(nullptr), Kind(static_cast(Kind)),
-  Role(0), Original(false), CanModify(false) {
+: Parent(nullptr), NextSibling(nullptr), PreviousSibling(nullptr),
+  Kind(static_cast(Kind)), Role(0), Original(false),
+  CanModify(false) {
   this->setRole(NodeRole::Detached);
 }
 
@@ -74,6 +75,30 @@
   return N->getKind() > NodeKind::Leaf;
 }
 
+void syntax::Tree::appendChildLowLevel(Node *Child, NodeRole Role) {
+  assert(Child->getRole() == NodeRole::Detached);
+  assert(Role != NodeRole::Detached);
+
+  Child->setRole(Role);
+  appendChildLowLevel(Child);
+}
+
+void syntax::Tree::appendChildLowLevel(Node *Child) {
+  assert(Child->Parent == nullptr);
+  assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
+  assert(Child->getRole() != NodeRole::Detached);
+
+  Child->Parent = this;
+  if (this->LastChild) {
+Child->PreviousSibling = this->LastChild;
+this->LastChild->NextSibling = Child;
+  } else
+this->FirstChild = Child;
+
+  this->LastChild = Child;
+}
+
 void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) {
   assert(Child->getRole() == NodeRole::Detached);
   assert(Role != NodeRole::Detached);
@@ -85,22 +110,26 @@
 void syntax::Tree::prependChildLowLevel(Node *Child) {
   assert(Child->Parent == nullptr);
   assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
   assert(Child->getRole() != NodeRole::Detached);
 
   Child->Parent = this;
-  Child->NextSibling = this->FirstChild;
+  if (this->FirstChild) {
+Child->NextSibling = this->FirstChild;
+this->FirstChild->PreviousSibling = Child;
+  } else
+this->LastChild = Child;
+
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
+void syntax::Tree::replaceChildRangeLowLevel(Node *Begin, Node *End,
  Node *New) {
-  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
- "`BeforeBegin` is not a child of `this`.");
+  assert((!Begin || Begin->Parent == this) &&
+ "`Begin` is not a child of `this`.");
   assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
   assert(canModify() && "Cannot modify `this`.");
 
-  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
-
 #ifndef NDEBUG
   for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
@@ -116,9 +145,8 @@
 return true;
 return false;
   };
-  assert(Reachable(FirstChild, BeforeBegin) &&
- "`BeforeBegin` is not reachable.");
-  assert(Reachable(Begin, End) && "`End` is not after `BeforeBegin`.");
+  assert(Reachable(FirstChild, Begin) && "`Begin` is not reachable.");
+  assert(Reachable(Begin, End) && "`End` is not after `Begin`.");
 #endif
 
   if (!New && Begin == End)
@@ -128,6 +156,10 @@
   for (auto *T = this; T && T->Original; T = T->Parent)
 T->Original = false;
 
+  // Save the node before the range to be removed. Later we insert the `New`
+  // range after this node.
+  auto *BeforeBegin = Begin ? Begin->PreviousSibling : LastChild;
+
   // Detach old nodes.
   for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
@@ -135,24 +167,33 @@
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+N->PreviousSibling = nullptr;
 if (N->Original)
   traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  // Attach new range.
+  auto * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  auto * = End ? End->PreviousSibling : LastChild;
+
   if (!New) {
-Begin = End;
+NewFirst = End;
+NewLast = BeforeBegin;
 return;
   }
-  // Attach new nodes.
-  Begin = New;
-  auto *Last = New;
+
+  New->PreviousSibling = BeforeBegin;
+  NewFirst = New;
+
+  Node *LastInNew;
   for (auto *N = New; N != nullptr; N = N->NextSibling) {
-Last = N;
+LastInNew = N;
 N->Parent = this;
   }
-  Last->NextSibling = End;
+  LastInNew->NextSibling = End;
+  NewLast = LastInNew;
 }
 
 namespace {
@@ -248,6 +289,11 @@
   assert(C.isOriginal());
 assert(!C.isDetached());
 assert(C.getParent() == T);
+const auto *Next = C.getNextSibling();
+assert(!Next ||  == Next->getPreviousSibling());
+ 

[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-11-04 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a comment.

As we discussed offline we will probably not provide `replaceChildRange` in the 
public mutations API anymore.

As a result I don't think we should be chaining changes related to 
`replaceChildRange` in this patch, and thus it should be ready to go.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

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


[PATCH] D90543: [Syntax] Start to move trivial Node class definitions to TableGen. NFC

2020-11-04 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a comment.

> Compared to Dmitri's prototype, Nodes.td looks more like a class hierarchy and
> less like a grammar. (E.g. variants list the Alternatives parent rather than
> vice versa).



> e.g. we may introduce abstract bases like "loop" that the grammar doesn't 
> care about in order to model is-a concepts that might make refactorings more 
> expressive. This is less natural in a grammar-like idiom.

Do you have a concrete example of such abstract base -- loop is in the grammar 
 ? And in the case 
such an example appear, in my mind we would just change "our grammar" to have 
an alternative for this "loop" abstract base.

> e.g. we're likely to have to model some alternatives as variants and others 
> as class hierarchies, the choice will probably be based on natural is-a 
> relationships.

I agree, alternatives and the two ways to model them are a tricky subject...

> it reduces the cognitive load of switching from editing *.td to working with 
> code that uses the generated classes

I think we should consider reading prior to editing.
A grammar is how we represent syntax, and syntax trees model syntax, as such I 
think we should rather consider the cognitive load of switching between the 
grammar and the definition of syntax trees.

I have a lot to improve in my writing skills, so I don't know if I could 
express all my thoughts. We could have a call if you want ;)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90543/new/

https://reviews.llvm.org/D90543

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


[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-10-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 301315.
eduucaldas marked 9 inline comments as done.
eduucaldas added a comment.

Answered all comments but:

- Add tests for `replaceChildRangeLowLevel`
- Asymmetry of `replaceChildRangeLowLevel`, do we need to separate children of 
the replaced range?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -57,8 +57,9 @@
 }
 
 syntax::Node::Node(NodeKind Kind)
-: Parent(nullptr), NextSibling(nullptr), Kind(static_cast(Kind)),
-  Role(0), Original(false), CanModify(false) {
+: Parent(nullptr), NextSibling(nullptr), PreviousSibling(nullptr),
+  Kind(static_cast(Kind)), Role(0), Original(false),
+  CanModify(false) {
   this->setRole(NodeRole::Detached);
 }
 
@@ -74,6 +75,30 @@
   return N->getKind() > NodeKind::Leaf;
 }
 
+void syntax::Tree::appendChildLowLevel(Node *Child, NodeRole Role) {
+  assert(Child->getRole() == NodeRole::Detached);
+  assert(Role != NodeRole::Detached);
+
+  Child->setRole(Role);
+  appendChildLowLevel(Child);
+}
+
+void syntax::Tree::appendChildLowLevel(Node *Child) {
+  assert(Child->Parent == nullptr);
+  assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
+  assert(Child->getRole() != NodeRole::Detached);
+
+  Child->Parent = this;
+  if (this->LastChild) {
+Child->PreviousSibling = this->LastChild;
+this->LastChild->NextSibling = Child;
+  } else
+this->FirstChild = Child;
+
+  this->LastChild = Child;
+}
+
 void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) {
   assert(Child->getRole() == NodeRole::Detached);
   assert(Role != NodeRole::Detached);
@@ -85,22 +110,26 @@
 void syntax::Tree::prependChildLowLevel(Node *Child) {
   assert(Child->Parent == nullptr);
   assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
   assert(Child->getRole() != NodeRole::Detached);
 
   Child->Parent = this;
-  Child->NextSibling = this->FirstChild;
+  if (this->FirstChild) {
+Child->NextSibling = this->FirstChild;
+this->FirstChild->PreviousSibling = Child;
+  } else
+this->LastChild = Child;
+
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
+void syntax::Tree::replaceChildRangeLowLevel(Node *Begin, Node *End,
  Node *New) {
-  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
- "`BeforeBegin` is not a child of `this`.");
+  assert((!Begin || Begin->Parent == this) &&
+ "`Begin` is not a child of `this`.");
   assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
   assert(canModify() && "Cannot modify `this`.");
 
-  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
-
 #ifndef NDEBUG
   for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
@@ -116,9 +145,8 @@
 return true;
 return false;
   };
-  assert(Reachable(FirstChild, BeforeBegin) &&
- "`BeforeBegin` is not reachable.");
-  assert(Reachable(Begin, End) && "`End` is not after `BeforeBegin`.");
+  assert(Reachable(FirstChild, Begin) && "`Begin` is not reachable.");
+  assert(Reachable(Begin, End) && "`End` is not after `Begin`.");
 #endif
 
   if (!New && Begin == End)
@@ -128,6 +156,10 @@
   for (auto *T = this; T && T->Original; T = T->Parent)
 T->Original = false;
 
+  // Save the node before the range to be removed. Later we insert the `New`
+  // range after this node.
+  auto *BeforeBegin = Begin ? Begin->PreviousSibling : LastChild;
+
   // Detach old nodes.
   for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
@@ -135,24 +167,33 @@
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+N->PreviousSibling = nullptr;
 if (N->Original)
   traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  // Attach new range.
+  auto * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  auto * = End ? End->PreviousSibling : LastChild;
+
   if (!New) {
-Begin = End;
+NewFirst = End;
+NewLast = BeforeBegin;
 return;
   }
-  // Attach new nodes.
-  Begin = New;
-  auto *Last = New;
+
+  New->PreviousSibling = BeforeBegin;
+  NewFirst = New;
+
+  Node *LastInNew;
   for (auto *N = New; N != nullptr; N = N->NextSibling) {
-Last = N;
+LastInNew = N;
 N->Parent = this;
   }
-  Last->NextSibling = End;
+  LastInNew->NextSibling = End;
+  NewLast = LastInNew;
 }
 
 namespace {
@@ 

[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-10-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 301256.
eduucaldas added a comment.

I was silly on the last rebase. Ignore this.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -57,8 +57,9 @@
 }
 
 syntax::Node::Node(NodeKind Kind)
-: Parent(nullptr), NextSibling(nullptr), Kind(static_cast(Kind)),
-  Role(0), Original(false), CanModify(false) {
+: Parent(nullptr), NextSibling(nullptr), PreviousSibling(nullptr),
+  Kind(static_cast(Kind)), Role(0), Original(false),
+  CanModify(false) {
   this->setRole(NodeRole::Detached);
 }
 
@@ -74,6 +75,30 @@
   return N->getKind() > NodeKind::Leaf;
 }
 
+void syntax::Tree::appendChildLowLevel(Node *Child, NodeRole Role) {
+  assert(Child->getRole() == NodeRole::Detached);
+  assert(Role != NodeRole::Detached);
+
+  Child->setRole(Role);
+  appendChildLowLevel(Child);
+}
+
+void syntax::Tree::appendChildLowLevel(Node *Child) {
+  assert(Child->Parent == nullptr);
+  assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
+  assert(Child->getRole() != NodeRole::Detached);
+
+  Child->Parent = this;
+  if (this->LastChild) {
+Child->PreviousSibling = this->LastChild;
+this->LastChild->NextSibling = Child;
+  } else
+this->FirstChild = Child;
+
+  this->LastChild = Child;
+}
+
 void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) {
   assert(Child->getRole() == NodeRole::Detached);
   assert(Role != NodeRole::Detached);
@@ -85,22 +110,26 @@
 void syntax::Tree::prependChildLowLevel(Node *Child) {
   assert(Child->Parent == nullptr);
   assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
   assert(Child->getRole() != NodeRole::Detached);
 
   Child->Parent = this;
-  Child->NextSibling = this->FirstChild;
+  if (this->FirstChild) {
+Child->NextSibling = this->FirstChild;
+this->FirstChild->PreviousSibling = Child;
+  } else
+this->LastChild = Child;
+
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
+void syntax::Tree::replaceChildRangeLowLevel(Node *Begin, Node *End,
  Node *New) {
-  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
- "`BeforeBegin` is not a child of `this`.");
+  assert(((!Begin && !FirstChild) || Begin->Parent == this) &&
+ "`Begin` is not a child of `this`.");
   assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
   assert(canModify() && "Cannot modify `this`.");
 
-  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
-
 #ifndef NDEBUG
   for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
@@ -116,18 +145,21 @@
 return true;
 return false;
   };
-  assert(Reachable(FirstChild, BeforeBegin) &&
- "`BeforeBegin` is not reachable.");
-  assert(Reachable(Begin, End) && "`End` is not after `BeforeBegin`.");
+  assert(Reachable(FirstChild, Begin) && "`Begin` is not reachable.");
+  assert(Reachable(Begin, End) && "`End` is not after `Begin`.");
 #endif
 
-  if (!New && Begin == End)
+  if (!New && (!Begin || Begin == End))
 return;
 
   // Mark modification.
   for (auto *T = this; T && T->Original; T = T->Parent)
 T->Original = false;
 
+  // Point to node before the range to be removed, to later insert the `New`
+  // range after it.
+  auto *BeforeBegin = Begin ? Begin->PreviousSibling : nullptr;
+
   // Detach old nodes.
   for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
@@ -135,24 +167,33 @@
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+N->PreviousSibling = nullptr;
 if (N->Original)
   traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  // Attach new range.
+  auto * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  auto * = End ? End->PreviousSibling : LastChild;
+
   if (!New) {
-Begin = End;
+NewBegin = End;
+NewLast = BeforeBegin;
 return;
   }
-  // Attach new nodes.
-  Begin = New;
-  auto *Last = New;
+
+  New->PreviousSibling = BeforeBegin;
+  NewBegin = New;
+
+  Node *LastInNew;
   for (auto *N = New; N != nullptr; N = N->NextSibling) {
-Last = N;
+LastInNew = N;
 N->Parent = this;
   }
-  Last->NextSibling = End;
+  LastInNew->NextSibling = End;
+  NewLast = LastInNew;
 }
 
 namespace {
@@ -248,6 +289,11 @@
   assert(C.isOriginal());
 assert(!C.isDetached());
 

[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-10-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 301255.
eduucaldas added a comment.

Rebase to include ChildIterator patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -57,8 +57,9 @@
 }
 
 syntax::Node::Node(NodeKind Kind)
-: Parent(nullptr), NextSibling(nullptr), Kind(static_cast(Kind)),
-  Role(0), Original(false), CanModify(false) {
+: Parent(nullptr), NextSibling(nullptr), PreviousSibling(nullptr),
+  Kind(static_cast(Kind)), Role(0), Original(false),
+  CanModify(false) {
   this->setRole(NodeRole::Detached);
 }
 
@@ -74,6 +75,30 @@
   return N->getKind() > NodeKind::Leaf;
 }
 
+void syntax::Tree::appendChildLowLevel(Node *Child, NodeRole Role) {
+  assert(Child->getRole() == NodeRole::Detached);
+  assert(Role != NodeRole::Detached);
+
+  Child->setRole(Role);
+  appendChildLowLevel(Child);
+}
+
+void syntax::Tree::appendChildLowLevel(Node *Child) {
+  assert(Child->Parent == nullptr);
+  assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
+  assert(Child->getRole() != NodeRole::Detached);
+
+  Child->Parent = this;
+  if (this->LastChild) {
+Child->PreviousSibling = this->LastChild;
+this->LastChild->NextSibling = Child;
+  } else
+this->FirstChild = Child;
+
+  this->LastChild = Child;
+}
+
 void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) {
   assert(Child->getRole() == NodeRole::Detached);
   assert(Role != NodeRole::Detached);
@@ -85,22 +110,26 @@
 void syntax::Tree::prependChildLowLevel(Node *Child) {
   assert(Child->Parent == nullptr);
   assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
   assert(Child->getRole() != NodeRole::Detached);
 
   Child->Parent = this;
-  Child->NextSibling = this->FirstChild;
+  if (this->FirstChild) {
+Child->NextSibling = this->FirstChild;
+this->FirstChild->PreviousSibling = Child;
+  } else
+this->LastChild = Child;
+
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
+void syntax::Tree::replaceChildRangeLowLevel(Node *Begin, Node *End,
  Node *New) {
-  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
- "`BeforeBegin` is not a child of `this`.");
+  assert(((!Begin && !FirstChild) || Begin->Parent == this) &&
+ "`Begin` is not a child of `this`.");
   assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
   assert(canModify() && "Cannot modify `this`.");
 
-  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
-
 #ifndef NDEBUG
   for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
@@ -116,18 +145,21 @@
 return true;
 return false;
   };
-  assert(Reachable(FirstChild, BeforeBegin) &&
- "`BeforeBegin` is not reachable.");
-  assert(Reachable(Begin, End) && "`End` is not after `BeforeBegin`.");
+  assert(Reachable(FirstChild, Begin) && "`Begin` is not reachable.");
+  assert(Reachable(Begin, End) && "`End` is not after `Begin`.");
 #endif
 
-  if (!New && Begin == End)
+  if (!New && (!Begin || Begin == End))
 return;
 
   // Mark modification.
   for (auto *T = this; T && T->Original; T = T->Parent)
 T->Original = false;
 
+  // Point to node before the range to be removed, to later insert the `New`
+  // range after it.
+  auto *BeforeBegin = Begin ? Begin->PreviousSibling : nullptr;
+
   // Detach old nodes.
   for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
@@ -135,24 +167,33 @@
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+N->PreviousSibling = nullptr;
 if (N->Original)
   traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  // Attach new range.
+  auto * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  auto * = End ? End->PreviousSibling : LastChild;
+
   if (!New) {
-Begin = End;
+NewBegin = End;
+NewLast = BeforeBegin;
 return;
   }
-  // Attach new nodes.
-  Begin = New;
-  auto *Last = New;
+
+  New->PreviousSibling = BeforeBegin;
+  NewBegin = New;
+
+  Node *LastInNew;
   for (auto *N = New; N != nullptr; N = N->NextSibling) {
-Last = N;
+LastInNew = N;
 N->Parent = this;
   }
-  Last->NextSibling = End;
+  LastInNew->NextSibling = End;
+  NewLast = LastInNew;
 }
 
 namespace {
@@ -245,9 +286,14 @@
 return;
   for (const Node  : T->getChildren()) {
 if (T->isOriginal())
-  

[PATCH] D89794: [SyntaxTree] Implement "by-pointer output parameter to return value" refactoring.

2020-10-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a subscriber: sammccall.
eduucaldas added a comment.

Sam, this patch is outdated, as we're still making decisions on 
https://reviews.llvm.org/D90161 and we haven't yet landed 
https://reviews.llvm.org/D90240, please don't bother reviewing it.

This is where we started thinking about iterators on lists, and about the 
usefulness of having reverse links for syntax nodes.

I have added you to the subscriber list so you can follow the discussion once 
this is unblocked, and to show you the Mutations API in its early steps.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89794/new/

https://reviews.llvm.org/D89794

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


[PATCH] D89794: [SyntaxTree] Implement "by-pointer output parameter to return value" refactoring.

2020-10-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a subscriber: gribozavr2.
eduucaldas added a comment.

This patch will build upon https://reviews.llvm.org/D90240 and 
https://reviews.llvm.org/D90161
When those patches land, work on this patch will resume. 
It is here to illustrate the relevance of the previous patches and the general 
direction we're going.

Glad to see that you're interested David, we'll keep you updated.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89794/new/

https://reviews.llvm.org/D89794

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


[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-10-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas marked 2 inline comments as done.
eduucaldas added inline comments.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:109
   Node *getNextSibling() { return NextSibling; }
+  const Node *getPreviousSibling() const { return PreviousSibling; }
+  Node *getPreviousSibling() { return PreviousSibling; }

sammccall wrote:
> (Not something to change in this patch...)
> Since adding the "get" prefixes, getNextSibling() and now 
> getPreviousSibling() are pretty verbose, we might consider 
> getNext()/getPrevious()
I agree





Comment at: clang/include/clang/Tooling/Syntax/Tree.h:189
   /// EXPECTS: Child->Role != Detached
   void prependChildLowLevel(Node *Child);
   friend class TreeBuilder;

sammccall wrote:
> gribozavr2 wrote:
> > eduucaldas wrote:
> > > Should we provide an `appendChildLowLevel` as well?
> > > 
> > > That has one use inside `foldChildren` in `BuildTree.cpp`. 
> > > Currently this function does a reverse iteration prepending children. We 
> > > could change that into a forward iteration appending. There is no impact 
> > > in time-complexity. This change would just improve readability inside 
> > > this function.
> > There is some awkwardness in foldChildren because we can only go in reverse 
> > -- maybe append is indeed more natural.
> Consider `insert(Node *Child, const Node *Before)` where Before=Null means 
> append.
> 
> This is fairly ergonomic for common cases: 
>  - append: `insert(N, null)`
>  - prepend: `insert(N, N->firstChild())`
>  - insert-before: `insert(N, M)`
>  - insert-after: `insert(N, M->nextSibling())`
> 
> (Either before or after works fine, before matches STL insert better)
That is great, but we have even a superset of this:
`replaceChildRangeLowLevel(Node* BeforeBegin, Node* End, Node* New)`
where:
`insert(Child, Before) = replaceChildRangeLowLevel(Before, 
Before->getNextSibling(), Child)`

I think the point of having append and prepend is that until now that's what 
builders need, and such a specialization carries more semantics.

For the mutations API, where we need this kind of capability we provide 
`replaceChildRangeLowLevel`.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:189
   /// EXPECTS: Child->Role != Detached
   void prependChildLowLevel(Node *Child);
   friend class TreeBuilder;

eduucaldas wrote:
> sammccall wrote:
> > gribozavr2 wrote:
> > > eduucaldas wrote:
> > > > Should we provide an `appendChildLowLevel` as well?
> > > > 
> > > > That has one use inside `foldChildren` in `BuildTree.cpp`. 
> > > > Currently this function does a reverse iteration prepending children. 
> > > > We could change that into a forward iteration appending. There is no 
> > > > impact in time-complexity. This change would just improve readability 
> > > > inside this function.
> > > There is some awkwardness in foldChildren because we can only go in 
> > > reverse -- maybe append is indeed more natural.
> > Consider `insert(Node *Child, const Node *Before)` where Before=Null means 
> > append.
> > 
> > This is fairly ergonomic for common cases: 
> >  - append: `insert(N, null)`
> >  - prepend: `insert(N, N->firstChild())`
> >  - insert-before: `insert(N, M)`
> >  - insert-after: `insert(N, M->nextSibling())`
> > 
> > (Either before or after works fine, before matches STL insert better)
> That is great, but we have even a superset of this:
> `replaceChildRangeLowLevel(Node* BeforeBegin, Node* End, Node* New)`
> where:
> `insert(Child, Before) = replaceChildRangeLowLevel(Before, 
> Before->getNextSibling(), Child)`
> 
> I think the point of having append and prepend is that until now that's what 
> builders need, and such a specialization carries more semantics.
> 
> For the mutations API, where we need this kind of capability we provide 
> `replaceChildRangeLowLevel`.
I replace every place where we did a reverse iteration prepending for a normal 
iteration appending, and now there are no more users of prepend ^^. 

I propose we keep it anyways, we have bidirection list, makes sense to have 
both.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

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


[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-10-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 301198.
eduucaldas marked 3 inline comments as done.
eduucaldas added a comment.

- `replaceChildRangeLowLevel` now takes Begin instead of BeforeBegin
- `appendChildLowLevel`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -57,8 +57,9 @@
 }
 
 syntax::Node::Node(NodeKind Kind)
-: Parent(nullptr), NextSibling(nullptr), Kind(static_cast(Kind)),
-  Role(0), Original(false), CanModify(false) {
+: Parent(nullptr), NextSibling(nullptr), PreviousSibling(nullptr),
+  Kind(static_cast(Kind)), Role(0), Original(false),
+  CanModify(false) {
   this->setRole(NodeRole::Detached);
 }
 
@@ -74,6 +75,30 @@
   return N->getKind() > NodeKind::Leaf;
 }
 
+void syntax::Tree::appendChildLowLevel(Node *Child, NodeRole Role) {
+  assert(Child->getRole() == NodeRole::Detached);
+  assert(Role != NodeRole::Detached);
+
+  Child->setRole(Role);
+  appendChildLowLevel(Child);
+}
+
+void syntax::Tree::appendChildLowLevel(Node *Child) {
+  assert(Child->Parent == nullptr);
+  assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
+  assert(Child->getRole() != NodeRole::Detached);
+
+  Child->Parent = this;
+  if (this->LastChild) {
+Child->PreviousSibling = this->LastChild;
+this->LastChild->NextSibling = Child;
+  } else
+this->FirstChild = Child;
+
+  this->LastChild = Child;
+}
+
 void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) {
   assert(Child->getRole() == NodeRole::Detached);
   assert(Role != NodeRole::Detached);
@@ -85,22 +110,26 @@
 void syntax::Tree::prependChildLowLevel(Node *Child) {
   assert(Child->Parent == nullptr);
   assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
   assert(Child->getRole() != NodeRole::Detached);
 
   Child->Parent = this;
-  Child->NextSibling = this->FirstChild;
+  if (this->FirstChild) {
+Child->NextSibling = this->FirstChild;
+this->FirstChild->PreviousSibling = Child;
+  } else
+this->LastChild = Child;
+
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
+void syntax::Tree::replaceChildRangeLowLevel(Node *Begin, Node *End,
  Node *New) {
-  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
- "`BeforeBegin` is not a child of `this`.");
+  assert(((!Begin && !FirstChild) || Begin->Parent == this) &&
+ "`Begin` is not a child of `this`.");
   assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
   assert(canModify() && "Cannot modify `this`.");
 
-  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
-
 #ifndef NDEBUG
   for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
@@ -116,18 +145,21 @@
 return true;
 return false;
   };
-  assert(Reachable(FirstChild, BeforeBegin) &&
- "`BeforeBegin` is not reachable.");
-  assert(Reachable(Begin, End) && "`End` is not after `BeforeBegin`.");
+  assert(Reachable(FirstChild, Begin) && "`Begin` is not reachable.");
+  assert(Reachable(Begin, End) && "`End` is not after `Begin`.");
 #endif
 
-  if (!New && Begin == End)
+  if (!New && (!Begin || Begin == End))
 return;
 
   // Mark modification.
   for (auto *T = this; T && T->Original; T = T->Parent)
 T->Original = false;
 
+  // Point to node before the range to be removed, to later insert the `New`
+  // range after it.
+  auto *BeforeBegin = Begin ? Begin->PreviousSibling : nullptr;
+
   // Detach old nodes.
   for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
@@ -135,24 +167,33 @@
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+N->PreviousSibling = nullptr;
 if (N->Original)
   traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  // Attach new range.
+  auto * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  auto * = End ? End->PreviousSibling : LastChild;
+
   if (!New) {
-Begin = End;
+NewBegin = End;
+NewLast = BeforeBegin;
 return;
   }
-  // Attach new nodes.
-  Begin = New;
-  auto *Last = New;
+
+  New->PreviousSibling = BeforeBegin;
+  NewBegin = New;
+
+  Node *LastInNew;
   for (auto *N = New; N != nullptr; N = N->NextSibling) {
-Last = N;
+LastInNew = N;
 N->Parent = this;
   }
-  Last->NextSibling = End;
+  LastInNew->NextSibling = End;
+  NewLast = LastInNew;
 }
 
 namespace {
@@ -248,6 

[PATCH] D90161: [SyntaxTree] Provide iterators for Lists

2020-10-27 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 301024.
eduucaldas marked 6 inline comments as done.
eduucaldas added a comment.

- `const` on `getElement` and similar.
- `NotSentinel` -> `Element`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90161/new/

https://reviews.llvm.org/D90161

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -11,6 +11,7 @@
 #include "clang/Tooling/Syntax/BuildTree.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -125,7 +126,7 @@
 }
 
 class ListTest : public SyntaxTreeTest {
-private:
+protected:
   std::string dumpQuotedTokensOrNull(const Node *N) {
 return N ? "'" +
StringRef(N->dumpTokens(Arena->getSourceManager()))
@@ -135,7 +136,15 @@
  : "null";
   }
 
-protected:
+  std::string
+  dumpElementAndDelimiter(const List::ElementAndDelimiter ED) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+   << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+return OS.str();
+  }
+
   std::string
   dumpElementsAndDelimiters(ArrayRef> EDs) {
 std::string Storage;
@@ -145,8 +154,7 @@
 
 llvm::interleaveComma(
 EDs, OS, [, this](const List::ElementAndDelimiter ) {
-  OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
- << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+  OS << dumpElementAndDelimiter(ED);
 });
 
 OS << "]";
@@ -351,4 +359,143 @@
   EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
 }
 
+TEST_P(ListTest, List_Terminated_Iterator_Parent) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a   b::  :: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersBeforeBegin().getParent(),
+List);
+
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End; ++It)
+EXPECT_EQ(It.getParent(), List);
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersEnd().getParent(), List);
+}
+
+TEST_P(ListTest, List_Terminated_Iterator_Equality) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a   b::  :: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  // different iterators are different.
+  for (auto BeforeIt = List->getElementsAsNodesAndDelimitersBeforeBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   BeforeIt != End; ++BeforeIt) {
+auto It = BeforeIt;
+++It;
+for (; It != End; ++It) {
+  EXPECT_NE(BeforeIt, It);
+}
+  }
+
+  // Equal iterators are equal.
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+It2 = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End && It2 != End;) {
+EXPECT_EQ(++It, ++It2);
+  }
+
+  // Different sentinel iterators are different.
+  EXPECT_NE(List->getElementsAsNodesAndDelimitersBeforeBegin(),
+List->getElementsAsNodesAndDelimitersEnd());
+}
+
+TEST_P(ListTest, List_Separated_Iterator_Parent) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a  b,  ,"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  

[PATCH] D90161: [SyntaxTree] Provide iterators for Lists

2020-10-27 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas marked 8 inline comments as done.
eduucaldas added a comment.

I left some points unanswered, I'll answer them tomorrow :)




Comment at: clang/include/clang/Tooling/Syntax/Tree.h:231
+  /// `ElementAndDelimiter` acts as one.
+  template  class ElementAndDelimiterIterator {
+  public:

sammccall wrote:
> sammccall wrote:
> > sammccall wrote:
> > > this name is bulky and hard to read, leads to more unwieldy names later 
> > > (e.g. `getElementAndDelimiterBeforeBegin` which despite its length is 
> > > unclear that it's even an iterator).
> > > 
> > > Would there be actual confusion in calling this `ElementIterator`, and 
> > > providing the associated delimiters in addition to the elements?
> > overall, this design seems to be focused on the size being a single 
> > pointer, at the cost of complexity of most of the operations.
> > 
> > Why is this important to optimize for, compared to doing something like:
> > ```
> > private:
> >   Node *Elt, *Delim, *Next;
> >   void advance() {
> > // read one element
> > // set Elt and/or Delim
> > // always advances Next by at least one
> >   }
> > public:
> >   Iterator(Node *Start) : Elt(nullptr), Delim(nullptr), Next(Start) {}
> >   operator++() { advance(); }
> >   Leaf *getDelimiter() { return Delim; }
> >   Node *getElement() { return Elt; }
> > ```
> > 
> > (Performance isn't the biggest concern here, but I'd usually expect two 
> > extra pointers on the stack to be preferable to all the branches in the 
> > current impl)
> how sure are we that the template and strong typing is worthwhile on the 
> iterators? Can we try without it for a while and see how painful it is?
> how sure are we that the template and strong typing is worthwhile on the 
> iterators? Can we try without it for a while and see how painful it is?
It avoids duplication of code on derived classes - `CallArguments`, 
`ParameterDeclarationList` I suggest that we try to simplify other things 
and then rediscuss this point.  

1. BeforeBegin.
2. Trying to fit everything into a pointer. 
3. Templates and strong typing.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:231
+  /// `ElementAndDelimiter` acts as one.
+  template  class ElementAndDelimiterIterator {
+  public:

eduucaldas wrote:
> sammccall wrote:
> > sammccall wrote:
> > > sammccall wrote:
> > > > this name is bulky and hard to read, leads to more unwieldy names later 
> > > > (e.g. `getElementAndDelimiterBeforeBegin` which despite its length is 
> > > > unclear that it's even an iterator).
> > > > 
> > > > Would there be actual confusion in calling this `ElementIterator`, and 
> > > > providing the associated delimiters in addition to the elements?
> > > overall, this design seems to be focused on the size being a single 
> > > pointer, at the cost of complexity of most of the operations.
> > > 
> > > Why is this important to optimize for, compared to doing something like:
> > > ```
> > > private:
> > >   Node *Elt, *Delim, *Next;
> > >   void advance() {
> > > // read one element
> > > // set Elt and/or Delim
> > > // always advances Next by at least one
> > >   }
> > > public:
> > >   Iterator(Node *Start) : Elt(nullptr), Delim(nullptr), Next(Start) {}
> > >   operator++() { advance(); }
> > >   Leaf *getDelimiter() { return Delim; }
> > >   Node *getElement() { return Elt; }
> > > ```
> > > 
> > > (Performance isn't the biggest concern here, but I'd usually expect two 
> > > extra pointers on the stack to be preferable to all the branches in the 
> > > current impl)
> > how sure are we that the template and strong typing is worthwhile on the 
> > iterators? Can we try without it for a while and see how painful it is?
> > how sure are we that the template and strong typing is worthwhile on the 
> > iterators? Can we try without it for a while and see how painful it is?
> It avoids duplication of code on derived classes - `CallArguments`, 
> `ParameterDeclarationList` I suggest that we try to simplify other things 
> and then rediscuss this point.  
> 
> 1. BeforeBegin.
> 2. Trying to fit everything into a pointer. 
> 3. Templates and strong typing.
> Would there be actual confusion in calling this ElementIterator, and 
> providing the associated delimiters in addition to the elements?

This is a very interesting proposition! And it brought some interesting ideas. 
We'll further develop on them and answer to this inline tomorrow :) 



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:235
+  switch (getKind()) {
+  case IteratorKind::BeforeBegin: {
+if (auto *Begin = getParent()->getFirstChild())

sammccall wrote:
> Are the before-begin iterators needed immediately?
> 
> My understanding is that before-begin iterators are used with nodes due to 
> the single-linked-list implementation, but that's going away. Can we avoid 
> adding more of these by sequencing those 

[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-10-27 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a reviewer: gribozavr2.
eduucaldas added a subscriber: sammccall.
eduucaldas added inline comments.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:189
   /// EXPECTS: Child->Role != Detached
   void prependChildLowLevel(Node *Child);
   friend class TreeBuilder;

Should we provide an `appendChildLowLevel` as well?

That has one use inside `foldChildren` in `BuildTree.cpp`. 
Currently this function does a reverse iteration prepending children. We could 
change that into a forward iteration appending. There is no impact in 
time-complexity. This change would just improve readability inside this 
function.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90240/new/

https://reviews.llvm.org/D90240

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


[PATCH] D90240: [SyntaxTree] Add reverse links to syntax Nodes.

2020-10-27 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Rationale:
Children of a syntax tree had forward links only, because there was no
need for reverse links.

This need appeared when we started mutating the syntax tree.
On a forward list, to remove a target node in O(1) we need a pointer to the 
node before the target. If we don't have this "before" pointer, we have to find 
it, and that requires O(n).
So in order to remove a syntax node from a tree, we would similarly need to 
find the node before to then remove. This is both not ergonomic nor does it 
have a good complexity.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D90240

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -57,8 +57,9 @@
 }
 
 syntax::Node::Node(NodeKind Kind)
-: Parent(nullptr), NextSibling(nullptr), Kind(static_cast(Kind)),
-  Role(0), Original(false), CanModify(false) {
+: Parent(nullptr), NextSibling(nullptr), PreviousSibling(nullptr),
+  Kind(static_cast(Kind)), Role(0), Original(false),
+  CanModify(false) {
   this->setRole(NodeRole::Detached);
 }
 
@@ -85,10 +86,16 @@
 void syntax::Tree::prependChildLowLevel(Node *Child) {
   assert(Child->Parent == nullptr);
   assert(Child->NextSibling == nullptr);
+  assert(Child->PreviousSibling == nullptr);
   assert(Child->getRole() != NodeRole::Detached);
 
   Child->Parent = this;
-  Child->NextSibling = this->FirstChild;
+  if (this->FirstChild) {
+Child->NextSibling = this->FirstChild;
+this->FirstChild->PreviousSibling = Child;
+  } else
+this->LastChild = Child;
+
   this->FirstChild = Child;
 }
 
@@ -100,6 +107,7 @@
   assert(canModify() && "Cannot modify `this`.");
 
   Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  Node * = End ? End->PreviousSibling : LastChild;
 
 #ifndef NDEBUG
   for (auto *N = New; N; N = N->NextSibling) {
@@ -135,6 +143,7 @@
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+N->PreviousSibling = nullptr;
 if (N->Original)
   traverse(N, [](Node *C) { C->Original = false; });
 
@@ -143,16 +152,19 @@
 
   if (!New) {
 Begin = End;
+Last = BeforeBegin;
 return;
   }
   // Attach new nodes.
   Begin = New;
-  auto *Last = New;
+  New->PreviousSibling = BeforeBegin;
+  auto *LastInNew = New;
   for (auto *N = New; N != nullptr; N = N->NextSibling) {
-Last = N;
+LastInNew = N;
 N->Parent = this;
   }
-  Last->NextSibling = End;
+  LastInNew->NextSibling = End;
+  Last = LastInNew;
 }
 
 namespace {
@@ -243,11 +255,20 @@
   const auto *T = dyn_cast(this);
   if (!T)
 return;
+
+  const auto *Last = T->getFirstChild();
+  for (; Last && Last->getNextSibling(); Last = Last->getNextSibling())
+;
+  assert(Last == T->getLastChild() &&
+ "Last child is reachable by advancing from the first child.");
+
   for (const auto *C = T->getFirstChild(); C; C = C->getNextSibling()) {
 if (T->isOriginal())
   assert(C->isOriginal());
 assert(!C->isDetached());
 assert(C->getParent() == T);
+const auto *Next = C->getNextSibling();
+assert(!Next || C == Next->getPreviousSibling());
   }
 
   const auto *L = dyn_cast(T);
@@ -282,14 +303,13 @@
 }
 
 syntax::Leaf *syntax::Tree::findLastLeaf() {
-  syntax::Leaf *Last = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
+  for (auto *C = getLastChild(); C; C = C->getPreviousSibling()) {
 if (auto *L = dyn_cast(C))
-  Last = L;
-else if (auto *L = cast(C)->findLastLeaf())
-  Last = L;
+  return L;
+if (auto *L = cast(C)->findLastLeaf())
+  return L;
   }
-  return Last;
+  return nullptr;
 }
 
 syntax::Node *syntax::Tree::findChild(NodeRole R) {
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,19 +23,6 @@
 
 using namespace clang;
 
-static syntax::Node *findPrevious(syntax::Node *N) {
-  assert(N);
-  assert(N->getParent());
-  if (N->getParent()->getFirstChild() == N)
-return nullptr;
-  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
-   C = C->getNextSibling()) {
-if (C->getNextSibling() == N)
-  return C;
-  }
-  llvm_unreachable("could not find a child node");
-}
-
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -46,6 +33,7 @@
 assert(Anchor->Parent != nullptr);
 assert(New->Parent == 

[PATCH] D90161: [SyntaxTree] Provide iterators for Lists

2020-10-26 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 300702.
eduucaldas added a comment.

Diff against master, sorry about that


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90161/new/

https://reviews.llvm.org/D90161

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -11,6 +11,7 @@
 #include "clang/Tooling/Syntax/BuildTree.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -125,7 +126,7 @@
 }
 
 class ListTest : public SyntaxTreeTest {
-private:
+protected:
   std::string dumpQuotedTokensOrNull(const Node *N) {
 return N ? "'" +
StringRef(N->dumpTokens(Arena->getSourceManager()))
@@ -135,7 +136,15 @@
  : "null";
   }
 
-protected:
+  std::string
+  dumpElementAndDelimiter(const List::ElementAndDelimiter ED) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+   << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+return OS.str();
+  }
+
   std::string
   dumpElementsAndDelimiters(ArrayRef> EDs) {
 std::string Storage;
@@ -145,8 +154,7 @@
 
 llvm::interleaveComma(
 EDs, OS, [, this](const List::ElementAndDelimiter ) {
-  OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
- << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+  OS << dumpElementAndDelimiter(ED);
 });
 
 OS << "]";
@@ -351,4 +359,143 @@
   EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
 }
 
+TEST_P(ListTest, List_Terminated_Iterator_Parent) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a   b::  :: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersBeforeBegin().getParent(),
+List);
+
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End; ++It)
+EXPECT_EQ(It.getParent(), List);
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersEnd().getParent(), List);
+}
+
+TEST_P(ListTest, List_Terminated_Iterator_Equality) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a   b::  :: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  // different iterators are different.
+  for (auto BeforeIt = List->getElementsAsNodesAndDelimitersBeforeBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   BeforeIt != End; ++BeforeIt) {
+auto It = BeforeIt;
+++It;
+for (; It != End; ++It) {
+  EXPECT_NE(BeforeIt, It);
+}
+  }
+
+  // Equal iterators are equal.
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+It2 = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End && It2 != End;) {
+EXPECT_EQ(++It, ++It2);
+  }
+
+  // Different sentinel iterators are different.
+  EXPECT_NE(List->getElementsAsNodesAndDelimitersBeforeBegin(),
+List->getElementsAsNodesAndDelimitersEnd());
+}
+
+TEST_P(ListTest, List_Separated_Iterator_Parent) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a  b,  ,"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  },
+ 

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-26 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas abandoned this revision.
eduucaldas added inline comments.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:252
+  Kind = IteratorKind::NotSentinel;
+  if (isElement(Begin))
+Current = getWithDelimiter(cast(Begin));

Since `NodeRole` is forward declared, we cannot access `NodeRole::ListElement` 
within the header, this is my work-around.

Another possibility would be to not forward declare and put `NodeRole` 
definition here. I think this is a bad choice because most roles are linked to 
syntax.

I declare `isElement` as a static private member function of `List` from lack 
of better place, I accept suggestions.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:390
+
+  ElementAndDelimiterIterator getBeforeBeginNode();
+  ElementAndDelimiterIterator getBeginNode();

I have a hard time finding a name that is expressive enough and concise enough. 
The current one seems to imply that we're getting a node...

Alternatives:
* `getBeforeBeginWithElementAsNode`
* `getBeforeBeginWithNodes`
* `getBeforeBeginBase`
* `getBeforeBeginNodeAndDelimiter`




Comment at: clang/include/clang/Tooling/Syntax/Tree.h:241-242
   /// "a; b; c"  <=> [("a" , ";"), ("b" , ";" ), ("c" , null)]
+  template 
+  class ElementAndDelimiterIterator
+  : public llvm::iterator_facade_base<

eduucaldas wrote:
> Since we're gonna provide strongly-typed iterators, I make the Iterator a 
> class template.
> 
> I keep the base functions as they were, `getElementAndDelimiterAfter...`, but 
> I cast their result using `castToElementType`.
> 
> Another option would be to make the base functions also templated and not 
> perform any casting.
> 
> We'll use `castToElementType` to provide the strongly-typed iterators as well.
Ignore comment above


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

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


[PATCH] D90161: [SyntaxTree] Provide iterators for Lists

2020-10-26 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 300696.
eduucaldas added a comment.

Fix comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90161/new/

https://reviews.llvm.org/D90161

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -359,42 +359,143 @@
   EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
 }
 
-TEST_P(ListTest, List_Iterator_StableDereference) {
+TEST_P(ListTest, List_Terminated_Iterator_Parent) {
   if (!GetParam().isCXX()) {
 return;
   }
   buildTree("", GetParam());
 
-  // "a:: b:: c"
-  auto *List = dyn_cast(syntax::createTree(
+  // "a   b::  :: c"
+  auto *List = dyn_cast(syntax::createTree(
   *Arena,
   {
   {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
   {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersBeforeBegin().getParent(),
+List);
+
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End; ++It)
+EXPECT_EQ(It.getParent(), List);
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersEnd().getParent(), List);
+}
+
+TEST_P(ListTest, List_Terminated_Iterator_Equality) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a   b::  :: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
   {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
   {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
   {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
   },
   NodeKind::NestedNameSpecifier));
 
-  auto It = List->getElementsAsNodesAndDelimitersBegin();
-  syntax::List::ElementAndDelimiter First = {It.getElement(),
-   It.getDelimiter()};
-  auto ItAssign = It;
+  // different iterators are different.
+  for (auto BeforeIt = List->getElementsAsNodesAndDelimitersBeforeBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   BeforeIt != End; ++BeforeIt) {
+auto It = BeforeIt;
+++It;
+for (; It != End; ++It) {
+  EXPECT_NE(BeforeIt, It);
+}
+  }
 
-  EXPECT_EQ(dumpElementAndDelimiter(First), "('a', '::')");
-  ++It;
-  EXPECT_EQ(dumpElementAndDelimiter(First), "('a', '::')");
+  // Equal iterators are equal.
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+It2 = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End && It2 != End;) {
+EXPECT_EQ(++It, ++It2);
+  }
 
-  EXPECT_EQ(ItAssign.getElement(), First.element);
+  // Different sentinel iterators are different.
+  EXPECT_NE(List->getElementsAsNodesAndDelimitersBeforeBegin(),
+List->getElementsAsNodesAndDelimitersEnd());
+}
 
-  auto It2 = ++(++List->getElementsAsNodesAndDelimitersBeforeBegin());
-  EXPECT_EQ(It, It2);
-  EXPECT_EQ(It.getElement(), It2.getElement());
-  EXPECT_EQ(It.getDelimiter(), It2.getDelimiter());
+TEST_P(ListTest, List_Separated_Iterator_Parent) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a  b,  ,"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersBeforeBegin().getParent(),
+List);
+
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End; ++It)
+EXPECT_EQ(It.getParent(), List);
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersEnd().getParent(), List);
+}
+
+TEST_P(ListTest, List_Separated_Iterator_Equality) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a  b,  ,"
+  auto *List = dyn_cast(syntax::createTree(
+  

[PATCH] D90161: [SyntaxTree] Provide iterators for Lists

2020-10-26 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas marked an inline comment as done.
eduucaldas added a comment.

In D90161#2353736 , @gribozavr2 wrote:

> Could you add tests that verify the pairing of elements and delimiters?

Those are surfaced through the previous tests, via 
`getElementsAsNodesAndDelimiters`




Comment at: clang/include/clang/Tooling/Syntax/Tree.h:442
+  ElementAndDelimiterIterator
+  getElementsAsNodesAndDelimitersBeforeBegin();
+  ElementAndDelimiterIterator getElementsAsNodesAndDelimitersBegin();

What do you think?

I didn't change it, because we had agreed on `ElementsAsNodesAndDelimiters`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90161/new/

https://reviews.llvm.org/D90161

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


[PATCH] D90023: [Syntax] Add iterators over children of syntax trees.

2020-10-26 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas accepted this revision.
eduucaldas added a comment.

Thanks for the instructive replies


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90023/new/

https://reviews.llvm.org/D90023

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


[PATCH] D90161: [SyntaxTree] Provide iterators for Lists

2020-10-26 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Provide an iterator for `List` that iterates through elements and delimiters on 
pairs. This iterator is robust to missing elements. For instance, in the 
following function call:
`f(a b, , )`
The iterator would emit the following elements and delimiters:
`[("a", null), ("b", ","), (null, ","), (null, null) ]`

This iterator abstracts out many corner cases when dealing with lists, thus 
providing a cleaner interface for mutation and traversal of `List`s.

Note: This iterator has the memory footprint of one pointer.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D90161

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -11,6 +11,7 @@
 #include "clang/Tooling/Syntax/BuildTree.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -125,7 +126,7 @@
 }
 
 class ListTest : public SyntaxTreeTest {
-private:
+protected:
   std::string dumpQuotedTokensOrNull(const Node *N) {
 return N ? "'" +
StringRef(N->dumpTokens(Arena->getSourceManager()))
@@ -135,7 +136,15 @@
  : "null";
   }
 
-protected:
+  std::string
+  dumpElementAndDelimiter(const List::ElementAndDelimiter ED) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+   << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+return OS.str();
+  }
+
   std::string
   dumpElementsAndDelimiters(ArrayRef> EDs) {
 std::string Storage;
@@ -145,8 +154,7 @@
 
 llvm::interleaveComma(
 EDs, OS, [, this](const List::ElementAndDelimiter ) {
-  OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
- << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+  OS << dumpElementAndDelimiter(ED);
 });
 
 OS << "]";
@@ -351,4 +359,143 @@
   EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
 }
 
+TEST_P(ListTest, List_Terminated_Iterator_Parent) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a   b::  :: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersBeforeBegin().getParent(),
+List);
+
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End; ++It)
+EXPECT_EQ(It.getParent(), List);
+
+  EXPECT_EQ(List->getElementsAsNodesAndDelimitersEnd().getParent(), List);
+}
+
+TEST_P(ListTest, List_Terminated_Iterator_Equality) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a   b::  :: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  // different iterators are different.
+  for (auto BeforeIt = List->getElementsAsNodesAndDelimitersBeforeBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   BeforeIt != End; ++BeforeIt) {
+auto It = BeforeIt;
+++It;
+for (; It != End; ++It) {
+  EXPECT_NE(BeforeIt, It);
+}
+  }
+
+  // Equal iterators are equal.
+  for (auto It = List->getElementsAsNodesAndDelimitersBegin(),
+It2 = List->getElementsAsNodesAndDelimitersBegin(),
+End = List->getElementsAsNodesAndDelimitersEnd();
+   It != End && It2 != End;) {
+EXPECT_EQ(++It, ++It2);
+  }
+
+  // Different sentinel iterators are different.
+  EXPECT_NE(List->getElementsAsNodesAndDelimitersBeforeBegin(),
+List->getElementsAsNodesAndDelimitersEnd());
+}
+
+TEST_P(ListTest, 

[PATCH] D90023: [Syntax] Add iterators over children of syntax trees.

2020-10-23 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a comment.

Thanks Sam! I learned a lot from your patch ^^




Comment at: clang/include/clang/Tooling/Syntax/Tree.h:157-184
+  /// Iterator over children (common base for const/non-const).
+  /// Not invalidated by tree mutations (holds a stable node pointer).
+  template 
+  class child_iterator_base
+  : public llvm::iterator_facade_base {
+  protected:

I think we should only have an iterator through `Node`s, not a general one like 
this.

In general `Tree` has *heterogeneous* children:
For instance, `1+2` yields the syntax tree:
```
BinaryOperatorExpression
|-  IntegerLiteralExpression
|-  Leaf
`- IntegerLiteralExpression
```

Very rarely will syntax trees have all their children of the same kind, so I 
don't think it makes sense to try an provide an iterator template. That makes 
sense for lists , because generally they *have* the same kind. But in that case 
we provide special iterators for lists only.




Comment at: clang/include/clang/Tooling/Syntax/Tree.h:202-219
+  /// child_iterator is not invalidated by mutations.
+  struct child_iterator : child_iterator_base {
+using Base::Base;
+  };
+  struct const_child_iterator
+  : child_iterator_base {
+using Base::Base;

TL;DR:
`child_iterator` -> `ChildIterator`
`const_child_iterator` -> `ConstChildIterator`
`children` -> `getChildren`

I see you followed the naming convention of the ClangAST, which makes loads of 
sense. 

In syntax trees we follow the naming conventions of the [conding 
standard](https://llvm.org/docs/CodingStandards.html#name-types-functions-variables-and-enumerators-properly)
 -- just because we want consistency and we had to pick a guideline to follow 
-- according to that, functions should be verb like and names should be camel 
case.

If like us you chose `const_child_iterator` and `children` kind of "just 
because", could you change it to follow `ConstChildIterator` and `getChildren` 
respectively. 



Comment at: clang/lib/Tooling/Syntax/Tree.cpp:22-23
   if (auto *T = dyn_cast(N)) {
-for (const auto *C = T->getFirstChild(); C; C = C->getNextSibling())
-  traverse(C, Visit);
+for (const syntax::Node  : T->children())
+  traverse(, Visit);
   }

Hmm... 
That looks funny, if the user uses a range-based for loop and forgets the `&`, 
then we would be copying the Node???

Also in many places we had to get the address to the node. That is not really 
consistent with how the syntax trees API's were designed, they generally take 
pointers instead of references. (that said we could obviously reconsider those 
design choices)



Comment at: clang/unittests/Tooling/Syntax/TreeTest.cpp:129
 
+TEST_F(TreeTest, Iterators) {
+  buildTree("", allTestClangConfigs().front());

Suggestion:
I would split this into `Iterators` and `ConstIterators`.



Comment at: clang/unittests/Tooling/Syntax/TreeTest.cpp:139
+  NodeKind::TranslationUnit);
+  const syntax::Tree *ConstTree = Tree;
+

nit, feel free to ignore.



Comment at: clang/unittests/Tooling/Syntax/TreeTest.cpp:159-169
+  for (unsigned I = 0; I < 3; ++I) {
+EXPECT_EQ(It, CIt);
+EXPECT_TRUE(It);
+EXPECT_TRUE(CIt);
+EXPECT_EQ(It.asPointer(), Children[I]);
+EXPECT_EQ(CIt.asPointer(), Children[I]);
+EXPECT_EQ(&*It, Children[I]);

Is there a reason why you didn't use a range-based for loop over Children?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D90023/new/

https://reviews.llvm.org/D90023

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


[PATCH] D88553: [clangd] Start using SyntaxTrees for folding ranges feature

2020-10-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added inline comments.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:172-174
   /// Find the first node with a corresponding role.
   Node *findChild(NodeRole R);
+  const Node *findChild(NodeRole R) const;

I think that makes sense, since all the functions used by `findChild` are 
public anyways.

WDYT Dmitri? Now that the API is being used I realize its surface ^^. Perhaps 
we should pour some thought into that in the future :) 




Comment at: clang/lib/Tooling/Syntax/Tree.cpp:304-308
+  for (const auto *C = FirstChild; C; C = C->getNextSibling()) {
+if (C->getRole() == R)
+  return C;
+  }
+  return nullptr;

Similarly to the const version of `findFirstLeaf`. I think this should work :)

Also you could put the definition in `clang/Tooling/Syntax/Tree.h`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88553/new/

https://reviews.llvm.org/D88553

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


[PATCH] D88553: [clangd] Start using SyntaxTrees for folding ranges feature

2020-10-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added inline comments.



Comment at: clang-tools-extra/clangd/SemanticSelection.cpp:52-55
+const syntax::Token *FirstToken = Tree->findFirstLeaf()->getToken(),
+*LastToken = Tree->findLastLeaf()->getToken();
+assert(FirstToken->kind() == tok::TokenKind::l_brace);
+assert(LastToken->kind() == tok::TokenKind::r_brace);

sammccall wrote:
> kbobyrev wrote:
> > sammccall wrote:
> > > eduucaldas wrote:
> > > > Take a look at `clang/include/clang/Tooling/Syntax/Nodes.h`, syntax 
> > > > constructs usually have nice classes with accessors.
> > > > 
> > > > For instance `CompoundStatement` has the accessors `getLbrace` and 
> > > > `getRbrace` that seem to be exactly what you want.
> > > > 
> > > > However these might not give exactly the first leaf and last leaf in 
> > > > the case of syntactically incorrect code.
> > > I think we should treat all bracket-like things generically. Today this 
> > > is just CompoundStmt, but we want to handle init lists, function calls, 
> > > parens around for-loop conditions, template parameter and arg lists etc 
> > > in the same way.
> > > 
> > > This sort of use is why the `OpenParen`/`CloseParen` NodeRoles are 
> > > generic - we can have one set of logic to handle all of these. (No 
> > > opinion on whether that should live here or in the syntax trees library, 
> > > but putting it here for now seems fine).
> > > 
> > > So in the end I think checking the class name and then grabbing the 
> > > braces by role (not kind) is the right thing here.
> > > We definitely want to avoid asserting that the code looks the way we 
> > > expect though.
> > > So in the end I think checking the class name and then grabbing the 
> > > braces by role (not kind) is the right thing here.
> > > We definitely want to avoid asserting that the code looks the way we 
> > > expect though.
> > 
> > Can you elaborate a bit on how this would work? Is your proposal to iterate 
> > through `CompoundStatement` first-level children and grab the `OpenParen` + 
> > `CloseParen` roles?
> Exactly. And bail out if both don't exist.
> 
> And this can be done on Tree, so it's trivial to add support for function 
> calls etc (but feel free to keep the scope small)
I very much agree on all the points you made Sam.

I think the logic to get ranges from node roles should eventually live in the 
syntax trees library. We might want to hide some of these lower-level functions 
in the future, so it would be better to not rely on them. But it's no harm for 
now :).


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88553/new/

https://reviews.llvm.org/D88553

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


[PATCH] D89794: [SyntaxTree] Implement "by-pointer output parameter to return value" refactoring.

2020-10-20 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Example of such refactoring:

  Customer* C;
  getCustumer(C);

  Customer C = getCustumer();


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D89794

Files:
  clang/include/clang/Tooling/Syntax/Mutations.h
  clang/lib/Tooling/Syntax/Mutations.cpp

Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -85,6 +85,92 @@
 P->assertInvariants();
 N->assertInvariants();
   }
+
+  static void
+  replaceChildRange(syntax::Tree *Parent, syntax::Node *BeforeBegin,
+syntax::Node *End,
+ArrayRef> NewRange) {
+if (NewRange.empty())
+  return Parent->replaceChildRangeLowLevel(BeforeBegin, End, nullptr);
+
+for (const auto  : NewRange)
+  NodeAndRole.first->setRole(NodeAndRole.second);
+
+for (auto It = NewRange.begin(), EndIt = std::prev(NewRange.end());
+ It != EndIt; ++It) {
+  auto *Node = It->first;
+  auto *Next = std::next(It)->first;
+  Node->NextSibling = Next;
+}
+
+Parent->replaceChildRangeLowLevel(BeforeBegin, End, NewRange.front().first);
+  }
+
+  /// Removes all the `Node`s between the iterators `BeforeBegin` and `End`. For
+  /// separated lists, if we remove the last element we may need to remove the
+  /// delimiter before it, in order to keep it a separated list.
+  static void removeElementAndDelimiterRange(
+  List::ElementAndDelimiterIterator BeforeBegin,
+  List::ElementAndDelimiterIterator End) {
+auto *EndNode = []() -> Node * {
+  if (End.isEnd())
+return nullptr;
+  if ((*End).element)
+return (*End).element;
+  return (*End).delimiter;
+}();
+auto *Parent = End.getParent();
+Node *BeforeBeginNode;
+if (End.isEnd() &&
+Parent->getTerminationKind() == List::TerminationKind::Separated) {
+  // In this case we also remove the delimiter before the last element of
+  // the list, if it exists.
+  if (BeforeBegin.isBeforeBegin())
+BeforeBeginNode = nullptr;
+  else if ((*BeforeBegin).element)
+BeforeBeginNode = (*BeforeBegin).element;
+  else if ((*BeforeBegin).delimiter)
+BeforeBeginNode = findPrevious((*BeforeBegin).delimiter);
+  else
+// `BeforeBegin` = (null, null) and thus we are in the last element of
+// the list. Nothing will be removed.
+return;
+} else
+  BeforeBeginNode = []() -> Node * {
+if (BeforeBegin.isBeforeBegin())
+  return nullptr;
+if ((*BeforeBegin).delimiter)
+  return (*BeforeBegin).delimiter;
+if ((*BeforeBegin).element)
+  return (*BeforeBegin).element;
+llvm_unreachable("(null, null) can only appear in the end of a "
+ "separated range, and this is not possible here");
+  }();
+
+Parent->replaceChildRangeLowLevel(BeforeBeginNode, EndNode, nullptr);
+  }
+
+  static Node *extractLastElementAndDelimiter(List *Parent) {
+auto Prev = [](List::ElementAndDelimiterIterator EDI,
+   unsigned i = 1) {
+  auto Previous = EDI.getParent()->getElementAndDelimiterBeforeBegin();
+  auto It = Previous;
+  for (unsigned j = 0; j < i; ++j)
+++It;
+  for (; It != EDI; ++It)
+++Previous;
+
+  return Previous;
+};
+if (Parent->getElementAndDelimiterBegin() ==
+Parent->getElementAndDelimiterEnd())
+  return nullptr;
+auto BeforeLast = Prev(Parent->getElementAndDelimiterEnd(), 2);
+auto Last = std::next(BeforeLast);
+removeElementAndDelimiterRange(BeforeLast,
+   Parent->getElementAndDelimiterEnd());
+return (*Last).element;
+  }
 };
 
 void syntax::removeStatement(syntax::Arena , syntax::Statement *S) {
@@ -102,3 +188,39 @@
 
   MutationsImpl::replace(S, createEmptyStatement(A));
 }
+
+void syntax::replaceOutputParameterWithReturnValue(syntax::Arena ,
+   syntax::CallExpression *CE) {
+  assert(CE);
+  assert(CE->canModify());
+  assert(CE->getParent());
+
+  // Extract Output Parameter
+  auto *OutParam =
+  MutationsImpl::extractLastElementAndDelimiter(CE->getArguments());
+
+  // Create pointer dereference with outParam
+  auto *DerefOutParam =
+  createTree(A,
+ {{createLeaf(A, tok::star), NodeRole::OperatorToken},
+  {OutParam, NodeRole::Operand}},
+ NodeKind::PrefixUnaryOperatorExpression);
+
+  // f(); -> *outParam = f();
+  // TODO: How to improve this: Add parent to AddAfter. Or `NodeIterator`, with
+  // a pointer to Parent when it's a sentinel.
+  auto *Parent = CE->getParent();
+  auto *BeforeBegin = findPrevious(CE);
+  

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-20 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 299348.
eduucaldas added a comment.

rename getBeforeBegin


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -11,6 +11,7 @@
 #include "clang/Tooling/Syntax/BuildTree.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -125,7 +126,7 @@
 }
 
 class ListTest : public SyntaxTreeTest {
-private:
+protected:
   std::string dumpQuotedTokensOrNull(const Node *N) {
 return N ? "'" +
StringRef(N->dumpTokens(Arena->getSourceManager()))
@@ -135,7 +136,15 @@
  : "null";
   }
 
-protected:
+  std::string
+  dumpElementAndDelimiter(const List::ElementAndDelimiter ED) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+   << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+return OS.str();
+  }
+
   std::string
   dumpElementsAndDelimiters(ArrayRef> EDs) {
 std::string Storage;
@@ -145,8 +154,7 @@
 
 llvm::interleaveComma(
 EDs, OS, [, this](const List::ElementAndDelimiter ) {
-  OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
- << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+  OS << dumpElementAndDelimiter(ED);
 });
 
 OS << "]";
@@ -351,4 +359,40 @@
   EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
 }
 
+TEST_P(ListTest, List_Iterator_StableDereference) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a:: b:: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  auto It = List->getElementAndDelimiterBegin();
+  const auto  = *It;
+  auto ItAssign = It;
+
+  EXPECT_EQ(dumpElementAndDelimiter(First), "('a', '::')");
+  ++It;
+  EXPECT_EQ(dumpElementAndDelimiter(First), "('a', '::')");
+
+  EXPECT_EQ(*ItAssign, First);
+
+  auto It2 = std::next(List->getElementAndDelimiterBeforeBegin(), 2);
+  EXPECT_EQ(It, It2);
+  EXPECT_EQ(*It, *It2);
+
+  ++It;
+  ++It;
+  EXPECT_EQ(It, List->getElementAndDelimiterEnd());
+}
 } // namespace
Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -311,91 +311,41 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
-  }
+bool syntax::List::isElement(syntax::Node *N) {
+  return N && N->getRole() == NodeRole::ListElement;
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
-}
-break;
-  }
-  }
+bool syntax::List::isDelimiter(syntax::Node *N) {
+  return N && N->getRole() == NodeRole::ListDelimiter;
+}
 
-  return Children;
+syntax::List::ElementAndDelimiterIterator
+syntax::List::getElementAndDelimiterBeforeBegin() {
+  return ElementAndDelimiterIterator::makeBeforeBegin(this);
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-20 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 299347.
eduucaldas marked 2 inline comments as not done.
eduucaldas added a comment.

Add tests, `ElementAndDelimiter` are input iterators


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -11,6 +11,7 @@
 #include "clang/Tooling/Syntax/BuildTree.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -125,7 +126,7 @@
 }
 
 class ListTest : public SyntaxTreeTest {
-private:
+protected:
   std::string dumpQuotedTokensOrNull(const Node *N) {
 return N ? "'" +
StringRef(N->dumpTokens(Arena->getSourceManager()))
@@ -135,7 +136,15 @@
  : "null";
   }
 
-protected:
+  std::string
+  dumpElementAndDelimiter(const List::ElementAndDelimiter ED) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+   << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+return OS.str();
+  }
+
   std::string
   dumpElementsAndDelimiters(ArrayRef> EDs) {
 std::string Storage;
@@ -145,8 +154,7 @@
 
 llvm::interleaveComma(
 EDs, OS, [, this](const List::ElementAndDelimiter ) {
-  OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
- << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+  OS << dumpElementAndDelimiter(ED);
 });
 
 OS << "]";
@@ -351,4 +359,40 @@
   EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
 }
 
+TEST_P(ListTest, List_Iterator_StableDereference) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a:: b:: c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::coloncolon), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::NestedNameSpecifier));
+
+  auto It = List->getBeginNode();
+  const auto  = *It;
+  auto ItAssign = It;
+
+  EXPECT_EQ(dumpElementAndDelimiter(First), "('a', '::')");
+  ++It;
+  EXPECT_EQ(dumpElementAndDelimiter(First), "('a', '::')");
+
+  EXPECT_EQ(*ItAssign, First);
+
+  auto It2 = std::next(List->getBeforeBegin(), 2);
+  EXPECT_EQ(It, It2);
+  EXPECT_EQ(*It, *It2);
+
+  ++It;
+  ++It;
+  EXPECT_EQ(It, List->getEndNode());
+}
 } // namespace
Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -311,91 +311,40 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
-  }
+bool syntax::List::isElement(syntax::Node *N) {
+  return N && N->getRole() == NodeRole::ListElement;
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
-}
-break;
-  }
-  }
+bool syntax::List::isDelimiter(syntax::Node *N) {
+  return N && N->getRole() == NodeRole::ListDelimiter;
+}
 
-  return Children;
+syntax::List::ElementAndDelimiterIterator
+syntax::List::getBeforeBegin() {
+  return ElementAndDelimiterIterator::makeBeforeBegin(this);
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring 

[PATCH] D88553: [clangd] Start using SyntaxTrees for folding ranges feature

2020-10-20 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added inline comments.



Comment at: clang-tools-extra/clangd/SemanticSelection.cpp:49-51
+  if (Node->getKind() == syntax::NodeKind::CompoundStatement) {
+const auto *Tree = dyn_cast(Node);
+assert(Tree);





Comment at: clang-tools-extra/clangd/SemanticSelection.cpp:52-55
+const syntax::Token *FirstToken = Tree->findFirstLeaf()->getToken(),
+*LastToken = Tree->findLastLeaf()->getToken();
+assert(FirstToken->kind() == tok::TokenKind::l_brace);
+assert(LastToken->kind() == tok::TokenKind::r_brace);

Take a look at `clang/include/clang/Tooling/Syntax/Nodes.h`, syntax constructs 
usually have nice classes with accessors.

For instance `CompoundStatement` has the accessors `getLbrace` and `getRbrace` 
that seem to be exactly what you want.

However these might not give exactly the first leaf and last leaf in the case 
of syntactically incorrect code.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88553/new/

https://reviews.llvm.org/D88553

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


[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-15 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 298420.
eduucaldas added a comment.

Linting


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -311,91 +311,40 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
-  }
+bool syntax::List::isElement(syntax::Node *N) {
+  return N && N->getRole() == NodeRole::ListElement;
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
-}
-break;
-  }
-  }
+bool syntax::List::isDelimiter(syntax::Node *N) {
+  return N && N->getRole() == NodeRole::ListDelimiter;
+}
 
-  return Children;
+syntax::List::ElementAndDelimiterIterator
+syntax::List::getBeforeBeginWithElementAsNode() {
+  return ElementAndDelimiterIterator::makeBeforeBegin(this);
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
-std::vector syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-return {};
+syntax::List::ElementAndDelimiterIterator
+syntax::List::getBeginNode() {
+  return std::next(ElementAndDelimiterIterator::makeBeforeBegin(this));
+}
 
-  std::vector Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back(ElementWithoutDelimiter);
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back(ElementWithoutDelimiter);
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable("A list has only elements or delimiters.");
-}
-  }
+syntax::List::ElementAndDelimiterIterator
+syntax::List::getEndNode() {
+  return ElementAndDelimiterIterator::makeEnd(this);
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back(ElementWithoutDelimiter);
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back(ElementWithoutDelimiter);
-}
-break;
-  }
-  }
+std::vector>
+syntax::List::getElementsAsNodesAndDelimiters() {
+  return std::vector>(
+  getBeginNode(), getEndNode());
+}
 
+std::vector syntax::List::getElementsAsNodes() {
+  std::vector Children;
+  std::transform(
+  getBeginNode(), getEndNode(), std::back_inserter(Children),
+  [](ElementAndDelimiter ED) { return ED.element; });
   return Children;
 }
 
Index: clang/lib/Tooling/Syntax/Nodes.cpp
===
--- clang/lib/Tooling/Syntax/Nodes.cpp
+++ clang/lib/Tooling/Syntax/Nodes.cpp
@@ -230,91 +230,77 @@
 // vector
 std::vector
 syntax::NestedNameSpecifier::getSpecifiers() {
-  auto SpecifiersAsNodes = getElementsAsNodes();
   std::vector Children;
-  for (const auto  : SpecifiersAsNodes) {
-Children.push_back(llvm::cast(Element));
-  }
+  for (auto C : getNodeRange())
+Children.push_back(cast(C.element));
+
   return Children;
 }
 
 std::vector>
 syntax::NestedNameSpecifier::getSpecifiersAndDoubleColons() {
-  auto SpecifiersAsNodesAndDoubleColons = getElementsAsNodesAndDelimiters();
   std::vector>
   Children;
-  for (const auto  : SpecifiersAsNodesAndDoubleColons) {
-Children.push_back(
-{llvm::cast(SpecifierAndDoubleColon.element),
- SpecifierAndDoubleColon.delimiter});
-  }
+  for (auto C : getNodeRange())
+

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-15 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 298416.
eduucaldas added a comment.

- [SyntaxTree] `ElementAndDelimiterIterator` is polymorphic and supports 
`BeforeBegin` iterator


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -9,6 +9,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
 #include 
@@ -311,91 +312,40 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
-  }
+bool syntax::List::isElement(syntax::Node *N) {
+  return N && N->getRole() == NodeRole::ListElement;
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
-}
-break;
-  }
-  }
+bool syntax::List::isDelimiter(syntax::Node *N) {
+  return N && N->getRole() == NodeRole::ListDelimiter;
+}
 
-  return Children;
+syntax::List::ElementAndDelimiterIterator
+syntax::List::getBeforeBeginNode() {
+  return ElementAndDelimiterIterator::makeBeforeBegin(this);
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
-std::vector syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-return {};
+syntax::List::ElementAndDelimiterIterator
+syntax::List::getBeginNode() {
+  return std::next(ElementAndDelimiterIterator::makeBeforeBegin(this));
+}
 
-  std::vector Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back(ElementWithoutDelimiter);
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back(ElementWithoutDelimiter);
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable("A list has only elements or delimiters.");
-}
-  }
+syntax::List::ElementAndDelimiterIterator
+syntax::List::getEndNode() {
+  return ElementAndDelimiterIterator::makeEnd(this);
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back(ElementWithoutDelimiter);
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back(ElementWithoutDelimiter);
-}
-break;
-  }
-  }
+std::vector>
+syntax::List::getElementsAsNodesAndDelimiters() {
+  return std::vector>(
+  getBeginNode(), getEndNode());
+}
 
+std::vector syntax::List::getElementsAsNodes() {
+  std::vector Children;
+  std::transform(
+  getBeginNode(), getEndNode(), std::back_inserter(Children),
+  [](ElementAndDelimiter ED) { return ED.element; });
   return Children;
 }
 
Index: clang/lib/Tooling/Syntax/Nodes.cpp
===
--- clang/lib/Tooling/Syntax/Nodes.cpp
+++ clang/lib/Tooling/Syntax/Nodes.cpp
@@ -230,91 +230,77 @@
 // vector
 std::vector
 syntax::NestedNameSpecifier::getSpecifiers() {
-  auto SpecifiersAsNodes = getElementsAsNodes();
   std::vector Children;
-  for (const auto  : SpecifiersAsNodes) {
-Children.push_back(llvm::cast(Element));
-  }
+  for (auto C : getNodeRange())
+Children.push_back(cast(C.element));
+
   return Children;
 }
 
 std::vector>
 syntax::NestedNameSpecifier::getSpecifiersAndDoubleColons() {
-  auto 

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added inline comments.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:241-242
   /// "a; b; c"  <=> [("a" , ";"), ("b" , ";" ), ("c" , null)]
+  template 
+  class ElementAndDelimiterIterator
+  : public llvm::iterator_facade_base<

Since we're gonna provide strongly-typed iterators, I make the Iterator a class 
template.

I keep the base functions as they were, `getElementAndDelimiterAfter...`, but I 
cast their result using `castToElementType`.

Another option would be to make the base functions also templated and not 
perform any casting.

We'll use `castToElementType` to provide the strongly-typed iterators as well.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

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


[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a comment.

Haven't yet implemented `BeforeBegin`, waiting for a heads up on the patch as 
is.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

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


[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added inline comments.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:269
+  return EDI == Other.EDI;
+}
+

gribozavr2 wrote:
> Please also define `operator!=`.
this is defined by the `iterato_facade_base`, take a look at the comments in 
this class template



Comment at: clang/lib/Tooling/Syntax/Tree.cpp:405
 
   return Children;
 }

gribozavr2 wrote:
> Ditto?
Here we could do `std::transform(getBegin(), getEnd(), 
std::back_inserter(result), [](ElementAndDelimiter ED){return ED.element;})`. 
Is that more idiomatic?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

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


[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 298156.
eduucaldas marked an inline comment as done.
eduucaldas added a comment.

- Make `ElementAndDelimiterIterator` templated.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -9,6 +9,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
 #include 
@@ -311,91 +312,112 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
+syntax::List::ElementAndDelimiter
+syntax::List::getWithDelimiter(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+return {Element, nullptr};
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return {Element, nullptr};
+  case syntax::NodeRole::ListDelimiter:
+return {Element, cast(Next)};
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterDelimiter(syntax::Leaf *Delimiter) {
+  assert(Delimiter && Delimiter->getRole() == syntax::NodeRole::ListDelimiter);
+  auto *List = cast(Delimiter->getParent());
+  auto *Next = Delimiter->getNextSibling();
+  if (!Next) {
+switch (List->getTerminationKind()) {
+// List is separated and ends with delimiter
+// => missing last ElementAndDelimiter.
+case syntax::List::TerminationKind::Separated:
+  return llvm::Optional>(
+  {nullptr, nullptr});
+case syntax::List::TerminationKind::Terminated:
+case syntax::List::TerminationKind::MaybeTerminated:
+  return None;
 }
-break;
   }
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // Consecutive Delimiters => missing Element
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(Next)});
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
-
-  return Children;
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
-std::vector syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-return {};
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterElement(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+// This was the last element of the list.
+return None;
+
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+// We have something of the form "a b ..." => 'b' starts the next
+// `ElementAndDelimiter`.
+return getWithDelimiter(Next);
+
+  case syntax::NodeRole::ListDelimiter:
+// We have something of the form "a , ..." => whatever comes after the comma
+// starts the next `ElementAndDelimiter`.
+return getElementAndDelimiterAfterDelimiter(cast(Next));
 
-  std::vector Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back(ElementWithoutDelimiter);
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case 

[PATCH] D89148: [SyntaxTree] Artificial use of the Mutations API.

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 298092.
eduucaldas added a comment.

Rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89148/new/

https://reviews.llvm.org/D89148

Files:
  clang/include/clang/Tooling/Syntax/Mutations.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/unittests/Tooling/Syntax/MutationsTest.cpp


Index: clang/unittests/Tooling/Syntax/MutationsTest.cpp
===
--- clang/unittests/Tooling/Syntax/MutationsTest.cpp
+++ clang/unittests/Tooling/Syntax/MutationsTest.cpp
@@ -52,6 +52,16 @@
 EXPECT_FALSE(S->isOriginal())
 << "node removed from tree cannot be marked as original";
   };
+
+  Transformation ParenthesizeRHS = [this](const llvm::Annotations ,
+  TranslationUnit *Root) {
+auto *BOE = cast(
+nodeByRange(Input.range(), Root));
+ASSERT_TRUE(BOE->canModify()) << "cannot remove a statement";
+syntax::parenthesizeRHS(*Arena, BOE);
+EXPECT_FALSE(BOE->isOriginal())
+<< "modified node cannot be marked as original";
+  };
 };
 
 INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, MutationTest,
@@ -71,4 +81,22 @@
   CheckTransformation(RemoveStatement, "void test() { if (1) [[{}]] else {} }",
   "void test() { if (1) ; else {} }");
 }
+
+TEST_P(MutationTest, Parenthesize_RHS) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(void test() { [[1 + 2]]; })cpp",
+  "void test() { 1 + (2); }");
+}
+
+TEST_P(MutationTest, Parenthesize_RHS_MACRO) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(
+#define THENUMBER 42
+void test() {
+  [[1 + (THENUMBER)]];
+})cpp",
+  R"cpp(
+#define THENUMBER 42
+void test() {
+  1 + ((THENUMBER));
+})cpp");
+}
 } // namespace
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -8,6 +8,7 @@
 #include "clang/Tooling/Syntax/Mutations.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
 #include "clang/Lex/Token.h"
 #include "clang/Tooling/Core/Replacement.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
@@ -102,3 +103,22 @@
 
   MutationsImpl::replace(S, createEmptyStatement(A));
 }
+
+void syntax::parenthesizeRHS(syntax::Arena ,
+ syntax::BinaryOperatorExpression *BOE) {
+  assert(BOE);
+  assert(BOE->canModify());
+
+  auto *RHS = BOE->getRhs();
+  auto *BeforeRHS = findPrevious(RHS);
+  MutationsImpl::remove(RHS);
+
+  auto *NewRHS =
+  createTree(A,
+ {{createLeaf(A, tok::l_paren), NodeRole::OpenParen},
+  {RHS, NodeRole::SubExpression},
+  {createLeaf(A, tok::r_paren), NodeRole::CloseParen}},
+ NodeKind::ParenExpression);
+
+  MutationsImpl::addAfter(BeforeRHS, NewRHS, NodeRole::RightHandSide);
+}
Index: clang/include/clang/Tooling/Syntax/Mutations.h
===
--- clang/include/clang/Tooling/Syntax/Mutations.h
+++ clang/include/clang/Tooling/Syntax/Mutations.h
@@ -31,6 +31,7 @@
 /// EXPECTS: S->canModify() == true
 void removeStatement(syntax::Arena , syntax::Statement *S);
 
+void parenthesizeRHS(syntax::Arena , syntax::BinaryOperatorExpression *BOE);
 } // namespace syntax
 } // namespace clang
 


Index: clang/unittests/Tooling/Syntax/MutationsTest.cpp
===
--- clang/unittests/Tooling/Syntax/MutationsTest.cpp
+++ clang/unittests/Tooling/Syntax/MutationsTest.cpp
@@ -52,6 +52,16 @@
 EXPECT_FALSE(S->isOriginal())
 << "node removed from tree cannot be marked as original";
   };
+
+  Transformation ParenthesizeRHS = [this](const llvm::Annotations ,
+  TranslationUnit *Root) {
+auto *BOE = cast(
+nodeByRange(Input.range(), Root));
+ASSERT_TRUE(BOE->canModify()) << "cannot remove a statement";
+syntax::parenthesizeRHS(*Arena, BOE);
+EXPECT_FALSE(BOE->isOriginal())
+<< "modified node cannot be marked as original";
+  };
 };
 
 INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, MutationTest,
@@ -71,4 +81,22 @@
   CheckTransformation(RemoveStatement, "void test() { if (1) [[{}]] else {} }",
   "void test() { if (1) ; else {} }");
 }
+
+TEST_P(MutationTest, Parenthesize_RHS) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(void test() { [[1 + 2]]; })cpp",
+  "void test() { 1 + (2); }");
+}
+
+TEST_P(MutationTest, Parenthesize_RHS_MACRO) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(
+#define THENUMBER 42
+void test() {
+  [[1 + (THENUMBER)]];
+})cpp",
+  R"cpp(
+#define THENUMBER 42
+void test() {
+  1 + ((THENUMBER));
+})cpp");
+}
 } // namespace
Index: 

[PATCH] D89303: [SyntaxTree] Improve safety of `replaceChildRangeLowLevel`

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas marked an inline comment as done.
eduucaldas added inline comments.



Comment at: clang/lib/Tooling/Syntax/Tree.cpp:122
 #endif
+  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+

gribozavr2 wrote:
> Could you move this definition up so that it can be used in the last assert 
> above?
Done in a separate commit: 6fbad9bf304c05d37454420f7d5a1c2ab3adab20


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89303/new/

https://reviews.llvm.org/D89303

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


[PATCH] D89314: [SyntaxTree] Bug fix in `MutationsImpl::addAfter`.

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG72732acade77: [SyntaxTree] Bug fix in 
`MutationsImpl::addAfter`. (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89314/new/

https://reviews.llvm.org/D89314

Files:
  clang/lib/Tooling/Syntax/Mutations.cpp


Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,19 @@
 
 using namespace clang;
 
+static syntax::Node *findPrevious(syntax::Node *N) {
+  assert(N);
+  assert(N->getParent());
+  if (N->getParent()->getFirstChild() == N)
+return nullptr;
+  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
+   C = C->getNextSibling()) {
+if (C->getNextSibling() == N)
+  return C;
+  }
+  llvm_unreachable("could not find a child node");
+}
+
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -30,14 +43,15 @@
   /// Add a new node with a specified role.
   static void addAfter(syntax::Node *Anchor, syntax::Node *New, NodeRole Role) 
{
 assert(Anchor != nullptr);
+assert(Anchor->Parent != nullptr);
 assert(New->Parent == nullptr);
 assert(New->NextSibling == nullptr);
-assert(!New->isDetached());
+assert(New->isDetached());
 assert(Role != NodeRole::Detached);
 
 New->setRole(Role);
 auto *P = Anchor->getParent();
-P->replaceChildRangeLowLevel(Anchor, Anchor, New);
+P->replaceChildRangeLowLevel(Anchor, Anchor->getNextSibling(), New);
 
 P->assertInvariants();
   }
@@ -60,6 +74,10 @@
 
   /// Completely remove the node from its parent.
   static void remove(syntax::Node *N) {
+assert(N != nullptr);
+assert(N->Parent != nullptr);
+assert(N->canModify());
+
 auto *P = N->getParent();
 P->replaceChildRangeLowLevel(findPrevious(N), N->getNextSibling(),
  /*New=*/nullptr);
@@ -67,18 +85,6 @@
 P->assertInvariants();
 N->assertInvariants();
   }
-
-private:
-  static syntax::Node *findPrevious(syntax::Node *N) {
-if (N->getParent()->getFirstChild() == N)
-  return nullptr;
-for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
- C = C->getNextSibling()) {
-  if (C->getNextSibling() == N)
-return C;
-}
-llvm_unreachable("could not find a child node");
-  }
 };
 
 void syntax::removeStatement(syntax::Arena , syntax::Statement *S) {


Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,19 @@
 
 using namespace clang;
 
+static syntax::Node *findPrevious(syntax::Node *N) {
+  assert(N);
+  assert(N->getParent());
+  if (N->getParent()->getFirstChild() == N)
+return nullptr;
+  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
+   C = C->getNextSibling()) {
+if (C->getNextSibling() == N)
+  return C;
+  }
+  llvm_unreachable("could not find a child node");
+}
+
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -30,14 +43,15 @@
   /// Add a new node with a specified role.
   static void addAfter(syntax::Node *Anchor, syntax::Node *New, NodeRole Role) {
 assert(Anchor != nullptr);
+assert(Anchor->Parent != nullptr);
 assert(New->Parent == nullptr);
 assert(New->NextSibling == nullptr);
-assert(!New->isDetached());
+assert(New->isDetached());
 assert(Role != NodeRole::Detached);
 
 New->setRole(Role);
 auto *P = Anchor->getParent();
-P->replaceChildRangeLowLevel(Anchor, Anchor, New);
+P->replaceChildRangeLowLevel(Anchor, Anchor->getNextSibling(), New);
 
 P->assertInvariants();
   }
@@ -60,6 +74,10 @@
 
   /// Completely remove the node from its parent.
   static void remove(syntax::Node *N) {
+assert(N != nullptr);
+assert(N->Parent != nullptr);
+assert(N->canModify());
+
 auto *P = N->getParent();
 P->replaceChildRangeLowLevel(findPrevious(N), N->getNextSibling(),
  /*New=*/nullptr);
@@ -67,18 +85,6 @@
 P->assertInvariants();
 N->assertInvariants();
   }
-
-private:
-  static syntax::Node *findPrevious(syntax::Node *N) {
-if (N->getParent()->getFirstChild() == N)
-  return nullptr;
-for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
- C = C->getNextSibling()) {
-  if (C->getNextSibling() == N)
-return C;
-}
-

[PATCH] D89303: [SyntaxTree] Improve safety of `replaceChildRangeLowLevel`

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG4178f8f2f08e: [SyntaxTree] Improve safety of 
`replaceChildRangeLowLevel` (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89303/new/

https://reviews.llvm.org/D89303

Files:
  clang/lib/Tooling/Syntax/Tree.cpp


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,65 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+
+  if (!New && Begin == End)
+return;
+
+  // Mark modification.
+  for (auto *T = this; T && T->Original; T = T->Parent)
+T->Original = false;
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  if (!New) {
+Begin = End;
+return;
+  }
   // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
-
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  Begin = New;
+  auto *Last = New;
+  for (auto *N = New; N != nullptr; N = N->NextSibling) {
+Last = N;
+N->Parent = this;
   }
-
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  Last->NextSibling = End;
 }
 
 namespace {


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,65 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+
+  if (!New && Begin == End)
+return;
+
+  // Mark modification.
+  for (auto *T = this; T && T->Original; T = T->Parent)
+T->Original = false;
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if 

[PATCH] D89303: [SyntaxTree] Improve safety of `replaceChildRangeLowLevel`

2020-10-14 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 298079.
eduucaldas marked 2 inline comments as done.
eduucaldas added a comment.

Answer to comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89303/new/

https://reviews.llvm.org/D89303

Files:
  clang/lib/Tooling/Syntax/Tree.cpp


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,65 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+
+  if (!New && Begin == End)
+return;
+
+  // Mark modification.
+  for (auto *T = this; T && T->Original; T = T->Parent)
+T->Original = false;
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  if (!New) {
+Begin = End;
+return;
+  }
   // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
-
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  Begin = New;
+  auto *Last = New;
+  for (auto *N = New; N != nullptr; N = N->NextSibling) {
+Last = N;
+N->Parent = this;
   }
-
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  Last->NextSibling = End;
 }
 
 namespace {


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,65 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  Node * = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+
+  if (!New && Begin == End)
+return;
+
+  // Mark modification.
+  for (auto *T = this; T && T->Original; T = T->Parent)
+T->Original = false;
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original 

[PATCH] D89146: [SyntaxTree] Fix rtti for `Expression`.

2020-10-13 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGa8f1790fdb8c: [SyntaxTree] Fix rtti for `Expression`. 
(authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89146/new/

https://reviews.llvm.org/D89146

Files:
  clang/include/clang/Tooling/Syntax/Nodes.h


Index: clang/include/clang/Tooling/Syntax/Nodes.h
===
--- clang/include/clang/Tooling/Syntax/Nodes.h
+++ clang/include/clang/Tooling/Syntax/Nodes.h
@@ -206,7 +206,7 @@
   Expression(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
 return NodeKind::UnknownExpression <= N->getKind() &&
-   N->getKind() <= NodeKind::UnknownExpression;
+   N->getKind() <= NodeKind::CallExpression;
   }
 };
 


Index: clang/include/clang/Tooling/Syntax/Nodes.h
===
--- clang/include/clang/Tooling/Syntax/Nodes.h
+++ clang/include/clang/Tooling/Syntax/Nodes.h
@@ -206,7 +206,7 @@
   Expression(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
 return NodeKind::UnknownExpression <= N->getKind() &&
-   N->getKind() <= NodeKind::UnknownExpression;
+   N->getKind() <= NodeKind::CallExpression;
   }
 };
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D89146: [SyntaxTree] Fix rtti for `Expression`.

2020-10-13 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297842.
eduucaldas added a comment.

rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89146/new/

https://reviews.llvm.org/D89146

Files:
  clang/include/clang/Tooling/Syntax/Nodes.h


Index: clang/include/clang/Tooling/Syntax/Nodes.h
===
--- clang/include/clang/Tooling/Syntax/Nodes.h
+++ clang/include/clang/Tooling/Syntax/Nodes.h
@@ -206,7 +206,7 @@
   Expression(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
 return NodeKind::UnknownExpression <= N->getKind() &&
-   N->getKind() <= NodeKind::UnknownExpression;
+   N->getKind() <= NodeKind::CallExpression;
   }
 };
 


Index: clang/include/clang/Tooling/Syntax/Nodes.h
===
--- clang/include/clang/Tooling/Syntax/Nodes.h
+++ clang/include/clang/Tooling/Syntax/Nodes.h
@@ -206,7 +206,7 @@
   Expression(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
 return NodeKind::UnknownExpression <= N->getKind() &&
-   N->getKind() <= NodeKind::UnknownExpression;
+   N->getKind() <= NodeKind::CallExpression;
   }
 };
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D89314: [SyntaxTree] Bug fix in `MutationsImpl::addAfter`.

2020-10-13 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

- Add assertions to other `MutationsImpl` member functions
- `findPrevious` is a free function


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D89314

Files:
  clang/lib/Tooling/Syntax/Mutations.cpp


Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,19 @@
 
 using namespace clang;
 
+static syntax::Node *findPrevious(syntax::Node *N) {
+  assert(N);
+  assert(N->getParent());
+  if (N->getParent()->getFirstChild() == N)
+return nullptr;
+  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
+   C = C->getNextSibling()) {
+if (C->getNextSibling() == N)
+  return C;
+  }
+  llvm_unreachable("could not find a child node");
+}
+
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -30,14 +43,15 @@
   /// Add a new node with a specified role.
   static void addAfter(syntax::Node *Anchor, syntax::Node *New, NodeRole Role) 
{
 assert(Anchor != nullptr);
+assert(Anchor->Parent != nullptr);
 assert(New->Parent == nullptr);
 assert(New->NextSibling == nullptr);
-assert(!New->isDetached());
+assert(New->isDetached());
 assert(Role != NodeRole::Detached);
 
 New->setRole(Role);
 auto *P = Anchor->getParent();
-P->replaceChildRangeLowLevel(Anchor, Anchor, New);
+P->replaceChildRangeLowLevel(Anchor, Anchor->getNextSibling(), New);
 
 P->assertInvariants();
   }
@@ -60,6 +74,10 @@
 
   /// Completely remove the node from its parent.
   static void remove(syntax::Node *N) {
+assert(N != nullptr);
+assert(N->Parent != nullptr);
+assert(N->canModify());
+
 auto *P = N->getParent();
 P->replaceChildRangeLowLevel(findPrevious(N), N->getNextSibling(),
  /*New=*/nullptr);
@@ -67,18 +85,6 @@
 P->assertInvariants();
 N->assertInvariants();
   }
-
-private:
-  static syntax::Node *findPrevious(syntax::Node *N) {
-if (N->getParent()->getFirstChild() == N)
-  return nullptr;
-for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
- C = C->getNextSibling()) {
-  if (C->getNextSibling() == N)
-return C;
-}
-llvm_unreachable("could not find a child node");
-  }
 };
 
 void syntax::removeStatement(syntax::Arena , syntax::Statement *S) {


Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,19 @@
 
 using namespace clang;
 
+static syntax::Node *findPrevious(syntax::Node *N) {
+  assert(N);
+  assert(N->getParent());
+  if (N->getParent()->getFirstChild() == N)
+return nullptr;
+  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
+   C = C->getNextSibling()) {
+if (C->getNextSibling() == N)
+  return C;
+  }
+  llvm_unreachable("could not find a child node");
+}
+
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -30,14 +43,15 @@
   /// Add a new node with a specified role.
   static void addAfter(syntax::Node *Anchor, syntax::Node *New, NodeRole Role) {
 assert(Anchor != nullptr);
+assert(Anchor->Parent != nullptr);
 assert(New->Parent == nullptr);
 assert(New->NextSibling == nullptr);
-assert(!New->isDetached());
+assert(New->isDetached());
 assert(Role != NodeRole::Detached);
 
 New->setRole(Role);
 auto *P = Anchor->getParent();
-P->replaceChildRangeLowLevel(Anchor, Anchor, New);
+P->replaceChildRangeLowLevel(Anchor, Anchor->getNextSibling(), New);
 
 P->assertInvariants();
   }
@@ -60,6 +74,10 @@
 
   /// Completely remove the node from its parent.
   static void remove(syntax::Node *N) {
+assert(N != nullptr);
+assert(N->Parent != nullptr);
+assert(N->canModify());
+
 auto *P = N->getParent();
 P->replaceChildRangeLowLevel(findPrevious(N), N->getNextSibling(),
  /*New=*/nullptr);
@@ -67,18 +85,6 @@
 P->assertInvariants();
 N->assertInvariants();
   }
-
-private:
-  static syntax::Node *findPrevious(syntax::Node *N) {
-if (N->getParent()->getFirstChild() == N)
-  return nullptr;
-for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
- C = C->getNextSibling()) {
-  if (C->getNextSibling() == N)
-return C;
-}
-llvm_unreachable("could not find a child node");
-  }
 };
 
 void 

[PATCH] D89303: [SyntaxTree] Improve safety of `replaceChildRangeLowLevel`

2020-10-13 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a reviewer: gribozavr2.
eduucaldas added inline comments.



Comment at: clang/lib/Tooling/Syntax/Tree.cpp:103
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);

Throughout the function we use data members instead of accessors. Is one 
preferrable to the other?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89303/new/

https://reviews.llvm.org/D89303

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


[PATCH] D89303: [SyntaxTree] Improve safety of `replaceChildRangeLowLevel`

2020-10-13 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297803.
eduucaldas added a comment.

minor


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89303/new/

https://reviews.llvm.org/D89303

Files:
  clang/lib/Tooling/Syntax/Tree.cpp


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,67 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
+
+  if (!New && GetBegin() == End)
+return;
+
+  // Mark modification.
+  for (auto *T = this; T && T->Original; T = T->Parent)
+T->Original = false;
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
+  if (!New) {
+GetBegin() = End;
+return;
+  }
   // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
-
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  GetBegin() = New;
+  auto *Last = New;
+  for (auto *N = New; N != nullptr; N = N->NextSibling) {
+Last = N;
+N->Parent = this;
   }
-
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  Last->NextSibling = End;
 }
 
 namespace {


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,67 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
+
+  if (!New && GetBegin() == End)
+return;
+
+  // Mark modification.
+  for (auto *T = this; T && T->Original; T = T->Parent)
+T->Original = false;
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-

[PATCH] D89303: [SyntaxTree] Improve safety of `replaceChildRangeLowLevel`

2020-10-13 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297800.
eduucaldas added a comment.

minor


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89303/new/

https://reviews.llvm.org/D89303

Files:
  clang/lib/Tooling/Syntax/Tree.cpp


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,65 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
-  // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
+  // Mark modification.
+  if (New || GetBegin() != End)
+for (auto *T = this; T && T->Original; T = T->Parent)
+  T->Original = false;
 
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  if (!New) {
+GetBegin() = End;
+return;
   }
-
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  // Attach new nodes.
+  GetBegin() = New;
+  auto *Last = New;
+  for (auto *N = New; N != nullptr; N = N->NextSibling) {
+Last = N;
+N->Parent = this;
+  }
+  Last->NextSibling = End;
 }
 
 namespace {


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,65 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
-  // Attach 

[PATCH] D89303: [SyntaxTree] Improve safety of `replaceChildRangeLowLevel`

2020-10-13 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

- Add assertions for other preconditions.
- If nothing is modified, don't mark it.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D89303

Files:
  clang/lib/Tooling/Syntax/Tree.cpp


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,65 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
-  // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
+  // Mark modification.
+  if (New || GetBegin() != End)
+for (auto *T = this; T && T->Original; T = T->Parent)
+  T->Original = false;
 
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  // Attach new nodes.
+  if (!New) {
+GetBegin() = End;
+return;
   }
-
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  GetBegin() = New;
+  auto *Last = New;
+  for (auto *N = New; N != nullptr; N = N->NextSibling) {
+Last = N;
+N->Parent = this;
+  }
+  Last->NextSibling = End;
 }
 
 namespace {


Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -94,48 +94,65 @@
 
 void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
  Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+  assert((!BeforeBegin || BeforeBegin->Parent == this) &&
+ "`BeforeBegin` is not a child of `this`.");
+  assert((!End || End->Parent == this) && "`End` is not a child of `this`.");
+  assert(canModify() && "Cannot modify `this`.");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (auto *N = New; N; N = N->NextSibling) {
 assert(N->Parent == nullptr);
 assert(N->getRole() != NodeRole::Detached && "Roles must be set");
 // FIXME: sanity-check the role.
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable.");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`.");
 #endif
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
 
   // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-12 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297574.
eduucaldas marked 8 inline comments as done.
eduucaldas added a comment.

Answer comments, TODO: think about templated iterators


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -9,6 +9,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
 #include 
@@ -294,91 +295,110 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
+syntax::List::ElementAndDelimiter
+syntax::List::getWithDelimiter(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+return {Element, nullptr};
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return {Element, nullptr};
+  case syntax::NodeRole::ListDelimiter:
+return {Element, cast(Next)};
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterDelimiter(syntax::Leaf *Delimiter) {
+  assert(Delimiter && Delimiter->getRole() == syntax::NodeRole::ListDelimiter);
+  auto *List = cast(Delimiter->getParent());
+  auto *Next = Delimiter->getNextSibling();
+  if (!Next) {
+switch (List->getTerminationKind()) {
+// List is separated and ends with delimiter
+// => missing last ElementAndDelimiter.
+case syntax::List::TerminationKind::Separated:
+  return llvm::Optional>(
+  {nullptr, nullptr});
+case syntax::List::TerminationKind::Terminated:
+case syntax::List::TerminationKind::MaybeTerminated:
+  return None;
 }
-break;
   }
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // Consecutive Delimiters => missing Element
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(Next)});
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
-
-  return Children;
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
-std::vector syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-return {};
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterElement(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+// This was the last element of the list.
+return None;
+
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+// We have something of the form "a b ..." => 'b' starts the next
+// `ElementAndDelimiter`.
+return getWithDelimiter(Next);
+
+  case syntax::NodeRole::ListDelimiter:
+// We have something of the form "a , ..." => whatever comes after the comma
+// starts the next `ElementAndDelimiter`.
+return getElementAndDelimiterAfterDelimiter(cast(Next));
 
-  std::vector Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back(ElementWithoutDelimiter);
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case 

[PATCH] D89148: [SyntaxTree] Artificial use of the Mutations API.

2020-10-12 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a subscriber: gribozavr2.
eduucaldas added a comment.

This patch implements the use case we discussed. It is merely for illustration.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89148/new/

https://reviews.llvm.org/D89148

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


[PATCH] D89147: [SyntaxTree] Improve the signature of `replaceChildRangeLowLevel`.

2020-10-12 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297511.
eduucaldas added a comment.

Add asserts to `MutationsImpl::remove`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89147/new/

https://reviews.llvm.org/D89147

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -92,50 +92,84 @@
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
- Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+std::vector>
+syntax::Tree::replaceChildRangeLowLevel(
+Node *BeforeBegin, Node *End,
+ArrayRef> NewRange) {
+  assert(!BeforeBegin || BeforeBegin->Parent == this &&
+ "`BeforeBegin` is not a child of `this`");
+  assert(!End || End->Parent == this && "`End` is not a child of `this`");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (const auto  : NewRange) {
+auto *N = NodeAndRole.first;
 assert(N->Parent == nullptr);
-assert(N->getRole() != NodeRole::Detached && "Roles must be set");
-// FIXME: sanity-check the role.
+assert(N->getRole() == NodeRole::Detached && "Roles must not be set");
+
+auto Role = NodeAndRole.second;
+assert(Role != NodeRole::Detached);
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`");
 #endif
 
-  // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
+
+  // Extract old nodes.
+  std::vector> ExtractedChildren;
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
+ExtractedChildren.push_back({N, N->getRole()});
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
-  // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
+  // If any modification occurred mark them.
+  if (!ExtractedChildren.empty() || !NewRange.empty())
+for (auto *T = this; T && T->Original; T = T->Parent)
+  T->Original = false;
 
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  // Attach new children to the replaced range.
+
+  if (NewRange.empty()) {
+GetBegin() = End;
+return ExtractedChildren;
+  }
+  GetBegin() = NewRange.front().first;
+  NewRange.back().first->NextSibling = End;
+
+  for (const auto  : NewRange) {
+NodeAndRole.first->setRole(NodeAndRole.second);
+NodeAndRole.first->Parent = this;
+  }
+
+  for (const auto *It = NewRange.begin(); It != std::prev(NewRange.end());
+   ++It) {
+auto *Node = It->first;
+auto *Next = std::next(It)->first;
+Node->NextSibling = Next;
   }
 
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  return ExtractedChildren;
 }
 
 namespace {
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,17 @@
 
 using namespace clang;
 
+static syntax::Node *findPrevious(syntax::Node *N) {
+  if (N->getParent()->getFirstChild() == N)
+return nullptr;
+  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
+   C = C->getNextSibling()) {
+if (C->getNextSibling() == N)
+  return C;
+  }
+  llvm_unreachable("could not find a child node");
+}
+
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -30,14 +41,15 @@
   /// Add a new node with a specified role.
   static void addAfter(syntax::Node *Anchor, syntax::Node *New, NodeRole Role) {
 assert(Anchor != nullptr);
+assert(Anchor->Parent != nullptr);
 assert(New->Parent == nullptr);
 

[PATCH] D89146: [SyntaxTree] Fix rtti for `Expression`.

2020-10-12 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297502.
eduucaldas added a comment.

Rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89146/new/

https://reviews.llvm.org/D89146

Files:
  clang/include/clang/Tooling/Syntax/Nodes.h


Index: clang/include/clang/Tooling/Syntax/Nodes.h
===
--- clang/include/clang/Tooling/Syntax/Nodes.h
+++ clang/include/clang/Tooling/Syntax/Nodes.h
@@ -206,7 +206,7 @@
   Expression(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
 return NodeKind::UnknownExpression <= N->getKind() &&
-   N->getKind() <= NodeKind::UnknownExpression;
+   N->getKind() <= NodeKind::CallExpression;
   }
 };
 


Index: clang/include/clang/Tooling/Syntax/Nodes.h
===
--- clang/include/clang/Tooling/Syntax/Nodes.h
+++ clang/include/clang/Tooling/Syntax/Nodes.h
@@ -206,7 +206,7 @@
   Expression(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
 return NodeKind::UnknownExpression <= N->getKind() &&
-   N->getKind() <= NodeKind::UnknownExpression;
+   N->getKind() <= NodeKind::CallExpression;
   }
 };
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D89148: [SyntaxTree] Artificial use of the Mutations API.

2020-10-10 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297398.
eduucaldas added a comment.

Rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89148/new/

https://reviews.llvm.org/D89148

Files:
  clang/include/clang/Tooling/Syntax/Mutations.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/unittests/Tooling/Syntax/MutationsTest.cpp


Index: clang/unittests/Tooling/Syntax/MutationsTest.cpp
===
--- clang/unittests/Tooling/Syntax/MutationsTest.cpp
+++ clang/unittests/Tooling/Syntax/MutationsTest.cpp
@@ -52,6 +52,16 @@
 EXPECT_FALSE(S->isOriginal())
 << "node removed from tree cannot be marked as original";
   };
+
+  Transformation ParenthesizeRHS = [this](const llvm::Annotations ,
+  TranslationUnit *Root) {
+auto *BOE = cast(
+nodeByRange(Input.range(), Root));
+ASSERT_TRUE(BOE->canModify()) << "cannot remove a statement";
+syntax::parenthesizeRHS(*Arena, BOE);
+EXPECT_FALSE(BOE->isOriginal())
+<< "modified node cannot be marked as original";
+  };
 };
 
 INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, MutationTest,
@@ -71,4 +81,22 @@
   CheckTransformation(RemoveStatement, "void test() { if (1) [[{}]] else {} }",
   "void test() { if (1) ; else {} }");
 }
+
+TEST_P(MutationTest, Parenthesize_RHS) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(void test() { [[1 + 2]]; })cpp",
+  "void test() { 1 + (2); }");
+}
+
+TEST_P(MutationTest, Parenthesize_RHS_MACRO) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(
+#define THENUMBER 42
+void test() {
+  [[1 + (THENUMBER)]];
+})cpp",
+  R"cpp(
+#define THENUMBER 42
+void test() {
+  1 + ((THENUMBER));
+})cpp");
+}
 } // namespace
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -8,6 +8,7 @@
 #include "clang/Tooling/Syntax/Mutations.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
 #include "clang/Lex/Token.h"
 #include "clang/Tooling/Core/Replacement.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
@@ -96,3 +97,22 @@
 
   MutationsImpl::replace(S, createEmptyStatement(A));
 }
+
+void syntax::parenthesizeRHS(syntax::Arena ,
+ syntax::BinaryOperatorExpression *BOE) {
+  assert(BOE);
+  assert(BOE->canModify());
+
+  auto *RHS = BOE->getRhs();
+  auto *BeforeRHS = findPrevious(RHS);
+  MutationsImpl::remove(RHS);
+
+  auto *NewRHS =
+  createTree(A,
+ {{createLeaf(A, tok::l_paren), NodeRole::OpenParen},
+  {RHS, NodeRole::SubExpression},
+  {createLeaf(A, tok::r_paren), NodeRole::CloseParen}},
+ NodeKind::ParenExpression);
+
+  MutationsImpl::addAfter(BeforeRHS, NewRHS, NodeRole::RightHandSide);
+}
Index: clang/include/clang/Tooling/Syntax/Mutations.h
===
--- clang/include/clang/Tooling/Syntax/Mutations.h
+++ clang/include/clang/Tooling/Syntax/Mutations.h
@@ -31,6 +31,7 @@
 /// EXPECTS: S->canModify() == true
 void removeStatement(syntax::Arena , syntax::Statement *S);
 
+void parenthesizeRHS(syntax::Arena , syntax::BinaryOperatorExpression *BOE);
 } // namespace syntax
 } // namespace clang
 


Index: clang/unittests/Tooling/Syntax/MutationsTest.cpp
===
--- clang/unittests/Tooling/Syntax/MutationsTest.cpp
+++ clang/unittests/Tooling/Syntax/MutationsTest.cpp
@@ -52,6 +52,16 @@
 EXPECT_FALSE(S->isOriginal())
 << "node removed from tree cannot be marked as original";
   };
+
+  Transformation ParenthesizeRHS = [this](const llvm::Annotations ,
+  TranslationUnit *Root) {
+auto *BOE = cast(
+nodeByRange(Input.range(), Root));
+ASSERT_TRUE(BOE->canModify()) << "cannot remove a statement";
+syntax::parenthesizeRHS(*Arena, BOE);
+EXPECT_FALSE(BOE->isOriginal())
+<< "modified node cannot be marked as original";
+  };
 };
 
 INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, MutationTest,
@@ -71,4 +81,22 @@
   CheckTransformation(RemoveStatement, "void test() { if (1) [[{}]] else {} }",
   "void test() { if (1) ; else {} }");
 }
+
+TEST_P(MutationTest, Parenthesize_RHS) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(void test() { [[1 + 2]]; })cpp",
+  "void test() { 1 + (2); }");
+}
+
+TEST_P(MutationTest, Parenthesize_RHS_MACRO) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(
+#define THENUMBER 42
+void test() {
+  [[1 + (THENUMBER)]];
+})cpp",
+  R"cpp(
+#define THENUMBER 42
+void test() {
+  1 + ((THENUMBER));
+})cpp");
+}
 } // namespace
Index: 

[PATCH] D89147: [SyntaxTree] Improve the signature of `replaceChildRangeLowLevel`.

2020-10-10 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297396.
eduucaldas added a comment.

Fix whitespacing


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89147/new/

https://reviews.llvm.org/D89147

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -92,50 +92,84 @@
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
- Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+std::vector>
+syntax::Tree::replaceChildRangeLowLevel(
+Node *BeforeBegin, Node *End,
+ArrayRef> NewRange) {
+  assert(!BeforeBegin || BeforeBegin->Parent == this &&
+ "`BeforeBegin` is not a child of `this`");
+  assert(!End || End->Parent == this && "`End` is not a child of `this`");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (const auto  : NewRange) {
+auto *N = NodeAndRole.first;
 assert(N->Parent == nullptr);
-assert(N->getRole() != NodeRole::Detached && "Roles must be set");
-// FIXME: sanity-check the role.
+assert(N->getRole() == NodeRole::Detached && "Roles must not be set");
+
+auto Role = NodeAndRole.second;
+assert(Role != NodeRole::Detached);
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`");
 #endif
 
-  // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
+
+  // Extract old nodes.
+  std::vector> ExtractedChildren;
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
+ExtractedChildren.push_back({N, N->getRole()});
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
-  // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
+  // If any modification occurred mark them.
+  if (!ExtractedChildren.empty() || !NewRange.empty())
+for (auto *T = this; T && T->Original; T = T->Parent)
+  T->Original = false;
 
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  // Attach new children to the replaced range.
+
+  if (NewRange.empty()) {
+GetBegin() = End;
+return ExtractedChildren;
+  }
+  GetBegin() = NewRange.front().first;
+  NewRange.back().first->NextSibling = End;
+
+  for (const auto  : NewRange) {
+NodeAndRole.first->setRole(NodeAndRole.second);
+NodeAndRole.first->Parent = this;
+  }
+
+  for (const auto *It = NewRange.begin(); It != std::prev(NewRange.end());
+   ++It) {
+auto *Node = It->first;
+auto *Next = std::next(It)->first;
+Node->NextSibling = Next;
   }
 
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  return ExtractedChildren;
 }
 
 namespace {
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,17 @@
 
 using namespace clang;
 
+static syntax::Node *findPrevious(syntax::Node *N) {
+  if (N->getParent()->getFirstChild() == N)
+return nullptr;
+  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
+   C = C->getNextSibling()) {
+if (C->getNextSibling() == N)
+  return C;
+  }
+  llvm_unreachable("could not find a child node");
+}
+
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -30,14 +41,15 @@
   /// Add a new node with a specified role.
   static void addAfter(syntax::Node *Anchor, syntax::Node *New, NodeRole Role) {
 assert(Anchor != nullptr);
+assert(Anchor->Parent != nullptr);
 assert(New->Parent == nullptr);
 assert(New->NextSibling == 

[PATCH] D89147: [SyntaxTree] Improve the signature of `replaceChildRangeLowLevel`.

2020-10-10 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297395.
eduucaldas added a comment.

Add role sanity-check. Introduce `GetBegin()`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89147/new/

https://reviews.llvm.org/D89147

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -92,50 +92,84 @@
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
- Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+std::vector>
+syntax::Tree::replaceChildRangeLowLevel(
+Node *BeforeBegin, Node *End,
+ArrayRef> NewRange) {
+  assert(!BeforeBegin || BeforeBegin->Parent == this &&
+ "`BeforeBegin` is not a child of `this`");
+  assert(!End || End->Parent == this && "`End` is not a child of `this`");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (const auto  : NewRange) {
+auto *N = NodeAndRole.first;
 assert(N->Parent == nullptr);
-assert(N->getRole() != NodeRole::Detached && "Roles must be set");
-// FIXME: sanity-check the role.
+assert(N->getRole() == NodeRole::Detached && "Roles must not be set");
+
+auto Role = NodeAndRole.second;
+assert(Role != NodeRole::Detached);
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`");
+
 #endif
 
-  // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  auto GetBegin = [,  = this->FirstChild]() -> Node *& {
+return BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  };
+
+  // Extract old nodes.
+  std::vector> ExtractedChildren;
+  for (auto *N = GetBegin(); N != End;) {
 auto *Next = N->NextSibling;
+ExtractedChildren.push_back({N, N->getRole()});
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
-  // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
+  // If any modification occurred mark this and its ancestors as modified.
+  if (!ExtractedChildren.empty() || !NewRange.empty())
+for (auto *T = this; T && T->Original; T = T->Parent)
+  T->Original = false;
 
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  // Attach new children to the replaced range.
+  if (NewRange.empty()) {
+GetBegin() = End;
+return ExtractedChildren;
+  }
+  GetBegin() = NewRange.front().first;
+  NewRange.back().first->NextSibling = End;
+
+  for (const auto  : NewRange) {
+NodeAndRole.first->setRole(NodeAndRole.second);
+NodeAndRole.first->Parent = this;
+  }
+
+  for (const auto *It = NewRange.begin(); It != std::prev(NewRange.end());
+   ++It) {
+auto *Node = It->first;
+auto *Next = std::next(It)->first;
+Node->NextSibling = Next;
   }
 
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  return ExtractedChildren;
 }
 
 namespace {
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,17 @@
 
 using namespace clang;
 
+static syntax::Node *findPrevious(syntax::Node *N) {
+  if (N->getParent()->getFirstChild() == N)
+return nullptr;
+  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
+   C = C->getNextSibling()) {
+if (C->getNextSibling() == N)
+  return C;
+  }
+  llvm_unreachable("could not find a child node");
+}
+
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -30,14 +41,15 @@
   /// Add a new node with a specified role.
   static void addAfter(syntax::Node *Anchor, syntax::Node *New, NodeRole Role) {
 assert(Anchor != nullptr);
+assert(Anchor->Parent != nullptr);
 

[PATCH] D89147: [SyntaxTree] Improve the signature of `replaceChildRangeLowLevel`.

2020-10-10 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297389.
eduucaldas added a comment.

Add reachability assertions


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89147/new/

https://reviews.llvm.org/D89147

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -92,50 +92,87 @@
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
- Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+std::vector>
+syntax::Tree::replaceChildRangeLowLevel(
+Node *BeforeBegin, Node *End,
+ArrayRef> NewRange) {
+  assert(!BeforeBegin || BeforeBegin->Parent == this &&
+ "`BeforeBegin` is not a child of `this`");
+  assert(!End || End->Parent == this && "`End` is not a child of `this`");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (const auto  : NewRange) {
+auto *N = NodeAndRole.first;
 assert(N->Parent == nullptr);
-assert(N->getRole() != NodeRole::Detached && "Roles must be set");
-// FIXME: sanity-check the role.
+assert(N->getRole() == NodeRole::Detached && "Roles must not be set");
   }
+
+  auto Reachable = [](Node *From, Node *N) {
+if (!N)
+  return true;
+for (auto *It = From; It; It = It->NextSibling)
+  if (It == N)
+return true;
+return false;
+  };
+  assert(Reachable(FirstChild, BeforeBegin) &&
+ "`BeforeBegin` is not reachable");
+  assert(Reachable(BeforeBegin ? BeforeBegin->NextSibling : FirstChild, End) &&
+ "`End` is not after `BeforeBegin`");
+
 #endif
 
-  // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  // Extract children to be replaced.
+  std::vector> ExtractedChildren;
+  auto *Begin = BeforeBegin ? BeforeBegin->NextSibling : FirstChild;
+  for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
+ExtractedChildren.push_back({N, N->getRole()});
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
-  // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
+  // If any modification occurred mark this and its ancestors as modified.
+  if (!ExtractedChildren.empty() || !NewRange.empty())
+for (auto *T = this; T && T->Original; T = T->Parent)
+  T->Original = false;
 
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  if (NewRange.empty()) {
+BeforeBegin->NextSibling = End;
+return ExtractedChildren;
   }
 
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  // `NewRange` becomes children of this Tree.
+  for (const auto  : NewRange) {
+NodeAndRole.first->setRole(NodeAndRole.second);
+NodeAndRole.first->Parent = this;
+  }
+
+  // `NewRange` nodes are siblings.
+  for (const auto *It = NewRange.begin(); It != std::prev(NewRange.end());
+   ++It) {
+auto *Node = It->first;
+auto *Next = std::next(It)->first;
+Node->NextSibling = Next;
+  }
+  // Attach new children to the end of the replaced range.
+  NewRange.back().first->NextSibling = End;
+
+  // New children are now the beginning of the replaced range.
+  auto *NewBegin = NewRange.front().first;
+  if (BeforeBegin)
+BeforeBegin->NextSibling = NewBegin;
+  else
+FirstChild = NewBegin;
+
+  return ExtractedChildren;
 }
 
 namespace {
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,17 @@
 
 using namespace clang;
 
+static syntax::Node *findPrevious(syntax::Node *N) {
+  if (N->getParent()->getFirstChild() == N)
+return nullptr;
+  for (syntax::Node *C = N->getParent()->getFirstChild(); C != nullptr;
+   C = C->getNextSibling()) {
+if (C->getNextSibling() == N)
+  return C;
+  }
+  llvm_unreachable("could not find a child node");
+}
+
 // This class has access to the internals of tree nodes. Its sole purpose is to
 // define helpers that allow implementing the high-level mutation operations.
 class syntax::MutationsImpl {
@@ -30,14 +41,15 @@
   /// Add a new node with a specified role.
   static void addAfter(syntax::Node 

[PATCH] D89148: [SyntaxTree] Artificial use of the Mutations API.

2020-10-09 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Not intended for submission.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D89148

Files:
  clang/include/clang/Tooling/Syntax/Mutations.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/unittests/Tooling/Syntax/MutationsTest.cpp


Index: clang/unittests/Tooling/Syntax/MutationsTest.cpp
===
--- clang/unittests/Tooling/Syntax/MutationsTest.cpp
+++ clang/unittests/Tooling/Syntax/MutationsTest.cpp
@@ -52,6 +52,16 @@
 EXPECT_FALSE(S->isOriginal())
 << "node removed from tree cannot be marked as original";
   };
+
+  Transformation ParenthesizeRHS = [this](const llvm::Annotations ,
+  TranslationUnit *Root) {
+auto *BOE = cast(
+nodeByRange(Input.range(), Root));
+ASSERT_TRUE(BOE->canModify()) << "cannot remove a statement";
+syntax::parenthesizeRHS(*Arena, BOE);
+EXPECT_FALSE(BOE->isOriginal())
+<< "modified node cannot be marked as original";
+  };
 };
 
 INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, MutationTest,
@@ -71,4 +81,22 @@
   CheckTransformation(RemoveStatement, "void test() { if (1) [[{}]] else {} }",
   "void test() { if (1) ; else {} }");
 }
+
+TEST_P(MutationTest, Parenthesize_RHS) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(void test() { [[1 + 2]]; })cpp",
+  "void test() { 1 + (2); }");
+}
+
+TEST_P(MutationTest, Parenthesize_RHS_MACRO) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(
+#define THENUMBER 42
+void test() {
+  [[1 + (THENUMBER)]];
+})cpp",
+  R"cpp(
+#define THENUMBER 42
+void test() {
+  1 + ((THENUMBER));
+})cpp");
+}
 } // namespace
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -8,6 +8,7 @@
 #include "clang/Tooling/Syntax/Mutations.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
 #include "clang/Lex/Token.h"
 #include "clang/Tooling/Core/Replacement.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
@@ -96,3 +97,22 @@
 
   MutationsImpl::replace(S, createEmptyStatement(A));
 }
+
+void syntax::parenthesizeRHS(syntax::Arena ,
+ syntax::BinaryOperatorExpression *BOE) {
+  assert(BOE);
+  assert(BOE->canModify());
+
+  auto *RHS = BOE->getRhs();
+  auto *BeforeRHS = findPrevious(RHS);
+  MutationsImpl::remove(RHS);
+
+  auto *NewRHS =
+  createTree(A,
+ {{createLeaf(A, tok::l_paren), NodeRole::OpenParen},
+  {RHS, NodeRole::SubExpression},
+  {createLeaf(A, tok::r_paren), NodeRole::CloseParen}},
+ NodeKind::ParenExpression);
+
+  MutationsImpl::addAfter(BeforeRHS, NewRHS, NodeRole::RightHandSide);
+}
Index: clang/include/clang/Tooling/Syntax/Mutations.h
===
--- clang/include/clang/Tooling/Syntax/Mutations.h
+++ clang/include/clang/Tooling/Syntax/Mutations.h
@@ -31,6 +31,7 @@
 /// EXPECTS: S->canModify() == true
 void removeStatement(syntax::Arena , syntax::Statement *S);
 
+void parenthesizeRHS(syntax::Arena , syntax::BinaryOperatorExpression *BOE);
 } // namespace syntax
 } // namespace clang
 


Index: clang/unittests/Tooling/Syntax/MutationsTest.cpp
===
--- clang/unittests/Tooling/Syntax/MutationsTest.cpp
+++ clang/unittests/Tooling/Syntax/MutationsTest.cpp
@@ -52,6 +52,16 @@
 EXPECT_FALSE(S->isOriginal())
 << "node removed from tree cannot be marked as original";
   };
+
+  Transformation ParenthesizeRHS = [this](const llvm::Annotations ,
+  TranslationUnit *Root) {
+auto *BOE = cast(
+nodeByRange(Input.range(), Root));
+ASSERT_TRUE(BOE->canModify()) << "cannot remove a statement";
+syntax::parenthesizeRHS(*Arena, BOE);
+EXPECT_FALSE(BOE->isOriginal())
+<< "modified node cannot be marked as original";
+  };
 };
 
 INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, MutationTest,
@@ -71,4 +81,22 @@
   CheckTransformation(RemoveStatement, "void test() { if (1) [[{}]] else {} }",
   "void test() { if (1) ; else {} }");
 }
+
+TEST_P(MutationTest, Parenthesize_RHS) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(void test() { [[1 + 2]]; })cpp",
+  "void test() { 1 + (2); }");
+}
+
+TEST_P(MutationTest, Parenthesize_RHS_MACRO) {
+  CheckTransformation(ParenthesizeRHS, R"cpp(
+#define THENUMBER 42
+void test() {
+  [[1 + (THENUMBER)]];
+})cpp",
+  R"cpp(
+#define THENUMBER 42
+void test() {
+  1 + ((THENUMBER));
+})cpp");
+}
 } // namespace

[PATCH] D89147: [SyntaxTree] Improve the signature of `replaceChildRangeLowLevel`.

2020-10-09 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Previously `replaceChildRangeLowLevel` took the new child range as a
`Node* New`.  `New` was expected to have siblings attached already, and
thus it was interpreted as a range. Additionally, the role of `New` and
its siblings were expected to be set prior to calling the function.  As
a result the `New` argument broke the invariant `New->Parent == nullptr`
<=> `New->Role == Detached`, at call site.  We change the signature of
`replaceChildRangeLowLevel` to take instead an
`ArrayRef>`, and thus move the burden of
setting siblings and roles from the user to the member function.

Moreover, `replaceChildRangeLowLevel` returns now a vector of the
replaced range, following the "law of useful returns". Previously, in
order to reuse the replaced elements the user needed to get pointers to
those elements and before calling the function.

We also fixed some minor bugs in `addAfter`, and added more asserts to
the new `replaceChildRangeLowLevel`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D89147

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Mutations.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -92,50 +92,76 @@
   this->FirstChild = Child;
 }
 
-void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
- Node *New) {
-  assert(!BeforeBegin || BeforeBegin->Parent == this);
+std::vector>
+syntax::Tree::replaceChildRangeLowLevel(
+Node *BeforeBegin, Node *End,
+ArrayRef> NewRange) {
+  assert(!BeforeBegin || BeforeBegin->Parent == this &&
+ "`BeforeBegin` is not a child of `this`");
+  assert(!End || End->Parent == this && "`End` is not a child of `this`");
 
 #ifndef NDEBUG
-  for (auto *N = New; N; N = N->getNextSibling()) {
+  for (const auto  : NewRange) {
+auto *N = NodeAndRole.first;
 assert(N->Parent == nullptr);
-assert(N->getRole() != NodeRole::Detached && "Roles must be set");
-// FIXME: sanity-check the role.
+assert(N->getRole() == NodeRole::Detached && "Roles must not be set");
   }
+  // TODO: assert that both `BeforeBegin` and `End` are children of parent, and
+  // that `End` is "after" `BeforeBegin`.
+
 #endif
 
-  // Detach old nodes.
-  for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->getNextSibling();
-   N != End;) {
+  // Extract children to be replaced.
+  std::vector> ExtractedChildren;
+  auto *Begin = BeforeBegin ? BeforeBegin->getNextSibling() : FirstChild;
+  for (auto *N = Begin; N != End;) {
 auto *Next = N->NextSibling;
+ExtractedChildren.push_back({N, N->getRole()});
 
 N->setRole(NodeRole::Detached);
 N->Parent = nullptr;
 N->NextSibling = nullptr;
+
 if (N->Original)
-  traverse(N, [&](Node *C) { C->Original = false; });
+  traverse(N, [](Node *C) { C->Original = false; });
 
 N = Next;
   }
 
-  // Attach new nodes.
-  if (BeforeBegin)
-BeforeBegin->NextSibling = New ? New : End;
-  else
-FirstChild = New ? New : End;
+  // If any modification occurred mark this and its ancestors as modified.
+  if (!ExtractedChildren.empty() || !NewRange.empty())
+for (auto *T = this; T && T->Original; T = T->Parent)
+  T->Original = false;
 
-  if (New) {
-auto *Last = New;
-for (auto *N = New; N != nullptr; N = N->getNextSibling()) {
-  Last = N;
-  N->Parent = this;
-}
-Last->NextSibling = End;
+  if (NewRange.empty()) {
+BeforeBegin->NextSibling = End;
+return ExtractedChildren;
+  }
+
+  // `NewRange` becomes children of this Tree.
+  for (const auto  : NewRange) {
+NodeAndRole.first->setRole(NodeAndRole.second);
+NodeAndRole.first->Parent = this;
+  }
+
+  // `NewRange` nodes are siblings.
+  for (const auto *It = NewRange.begin(); It != std::prev(NewRange.end());
+   ++It) {
+auto *Node = It->first;
+auto *Next = std::next(It)->first;
+Node->NextSibling = Next;
   }
+  // Attach new children to the end of the replaced range.
+  NewRange.back().first->NextSibling = End;
+
+  // New children are now the beginning of the replaced range.
+  auto *NewBegin = NewRange.front().first;
+  if (BeforeBegin)
+BeforeBegin->NextSibling = NewBegin;
+  else
+FirstChild = NewBegin;
 
-  // Mark the node as modified.
-  for (auto *T = this; T && T->Original; T = T->Parent)
-T->Original = false;
+  return ExtractedChildren;
 }
 
 namespace {
Index: clang/lib/Tooling/Syntax/Mutations.cpp
===
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -23,6 +23,17 @@
 
 using namespace clang;
 

[PATCH] D89146: [SyntaxTree] Fix rtti for `Expression`.

2020-10-09 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
eduucaldas added a reviewer: gribozavr2.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D89146

Files:
  clang/include/clang/Tooling/Syntax/Nodes.h


Index: clang/include/clang/Tooling/Syntax/Nodes.h
===
--- clang/include/clang/Tooling/Syntax/Nodes.h
+++ clang/include/clang/Tooling/Syntax/Nodes.h
@@ -206,7 +206,7 @@
   Expression(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
 return NodeKind::UnknownExpression <= N->getKind() &&
-   N->getKind() <= NodeKind::UnknownExpression;
+   N->getKind() <= NodeKind::CallExpression;
   }
 };
 


Index: clang/include/clang/Tooling/Syntax/Nodes.h
===
--- clang/include/clang/Tooling/Syntax/Nodes.h
+++ clang/include/clang/Tooling/Syntax/Nodes.h
@@ -206,7 +206,7 @@
   Expression(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
 return NodeKind::UnknownExpression <= N->getKind() &&
-   N->getKind() <= NodeKind::UnknownExpression;
+   N->getKind() <= NodeKind::CallExpression;
   }
 };
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-08 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a reviewer: gribozavr2.
eduucaldas added a comment.

This is quite low priority, compared to the other branches of work, but it was 
almost ready work, so I decided to polish it and send it to review now.




Comment at: clang/include/clang/Tooling/Syntax/Tree.h:238-273
+  class ElementAndDelimiterIterator
+  : public llvm::iterator_facade_base> {
+  public:
+ElementAndDelimiterIterator(llvm::Optional> ED)
+: EDI(ED) {}

Should we hide some of this? Most of the member functions are a couple of 
lines, so I decided not to. What is your opinion?



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:275-276
+
+  ElementAndDelimiterIterator getBegin();
+  ElementAndDelimiterIterator getEnd();
+

I chose to leave it as `getBegin` and `getEnd` instead  of 
`getElementsAndDelimitersBegin`. I did this because the return type of those 
methods already tells us that we're getting an `ElementAndDelimiterIterator`. 

What do you think?



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:312-319
+
+private:
+  // Auxiliary methods for implementing `ElementAndDelimiterIterator`.
+  static ElementAndDelimiter getWithDelimiter(Node *Element);
+  static llvm::Optional>
+  getElementAndDelimiterAfterDelimiter(Leaf *Delimiter);
+  static llvm::Optional>

These are all private static methods, should we hide them from the header?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

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


[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-08 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297006.
eduucaldas added a comment.

Replace `auto .. = std::vector();` with `std::vector ..;`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -9,6 +9,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
 #include 
@@ -294,89 +295,111 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
+syntax::List::ElementAndDelimiter
+syntax::List::getWithDelimiter(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+return {Element, nullptr};
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return {Element, nullptr};
+  case syntax::NodeRole::ListDelimiter:
+return {Element, cast(Next)};
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterDelimiter(syntax::Leaf *Delimiter) {
+  assert(Delimiter && Delimiter->getRole() == syntax::NodeRole::ListDelimiter);
+  auto *List = cast(Delimiter->getParent());
+  auto *Next = Delimiter->getNextSibling();
+  if (!Next) {
+switch (List->getTerminationKind()) {
+// List is separated and ends with delimiter
+// => missing last ElementAndDelimiter.
+case syntax::List::TerminationKind::Separated:
+  return llvm::Optional>(
+  {nullptr, nullptr});
+case syntax::List::TerminationKind::Terminated:
+case syntax::List::TerminationKind::MaybeTerminated:
+  return None;
 }
-break;
   }
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // Consecutive Delimiters => missing Element
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(Next)});
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
-
-  return Children;
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
-std::vector syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-return {};
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterElement(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  // a  b, x => End of list, this was the last ElementAndDelimiter.
+  if (!Next)
+return None;
 
-  std::vector Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back(ElementWithoutDelimiter);
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back(ElementWithoutDelimiter);
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable("A list has only elements or delimiters.");
-}
+  switch (Next->getRole()) {
+  // x  b, c => next ElementAndDelimiter starts with 'b'.
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // a  x, c => next ElementAndDelimiter starts after ','.

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-08 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 297001.
eduucaldas added a comment.

Reorganize methods to minimize diffs


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -9,6 +9,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
 #include 
@@ -294,89 +295,111 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
+syntax::List::ElementAndDelimiter
+syntax::List::getWithDelimiter(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+return {Element, nullptr};
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return {Element, nullptr};
+  case syntax::NodeRole::ListDelimiter:
+return {Element, cast(Next)};
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterDelimiter(syntax::Leaf *Delimiter) {
+  assert(Delimiter && Delimiter->getRole() == syntax::NodeRole::ListDelimiter);
+  auto *List = cast(Delimiter->getParent());
+  auto *Next = Delimiter->getNextSibling();
+  if (!Next) {
+switch (List->getTerminationKind()) {
+// List is separated and ends with delimiter
+// => missing last ElementAndDelimiter.
+case syntax::List::TerminationKind::Separated:
+  return llvm::Optional>(
+  {nullptr, nullptr});
+case syntax::List::TerminationKind::Terminated:
+case syntax::List::TerminationKind::MaybeTerminated:
+  return None;
 }
-break;
   }
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // Consecutive Delimiters => missing Element
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(Next)});
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
-
-  return Children;
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
-std::vector syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-return {};
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterElement(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  // a  b, x => End of list, this was the last ElementAndDelimiter.
+  if (!Next)
+return None;
 
-  std::vector Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back(ElementWithoutDelimiter);
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back(ElementWithoutDelimiter);
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable("A list has only elements or delimiters.");
-}
+  switch (Next->getRole()) {
+  // x  b, c => next ElementAndDelimiter starts with 'b'.
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // a  x, c => next ElementAndDelimiter starts after ','.
+  case 

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-08 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 296996.
eduucaldas added a comment.

Better comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -9,6 +9,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
 #include 
@@ -294,89 +295,111 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
-
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
-}
+syntax::List::ElementAndDelimiter
+syntax::List::getWithDelimiter(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+return {Element, nullptr};
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return {Element, nullptr};
+  case syntax::NodeRole::ListDelimiter:
+return {Element, cast(Next)};
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
+}
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
-  }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterDelimiter(syntax::Leaf *Delimiter) {
+  assert(Delimiter && Delimiter->getRole() == syntax::NodeRole::ListDelimiter);
+  auto *List = cast(Delimiter->getParent());
+  auto *Next = Delimiter->getNextSibling();
+  if (!Next) {
+switch (List->getTerminationKind()) {
+// List is separated and ends with delimiter
+// => missing last ElementAndDelimiter.
+case syntax::List::TerminationKind::Separated:
+  return llvm::Optional>(
+  {nullptr, nullptr});
+case syntax::List::TerminationKind::Terminated:
+case syntax::List::TerminationKind::MaybeTerminated:
+  return None;
 }
-break;
   }
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // Consecutive Delimiters => missing Element
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(Next)});
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
-
-  return Children;
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
-std::vector syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-return {};
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterElement(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  // a  b, x => End of list, this was the last ElementAndDelimiter.
+  if (!Next)
+return None;
 
-  std::vector Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back(ElementWithoutDelimiter);
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back(ElementWithoutDelimiter);
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable("A list has only elements or delimiters.");
-}
+  switch (Next->getRole()) {
+  // x  b, c => next ElementAndDelimiter starts with 'b'.
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // a  x, c => next ElementAndDelimiter starts after ','.
+  case syntax::NodeRole::ListDelimiter:

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-10-08 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 296994.
eduucaldas added a comment.

- [SyntaxTree] Provide iterator for `List` that iterates through 
`ElementAndDelimiter`s even for not well-defined lists.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88106/new/

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -9,6 +9,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
 #include 
@@ -294,45 +295,102 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
+syntax::List::ElementAndDelimiter
+syntax::List::getWithDelimiter(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+return {Element, nullptr};
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return {Element, nullptr};
+  case syntax::NodeRole::ListDelimiter:
+return {Element, cast(Next)};
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
+  }
+}
 
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterDelimiter(syntax::Leaf *Delimiter) {
+  assert(Delimiter && Delimiter->getRole() == syntax::NodeRole::ListDelimiter);
+  auto *List = cast(Delimiter->getParent());
+  auto *Next = Delimiter->getNextSibling();
+  if (!Next) {
+switch (List->getTerminationKind()) {
+// List is separated and ends with delimiter
+// => missing last ElementAndDelimiter.
+case syntax::List::TerminationKind::Separated:
+  return llvm::Optional>(
+  {nullptr, nullptr});
+case syntax::List::TerminationKind::Terminated:
+case syntax::List::TerminationKind::MaybeTerminated:
+  return None;
 }
   }
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // Consecutive Delimiters => missing Element
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(Next)});
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
+  }
+}
+
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterElement(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  // a  b, x => End of list, this was the last ElementAndDelimiter.
+  if (!Next)
+return None;
+
+  switch (Next->getRole()) {
+  // x  b, c => next ElementAndDelimiter starts with 'b'.
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // a  x, c => next ElementAndDelimiter starts after ','.
+  case syntax::NodeRole::ListDelimiter:
+return getElementAndDelimiterAfterDelimiter(cast(Next));
 
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
-}
-break;
+}
+
+syntax::List::ElementAndDelimiterIterator syntax::List::getEnd() {
+  return llvm::Optional>(None);
+}
+
+syntax::List::ElementAndDelimiterIterator syntax::List::getBegin() {
+  auto *First = getFirstChild();
+  if (!First)
+return getEnd();
+  switch (First->getRole()) {
+  case syntax::NodeRole::ListElement:
+return llvm::Optional>(getWithDelimiter(First));
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(First)});
+  default:
+llvm_unreachable(
+"A list can have only elements and 

[PATCH] D88403: Migrate Declarators to use the List API

2020-10-01 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5011d43108d1: Migrate Declarators to use the List API 
(authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88403/new/

https://reviews.llvm.org/D88403

Files:
  clang/include/clang/Tooling/Syntax/Nodes.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -188,8 +188,9 @@
 TranslationUnit Detached synthesized
 `-SimpleDeclaration synthesized
   |-'int' synthesized
-  |-SimpleDeclarator Declarator synthesized
-  | `-'a' synthesized
+  |-DeclaratorList Declarators synthesized
+  | `-SimpleDeclarator ListElement synthesized
+  |   `-'a' synthesized
   `-';' synthesized
   )txt"));
 }
@@ -201,8 +202,9 @@
   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
 SimpleDeclaration Detached synthesized
 |-'int' synthesized
-|-SimpleDeclarator Declarator synthesized
-| `-'a' synthesized
+|-DeclaratorList Declarators synthesized
+| `-SimpleDeclarator ListElement synthesized
+|   `-'a' synthesized
 `-';' synthesized
   )txt"));
 }
@@ -225,11 +227,12 @@
 TranslationUnit Detached synthesized
 `-SimpleDeclaration synthesized
   |-'void' synthesized
-  |-SimpleDeclarator Declarator synthesized
-  | |-'test' synthesized
-  | `-ParametersAndQualifiers synthesized
-  |   |-'(' OpenParen synthesized
-  |   `-')' CloseParen synthesized
+  |-DeclaratorList Declarators synthesized
+  | `-SimpleDeclarator ListElement synthesized
+  |   |-'test' synthesized
+  |   `-ParametersAndQualifiers synthesized
+  | |-'(' OpenParen synthesized
+  | `-')' CloseParen synthesized
   `-CompoundStatement synthesized
 |-'{' OpenParen synthesized
 |-IfStatement Statement synthesized
Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -92,21 +92,23 @@
 TranslationUnit Detached
 |-SimpleDeclaration
 | |-'int'
-| |-SimpleDeclarator Declarator
-| | |-'main'
-| | `-ParametersAndQualifiers
-| |   |-'(' OpenParen
-| |   `-')' CloseParen
+| |-DeclaratorList Declarators
+| | `-SimpleDeclarator ListElement
+| |   |-'main'
+| |   `-ParametersAndQualifiers
+| | |-'(' OpenParen
+| | `-')' CloseParen
 | `-CompoundStatement
 |   |-'{' OpenParen
 |   `-'}' CloseParen
 `-SimpleDeclaration
   |-'void'
-  |-SimpleDeclarator Declarator
-  | |-'foo'
-  | `-ParametersAndQualifiers
-  |   |-'(' OpenParen
-  |   `-')' CloseParen
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  |   |-'foo'
+  |   `-ParametersAndQualifiers
+  | |-'(' OpenParen
+  | `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
 `-'}' CloseParen
@@ -123,16 +125,18 @@
 TranslationUnit Detached
 |-SimpleDeclaration
 | |-'int'
-| |-SimpleDeclarator Declarator
-| | `-'a'
+| |-DeclaratorList Declarators
+| | `-SimpleDeclarator ListElement
+| |   `-'a'
 | `-';'
 `-SimpleDeclaration
   |-'int'
-  |-SimpleDeclarator Declarator
-  | |-'b'
-  | |-'='
-  | `-IntegerLiteralExpression
-  |   `-'42' LiteralToken
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  |   |-'b'
+  |   |-'='
+  |   `-IntegerLiteralExpression
+  | `-'42' LiteralToken
   `-';'
 )txt"));
 }
@@ -146,21 +150,24 @@
 TranslationUnit Detached
 `-SimpleDeclaration
   |-'void'
-  |-SimpleDeclarator Declarator
-  | |-'foo'
-  | `-ParametersAndQualifiers
-  |   |-'(' OpenParen
-  |   |-ParameterDeclarationList Parameters
-  |   | |-SimpleDeclaration ListElement
-  |   | | |-'int'
-  |   | | `-SimpleDeclarator Declarator
-  |   | |   `-'a'
-  |   | |-',' ListDelimiter
-  |   | `-SimpleDeclaration ListElement
-  |   |   |-'int'
-  |   |   `-SimpleDeclarator Declarator
-  |   | `-'b'
-  |   `-')' CloseParen
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  |   |-'foo'
+  |   `-ParametersAndQualifiers
+  | |-'(' OpenParen
+  | |-ParameterDeclarationList Parameters
+  | | |-SimpleDeclaration ListElement
+  | | | |-'int'
+  | | | `-DeclaratorList Declarators
+  | | |   `-SimpleDeclarator ListElement
+  | | | `-'a'
+  | | |-',' ListDelimiter
+  | | `-SimpleDeclaration ListElement
+  | |   |-'int'
+  | |   `-DeclaratorList Declarators
+  | | `-SimpleDeclarator ListElement
+  | |   `-'b'
+  | `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
 `-'}' CloseParen
@@ -178,8 +185,9 @@
 `-SimpleDeclaration
   |-'in\
 t'
-  |-SimpleDeclarator Declarator

[PATCH] D88403: Migrate Declarators to use the List API

2020-09-29 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 295053.
eduucaldas added a comment.

Update tests


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88403/new/

https://reviews.llvm.org/D88403

Files:
  clang/include/clang/Tooling/Syntax/Nodes.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -188,8 +188,9 @@
 TranslationUnit Detached synthesized
 `-SimpleDeclaration synthesized
   |-'int' synthesized
-  |-SimpleDeclarator Declarator synthesized
-  | `-'a' synthesized
+  |-DeclaratorList Declarators synthesized
+  | `-SimpleDeclarator ListElement synthesized
+  |   `-'a' synthesized
   `-';' synthesized
   )txt"));
 }
@@ -201,8 +202,9 @@
   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
 SimpleDeclaration Detached synthesized
 |-'int' synthesized
-|-SimpleDeclarator Declarator synthesized
-| `-'a' synthesized
+|-DeclaratorList Declarators synthesized
+| `-SimpleDeclarator ListElement synthesized
+|   `-'a' synthesized
 `-';' synthesized
   )txt"));
 }
@@ -225,11 +227,12 @@
 TranslationUnit Detached synthesized
 `-SimpleDeclaration synthesized
   |-'void' synthesized
-  |-SimpleDeclarator Declarator synthesized
-  | |-'test' synthesized
-  | `-ParametersAndQualifiers synthesized
-  |   |-'(' OpenParen synthesized
-  |   `-')' CloseParen synthesized
+  |-DeclaratorList Declarators synthesized
+  | `-SimpleDeclarator ListElement synthesized
+  |   |-'test' synthesized
+  |   `-ParametersAndQualifiers synthesized
+  | |-'(' OpenParen synthesized
+  | `-')' CloseParen synthesized
   `-CompoundStatement synthesized
 |-'{' OpenParen synthesized
 |-IfStatement Statement synthesized
Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -92,21 +92,23 @@
 TranslationUnit Detached
 |-SimpleDeclaration
 | |-'int'
-| |-SimpleDeclarator Declarator
-| | |-'main'
-| | `-ParametersAndQualifiers
-| |   |-'(' OpenParen
-| |   `-')' CloseParen
+| |-DeclaratorList Declarators
+| | `-SimpleDeclarator ListElement
+| |   |-'main'
+| |   `-ParametersAndQualifiers
+| | |-'(' OpenParen
+| | `-')' CloseParen
 | `-CompoundStatement
 |   |-'{' OpenParen
 |   `-'}' CloseParen
 `-SimpleDeclaration
   |-'void'
-  |-SimpleDeclarator Declarator
-  | |-'foo'
-  | `-ParametersAndQualifiers
-  |   |-'(' OpenParen
-  |   `-')' CloseParen
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  |   |-'foo'
+  |   `-ParametersAndQualifiers
+  | |-'(' OpenParen
+  | `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
 `-'}' CloseParen
@@ -123,16 +125,18 @@
 TranslationUnit Detached
 |-SimpleDeclaration
 | |-'int'
-| |-SimpleDeclarator Declarator
-| | `-'a'
+| |-DeclaratorList Declarators
+| | `-SimpleDeclarator ListElement
+| |   `-'a'
 | `-';'
 `-SimpleDeclaration
   |-'int'
-  |-SimpleDeclarator Declarator
-  | |-'b'
-  | |-'='
-  | `-IntegerLiteralExpression
-  |   `-'42' LiteralToken
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  |   |-'b'
+  |   |-'='
+  |   `-IntegerLiteralExpression
+  | `-'42' LiteralToken
   `-';'
 )txt"));
 }
@@ -146,21 +150,24 @@
 TranslationUnit Detached
 `-SimpleDeclaration
   |-'void'
-  |-SimpleDeclarator Declarator
-  | |-'foo'
-  | `-ParametersAndQualifiers
-  |   |-'(' OpenParen
-  |   |-ParameterDeclarationList Parameters
-  |   | |-SimpleDeclaration ListElement
-  |   | | |-'int'
-  |   | | `-SimpleDeclarator Declarator
-  |   | |   `-'a'
-  |   | |-',' ListDelimiter
-  |   | `-SimpleDeclaration ListElement
-  |   |   |-'int'
-  |   |   `-SimpleDeclarator Declarator
-  |   | `-'b'
-  |   `-')' CloseParen
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  |   |-'foo'
+  |   `-ParametersAndQualifiers
+  | |-'(' OpenParen
+  | |-ParameterDeclarationList Parameters
+  | | |-SimpleDeclaration ListElement
+  | | | |-'int'
+  | | | `-DeclaratorList Declarators
+  | | |   `-SimpleDeclarator ListElement
+  | | | `-'a'
+  | | |-',' ListDelimiter
+  | | `-SimpleDeclaration ListElement
+  | |   |-'int'
+  | |   `-DeclaratorList Declarators
+  | | `-SimpleDeclarator ListElement
+  | |   `-'b'
+  | `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
 `-'}' CloseParen
@@ -178,8 +185,9 @@
 `-SimpleDeclaration
   |-'in\
 t'
-  |-SimpleDeclarator Declarator
-  | `-'a'
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  

[PATCH] D88403: Migrate Declarators to use the List API

2020-09-29 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 294905.
eduucaldas marked an inline comment as done.
eduucaldas added a comment.

Improve comment


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88403/new/

https://reviews.llvm.org/D88403

Files:
  clang/include/clang/Tooling/Syntax/Nodes.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp

Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -2875,21 +2875,23 @@
 TranslationUnit Detached
 |-SimpleDeclaration
 | |-'int'
-| |-SimpleDeclarator Declarator
-| | |-'*'
-| | `-'a'
-| |-','
-| |-SimpleDeclarator Declarator
-| | `-'b'
+| |-DeclaratorList Declarators
+| | |-SimpleDeclarator ListElement
+| | | |-'*'
+| | | `-'a'
+| | |-',' ListDelimiter
+| | `-SimpleDeclarator ListElement
+| |   `-'b'
 | `-';'
 `-SimpleDeclaration
   |-'int'
-  |-SimpleDeclarator Declarator
-  | |-'*'
-  | `-'c'
-  |-','
-  |-SimpleDeclarator Declarator
-  | `-'d'
+  |-DeclaratorList Declarators
+  | |-SimpleDeclarator ListElement
+  | | |-'*'
+  | | `-'c'
+  | |-',' ListDelimiter
+  | `-SimpleDeclarator ListElement
+  |   `-'d'
   `-';'
 )txt"));
 }
@@ -2904,12 +2906,13 @@
 `-SimpleDeclaration
   |-'typedef'
   |-'int'
-  |-SimpleDeclarator Declarator
-  | |-'*'
-  | `-'a'
-  |-','
-  |-SimpleDeclarator Declarator
-  | `-'b'
+  |-DeclaratorList Declarators
+  | |-SimpleDeclarator ListElement
+  | | |-'*'
+  | | `-'a'
+  | |-',' ListDelimiter
+  | `-SimpleDeclarator ListElement
+  |   `-'b'
   `-';'
 )txt"));
 }
@@ -2926,33 +2929,36 @@
 TranslationUnit Detached
 `-SimpleDeclaration
   |-'void'
-  |-SimpleDeclarator Declarator
-  | |-'foo'
-  | `-ParametersAndQualifiers
-  |   |-'(' OpenParen
-  |   `-')' CloseParen
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  |   |-'foo'
+  |   `-ParametersAndQualifiers
+  | |-'(' OpenParen
+  | `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
 |-DeclarationStatement Statement
 | |-SimpleDeclaration
 | | |-'int'
-| | |-SimpleDeclarator Declarator
-| | | |-'*'
-| | | `-'a'
-| | |-','
-| | `-SimpleDeclarator Declarator
-| |   `-'b'
+| | `-DeclaratorList Declarators
+| |   |-SimpleDeclarator ListElement
+| |   | |-'*'
+| |   | `-'a'
+| |   |-',' ListDelimiter
+| |   `-SimpleDeclarator ListElement
+| | `-'b'
 | `-';'
 |-DeclarationStatement Statement
 | |-SimpleDeclaration
 | | |-'typedef'
 | | |-'int'
-| | |-SimpleDeclarator Declarator
-| | | |-'*'
-| | | `-'ta'
-| | |-','
-| | `-SimpleDeclarator Declarator
-| |   `-'tb'
+| | `-DeclaratorList Declarators
+| |   |-SimpleDeclarator ListElement
+| |   | |-'*'
+| |   | `-'ta'
+| |   |-',' ListDelimiter
+| |   `-SimpleDeclarator ListElement
+| | `-'tb'
 | `-';'
 `-'}' CloseParen
 )txt"));
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -183,6 +183,8 @@
 return new (A.getAllocator()) syntax::CallArguments;
   case syntax::NodeKind::ParameterDeclarationList:
 return new (A.getAllocator()) syntax::ParameterDeclarationList;
+  case syntax::NodeKind::DeclaratorList:
+return new (A.getAllocator()) syntax::DeclaratorList;
   }
   llvm_unreachable("unknown node kind");
 }
Index: clang/lib/Tooling/Syntax/Nodes.cpp
===
--- clang/lib/Tooling/Syntax/Nodes.cpp
+++ clang/lib/Tooling/Syntax/Nodes.cpp
@@ -136,6 +136,8 @@
 return OS << "CallArguments";
   case NodeKind::ParameterDeclarationList:
 return OS << "ParameterDeclarationList";
+  case NodeKind::DeclaratorList:
+return OS << "DeclaratorList";
   }
   llvm_unreachable("unknown node kind");
 }
@@ -218,6 +220,8 @@
 return OS << "Callee";
   case syntax::NodeRole::Arguments:
 return OS << "Arguments";
+  case syntax::NodeRole::Declarators:
+return OS << "Declarators";
   }
   llvm_unreachable("invalid role");
 }
@@ -291,6 +295,29 @@
   return Children;
 }
 
+std::vector
+syntax::DeclaratorList::getDeclarators() {
+  auto DeclaratorsAsNodes = getElementsAsNodes();
+  std::vector Children;
+  for (const auto  : DeclaratorsAsNodes) {
+Children.push_back(llvm::cast(DeclaratorAsNode));
+  }
+  return Children;
+}
+
+std::vector>
+syntax::DeclaratorList::getDeclaratorsAndCommas() {
+  auto DeclaratorsAsNodesAndCommas = getElementsAsNodesAndDelimiters();
+  std::vector>
+  Children;
+  for (const auto  : DeclaratorsAsNodesAndCommas) {
+Children.push_back(
+

[PATCH] D88403: Migrate Declarators to use the List API

2020-09-29 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas marked 3 inline comments as done.
eduucaldas added inline comments.



Comment at: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp:2932
   |-'void'
-  |-SimpleDeclarator Declarator
-  | |-'foo'
-  | `-ParametersAndQualifiers
-  |   |-'(' OpenParen
-  |   `-')' CloseParen
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement

gribozavr2 wrote:
> eduucaldas wrote:
> > This sounds weird and is probably the reason why SO many tests failed.
> > 
> > According to the [[ 
> > https://eel.is/c++draft/dcl.fct.def.general#nt:function-definition | 
> > grammar ]] a function definition/declaration might only have one declarator:
> > ```
> > function-definition:
> > attribute-specifier-seq_opt decl-specifier-seq_opt declarator 
> > virt-specifier-seq_opt function-body
> > ...
> > ```
> > But our implementation calls `processDeclaratorAndDeclaration` for 
> > `DeclaratorDecl`, which englobes `FunctionDecl`.
> > 
> > I also noticed that we seem to have quite extensive support for 
> > declarations even rarer ones:
> > `StaticAssertDeclaration`, `LinkageSpecificationDeclaration` ... Moreover, 
> > their names and definitions seem to adequately follow the grammar. However 
> > we don't have a `FunctionDefinition` as defined in the grammar. Was 
> > grouping `FunctionDefintion` and `FunctionDeclaration` as 
> > `SimpleDeclaration`  a conscious choice? (I looked quickly at commits that 
> > added `processDeclaratorAndDeclaration` but couldn't find any clue)
> It seemed reasonable for uniformity. However, I can definitely see an 
> argument for defining a special kind of tree node for them, especially now 
> that if we use SimpleDeclaration for functions we would always get a 
> 1-element list wrapper.
> 
> On the other hand, the following is a valid declaration:
> 
> ```
> int x, f(int);
> ```
> 
> Wouldn't it be weird if function definitions and declarations were modeled 
> differently? I understand they are different in the C++ grammar, but I think 
> it would be at least a bit surprising for users.
Oh my god... that hurts my eyes...

I don't know what is more weird to be fair. In my view unifying both seems like 
a similar decision as unifying expressions and statements under the same 
ClangAST node because:
1. I think they have other syntactic differences.
2. I think generally people see declarations and definitions as different 
things. Whereas having a list in the definition of a function is completely 
unexpected.

Looking back at the grammar for declarations I think we would also benefit of 
trying to simplify it before mapping it to syntax trees. Although I would love 
to do that, I'm afraid there's too much on my plate already.

Should I leave a `FIXME` regarding this and adapt the other tests? Or do you 
wanna discuss this more deeply? Both options are fine for me :)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88403/new/

https://reviews.llvm.org/D88403

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


[PATCH] D88403: Migrate Declarators to use the List API

2020-09-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a reviewer: gribozavr2.
eduucaldas added inline comments.



Comment at: clang/lib/Tooling/Syntax/BuildTree.cpp:594
+/// Shrink \p Range to a subrange that only contains tokens of a list.
+ArrayRef shrinkToFitList(ArrayRef Range) {
+  auto BeginChildren = Trees.lower_bound(Range.begin());

WDYT about the naming?



Comment at: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp:2932
   |-'void'
-  |-SimpleDeclarator Declarator
-  | |-'foo'
-  | `-ParametersAndQualifiers
-  |   |-'(' OpenParen
-  |   `-')' CloseParen
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement

This sounds weird and is probably the reason why SO many tests failed.

According to the [[ 
https://eel.is/c++draft/dcl.fct.def.general#nt:function-definition | grammar ]] 
a function definition/declaration might only have one declarator:
```
function-definition:
attribute-specifier-seq_opt decl-specifier-seq_opt declarator 
virt-specifier-seq_opt function-body
...
```
But our implementation calls `processDeclaratorAndDeclaration` for 
`DeclaratorDecl`, which englobes `FunctionDecl`.

I also noticed that we seem to have quite extensive support for declarations 
even rarer ones:
`StaticAssertDeclaration`, `LinkageSpecificationDeclaration` ... Moreover, 
their names and definitions seem to adequately follow the grammar. However we 
don't have a `FunctionDefinition` as defined in the grammar. Was grouping 
`FunctionDefintion` and `FunctionDeclaration` as `SimpleDeclaration`  a 
conscious choice? (I looked quickly at commits that added 
`processDeclaratorAndDeclaration` but couldn't find any clue)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88403/new/

https://reviews.llvm.org/D88403

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


[PATCH] D88403: Migrate Declarators to use the List API

2020-09-28 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

After this change all nodes that have a delimited-list are using the
`List` API.

Implementation details:
Let's look at a declaration with multiple declarators:
`int a, b;`
To generate a declarator list node we need to have the range of
declarators: `a, b`:
However, the `ClangAST` actually stores them as separate declarations:
`int a   ;`
`intb;`
We solve that by appropriately marking the declarators on each separate
declaration in the `ClangAST` and then for the final declarator `int
b`, shrinking its range to fit to the already marked declarators.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88403

Files:
  clang/include/clang/Tooling/Syntax/Nodes.h
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp

Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -2875,21 +2875,23 @@
 TranslationUnit Detached
 |-SimpleDeclaration
 | |-'int'
-| |-SimpleDeclarator Declarator
-| | |-'*'
-| | `-'a'
-| |-','
-| |-SimpleDeclarator Declarator
-| | `-'b'
+| |-DeclaratorList Declarators
+| | |-SimpleDeclarator ListElement
+| | | |-'*'
+| | | `-'a'
+| | |-',' ListDelimiter
+| | `-SimpleDeclarator ListElement
+| |   `-'b'
 | `-';'
 `-SimpleDeclaration
   |-'int'
-  |-SimpleDeclarator Declarator
-  | |-'*'
-  | `-'c'
-  |-','
-  |-SimpleDeclarator Declarator
-  | `-'d'
+  |-DeclaratorList Declarators
+  | |-SimpleDeclarator ListElement
+  | | |-'*'
+  | | `-'c'
+  | |-',' ListDelimiter
+  | `-SimpleDeclarator ListElement
+  |   `-'d'
   `-';'
 )txt"));
 }
@@ -2904,12 +2906,13 @@
 `-SimpleDeclaration
   |-'typedef'
   |-'int'
-  |-SimpleDeclarator Declarator
-  | |-'*'
-  | `-'a'
-  |-','
-  |-SimpleDeclarator Declarator
-  | `-'b'
+  |-DeclaratorList Declarators
+  | |-SimpleDeclarator ListElement
+  | | |-'*'
+  | | `-'a'
+  | |-',' ListDelimiter
+  | `-SimpleDeclarator ListElement
+  |   `-'b'
   `-';'
 )txt"));
 }
@@ -2926,33 +2929,36 @@
 TranslationUnit Detached
 `-SimpleDeclaration
   |-'void'
-  |-SimpleDeclarator Declarator
-  | |-'foo'
-  | `-ParametersAndQualifiers
-  |   |-'(' OpenParen
-  |   `-')' CloseParen
+  |-DeclaratorList Declarators
+  | `-SimpleDeclarator ListElement
+  |   |-'foo'
+  |   `-ParametersAndQualifiers
+  | |-'(' OpenParen
+  | `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
 |-DeclarationStatement Statement
 | |-SimpleDeclaration
 | | |-'int'
-| | |-SimpleDeclarator Declarator
-| | | |-'*'
-| | | `-'a'
-| | |-','
-| | `-SimpleDeclarator Declarator
-| |   `-'b'
+| | `-DeclaratorList Declarators
+| |   |-SimpleDeclarator ListElement
+| |   | |-'*'
+| |   | `-'a'
+| |   |-',' ListDelimiter
+| |   `-SimpleDeclarator ListElement
+| | `-'b'
 | `-';'
 |-DeclarationStatement Statement
 | |-SimpleDeclaration
 | | |-'typedef'
 | | |-'int'
-| | |-SimpleDeclarator Declarator
-| | | |-'*'
-| | | `-'ta'
-| | |-','
-| | `-SimpleDeclarator Declarator
-| |   `-'tb'
+| | `-DeclaratorList Declarators
+| |   |-SimpleDeclarator ListElement
+| |   | |-'*'
+| |   | `-'ta'
+| |   |-',' ListDelimiter
+| |   `-SimpleDeclarator ListElement
+| | `-'tb'
 | `-';'
 `-'}' CloseParen
 )txt"));
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -183,6 +183,8 @@
 return new (A.getAllocator()) syntax::CallArguments;
   case syntax::NodeKind::ParameterDeclarationList:
 return new (A.getAllocator()) syntax::ParameterDeclarationList;
+  case syntax::NodeKind::DeclaratorList:
+return new (A.getAllocator()) syntax::DeclaratorList;
   }
   llvm_unreachable("unknown node kind");
 }
Index: clang/lib/Tooling/Syntax/Nodes.cpp
===
--- clang/lib/Tooling/Syntax/Nodes.cpp
+++ clang/lib/Tooling/Syntax/Nodes.cpp
@@ -136,6 +136,8 @@
 return OS << "CallArguments";
   case NodeKind::ParameterDeclarationList:
 return OS << "ParameterDeclarationList";
+  case NodeKind::DeclaratorList:
+return OS << "DeclaratorList";
   }
   llvm_unreachable("unknown node kind");
 }
@@ -218,6 +220,8 @@
 return OS << "Callee";
   case syntax::NodeRole::Arguments:
 return OS << "Arguments";
+  case syntax::NodeRole::Declarators:
+return OS << "Declarators";
   }
   llvm_unreachable("invalid role");
 }
@@ -291,6 +295,29 @@
   return Children;
 }
 
+std::vector

[PATCH] D87839: [SyntaxTree] Test the List API

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGc3c08bfdfd62: [SyntaxTree] Test the List API (authored by 
eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87839/new/

https://reviews.llvm.org/D87839

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -9,6 +9,8 @@
 #include "clang/Tooling/Syntax/Tree.h"
 #include "TreeTestBase.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
+#include "clang/Tooling/Syntax/Nodes.h"
+#include "llvm/ADT/STLExtras.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -122,4 +124,231 @@
   }
 }
 
+class ListTest : public SyntaxTreeTest {
+private:
+  std::string dumpQuotedTokensOrNull(const Node *N) {
+return N ? "'" +
+   StringRef(N->dumpTokens(Arena->getSourceManager()))
+   .trim()
+   .str() +
+   "'"
+ : "null";
+  }
+
+protected:
+  std::string
+  dumpElementsAndDelimiters(ArrayRef> EDs) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+
+OS << "[";
+
+llvm::interleaveComma(
+EDs, OS, [, this](const List::ElementAndDelimiter ) {
+  OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+ << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+});
+
+OS << "]";
+
+return OS.str();
+  }
+
+  std::string dumpNodes(ArrayRef Nodes) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+
+OS << "[";
+
+llvm::interleaveComma(Nodes, OS, [, this](const Node *N) {
+  OS << dumpQuotedTokensOrNull(N);
+});
+
+OS << "]";
+
+return OS.str();
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(TreeTests, ListTest,
+::testing::ValuesIn(allTestClangConfigs()), );
+
+/// "a, b, c"  <=> [("a", ","), ("b", ","), ("c", null)]
+TEST_P(ListTest, List_Separated_WellFormed) {
+  buildTree("", GetParam());
+
+  // "a, b, c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', ','), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
+}
+
+/// "a,  , c"  <=> [("a", ","), (null, ","), ("c", null)]
+TEST_P(ListTest, List_Separated_MissingElement) {
+  buildTree("", GetParam());
+
+  // "a,  , c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), (null, ','), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', null, 'c']");
+}
+
+/// "a, b  c"  <=> [("a", ","), ("b", null), ("c", null)]
+TEST_P(ListTest, List_Separated_MissingDelimiter) {
+  buildTree("", GetParam());
+
+  // "a, b  c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', null), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
+}
+
+/// "a, b,"<=> [("a", ","), ("b", ","), (null, null)]
+TEST_P(ListTest, List_Separated_MissingLastElement) {
+  buildTree("", GetParam());
+
+  // "a, b, c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  

[PATCH] D88106: [SyntaxTree] Provide iterator-like functions for Lists

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88106

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -9,6 +9,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/Nodes.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
 #include 
@@ -294,46 +295,107 @@
   }
 }
 
-std::vector>
-syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-return {};
+syntax::List::ElementAndDelimiter
+syntax::List::getWithDelimiter(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  if (!Next)
+return {Element, nullptr};
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return {Element, nullptr};
+  case syntax::NodeRole::ListDelimiter:
+return {Element, cast(Next)};
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
+  }
+}
 
-  std::vector> Children;
-  syntax::Node *ElementWithoutDelimiter = nullptr;
-  for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
-switch (C->getRole()) {
-case syntax::NodeRole::ListElement: {
-  if (ElementWithoutDelimiter) {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-  }
-  ElementWithoutDelimiter = C;
-  break;
-}
-case syntax::NodeRole::ListDelimiter: {
-  Children.push_back({ElementWithoutDelimiter, cast(C)});
-  ElementWithoutDelimiter = nullptr;
-  break;
-}
-default:
-  llvm_unreachable(
-  "A list can have only elements and delimiters as children.");
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterDelimiter(syntax::Leaf *Delimiter) {
+  assert(Delimiter && Delimiter->getRole() == syntax::NodeRole::ListDelimiter);
+  auto *Next = Delimiter->getNextSibling();
+  if (!Next) {
+switch (getTerminationKind()) {
+// List is separated and ends with delimiter
+// => missing last ElementAndDelimiter.
+case syntax::List::TerminationKind::Separated:
+  return llvm::Optional>(
+  {nullptr, nullptr});
+case syntax::List::TerminationKind::Terminated:
+case syntax::List::TerminationKind::MaybeTerminated:
+  return None;
 }
   }
-
-  switch (getTerminationKind()) {
-  case syntax::List::TerminationKind::Separated: {
-Children.push_back({ElementWithoutDelimiter, nullptr});
-break;
+  switch (Next->getRole()) {
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // Consecutive Delimiters => missing Element
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(Next)});
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
-  case syntax::List::TerminationKind::Terminated:
-  case syntax::List::TerminationKind::MaybeTerminated: {
-if (ElementWithoutDelimiter) {
-  Children.push_back({ElementWithoutDelimiter, nullptr});
-}
-break;
+}
+
+llvm::Optional>
+syntax::List::getElementAndDelimiterAfterElement(syntax::Node *Element) {
+  assert(Element && Element->getRole() == syntax::NodeRole::ListElement);
+  auto *Next = Element->getNextSibling();
+  // a  b, x => End of list, this was the last ElementAndDelimiter.
+  if (!Next)
+return None;
+
+  switch (Next->getRole()) {
+  // x  b, c => next ElementAndDelimiter starts with 'b'.
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(Next);
+
+  // a  x, c => next ElementAndDelimiter starts after ','.
+  case syntax::NodeRole::ListDelimiter:
+return getElementAndDelimiterAfterDelimiter(cast(Next));
+
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as children.");
   }
+}
+
+llvm::Optional>
+syntax::List::getNext(syntax::List::ElementAndDelimiter ) {
+  if (auto *Delimiter = ED.delimiter)
+return getElementAndDelimiterAfterDelimiter(Delimiter);
+  if (auto *Element = ED.element)
+return getElementAndDelimiterAfterElement(Element);
+  return None;
+}
+
+llvm::Optional>
+syntax::List::getFirst() {
+  auto *First = this->getFirstChild();
+  if (!First)
+return None;
+  switch (First->getRole()) {
+  case syntax::NodeRole::ListElement:
+return getWithDelimiter(First);
+  case syntax::NodeRole::ListDelimiter:
+return llvm::Optional>(
+{nullptr, cast(First)});
+  default:
+llvm_unreachable(
+"A list can have only elements and delimiters as 

[PATCH] D87839: [SyntaxTree] Test the List API

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas marked an inline comment as done.
eduucaldas added inline comments.



Comment at: clang/include/clang/Tooling/Syntax/Tree.h:224
+  /// "a, b  c"  <=> [("a", ","), ("b", nul), ("c", nul)]
+  /// "a, b,"<=> [("a", ","), ("b", ","), (nul, nul)]
   ///

gribozavr2 wrote:
> I'd slightly prefer "null" b/c "nul" refers to the ASCII character. Feel free 
> to add more spaces to make columns line up :)
Thanks for providing my solution to my crazyness ^^


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87839/new/

https://reviews.llvm.org/D87839

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


[PATCH] D87839: [SyntaxTree] Test the List API

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293490.
eduucaldas added a comment.

.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87839/new/

https://reviews.llvm.org/D87839

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -9,6 +9,8 @@
 #include "clang/Tooling/Syntax/Tree.h"
 #include "TreeTestBase.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
+#include "clang/Tooling/Syntax/Nodes.h"
+#include "llvm/ADT/STLExtras.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -122,4 +124,231 @@
   }
 }
 
+class ListTest : public SyntaxTreeTest {
+private:
+  std::string dumpQuotedTokensOrNull(const Node *N) {
+return N ? "'" +
+   StringRef(N->dumpTokens(Arena->getSourceManager()))
+   .trim()
+   .str() +
+   "'"
+ : "null";
+  }
+
+protected:
+  std::string
+  dumpElementsAndDelimiters(ArrayRef> EDs) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+
+OS << "[";
+
+llvm::interleaveComma(
+EDs, OS, [, this](const List::ElementAndDelimiter ) {
+  OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+ << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+});
+
+OS << "]";
+
+return OS.str();
+  }
+
+  std::string dumpNodes(ArrayRef Nodes) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+
+OS << "[";
+
+llvm::interleaveComma(Nodes, OS, [, this](const Node *N) {
+  OS << dumpQuotedTokensOrNull(N);
+});
+
+OS << "]";
+
+return OS.str();
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(TreeTests, ListTest,
+::testing::ValuesIn(allTestClangConfigs()), );
+
+/// "a, b, c"  <=> [("a", ","), ("b", ","), ("c", null)]
+TEST_P(ListTest, List_Separated_WellFormed) {
+  buildTree("", GetParam());
+
+  // "a, b, c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', ','), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
+}
+
+/// "a,  , c"  <=> [("a", ","), (null, ","), ("c", null)]
+TEST_P(ListTest, List_Separated_MissingElement) {
+  buildTree("", GetParam());
+
+  // "a,  , c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), (null, ','), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', null, 'c']");
+}
+
+/// "a, b  c"  <=> [("a", ","), ("b", null), ("c", null)]
+TEST_P(ListTest, List_Separated_MissingDelimiter) {
+  buildTree("", GetParam());
+
+  // "a, b  c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', null), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
+}
+
+/// "a, b,"<=> [("a", ","), ("b", ","), (null, null)]
+TEST_P(ListTest, List_Separated_MissingLastElement) {
+  buildTree("", GetParam());
+
+  // "a, b, c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  },
+  

[PATCH] D87839: [SyntaxTree] Test the List API

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293489.
eduucaldas marked 3 inline comments as done.
eduucaldas added a comment.

Answer code review.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87839/new/

https://reviews.llvm.org/D87839

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -9,6 +9,8 @@
 #include "clang/Tooling/Syntax/Tree.h"
 #include "TreeTestBase.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
+#include "clang/Tooling/Syntax/Nodes.h"
+#include "llvm/ADT/STLExtras.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -122,4 +124,239 @@
   }
 }
 
+class ListTest : public SyntaxTreeTest {
+private:
+  std::string dumpQuotedTokensOrNull(const Node *N) {
+return N ? "'" +
+   StringRef(N->dumpTokens(Arena->getSourceManager()))
+   .trim()
+   .str() +
+   "'"
+ : "null";
+  }
+
+protected:
+  std::string
+  dumpElementsAndDelimiters(ArrayRef> EDs) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+
+OS << "[";
+
+llvm::interleaveComma(
+EDs, OS, [, this](const List::ElementAndDelimiter ) {
+  OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+ << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+});
+
+OS << "]";
+
+return OS.str();
+  }
+
+  std::string dumpNodes(ArrayRef Nodes) {
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+
+OS << "[";
+
+llvm::interleaveComma(Nodes, OS, [, this](const Node *N) {
+  OS << dumpQuotedTokensOrNull(N);
+});
+
+OS << "]";
+
+return OS.str();
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(TreeTests, ListTest,
+::testing::ValuesIn(allTestClangConfigs()), );
+
+/// "a, b, c"  <=> [("a", ","), ("b", ","), ("c", null)]
+TEST_P(ListTest, List_Separated_WellFormed) {
+  buildTree("", GetParam());
+
+  // "a, b, c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', ','), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()),
+"['a', 'b', 'c']");
+}
+
+/// "a,  , c"  <=> [("a", ","), (null, ","), ("c", null)]
+TEST_P(ListTest, List_Separated_MissingElement) {
+  buildTree("", GetParam());
+
+  // "a,  , c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), (null, ','), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()),
+"['a', null, 'c']");
+}
+
+/// "a, b  c"  <=> [("a", ","), ("b", null), ("c", null)]
+TEST_P(ListTest, List_Separated_MissingDelimiter) {
+  buildTree("", GetParam());
+
+  // "a, b  c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', null), ('c', null)]");
+  EXPECT_EQ(dumpNodes(List->getElementsAsNodes()),
+"['a', 'b', 'c']");
+}
+
+/// "a, b,"<=> [("a", ","), ("b", ","), (null, null)]
+TEST_P(ListTest, List_Separated_MissingLastElement) {
+  buildTree("", GetParam());
+
+  // "a, b, c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+ 

[PATCH] D88077: [SyntaxTree] Add tests for the assignment of the `canModify` tag.

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG6dc06fa09d1a: [SyntaxTree] Add tests for the assignment of 
the `canModify` tag. (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88077/new/

https://reviews.llvm.org/D88077

Files:
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp

Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -3855,8 +3855,76 @@
 )txt"));
 }
 
-TEST_P(BuildSyntaxTreeTest, NonModifiableNodes) {
-  // Some nodes are non-modifiable, they are marked with 'I:'.
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_Leaf) {
+  // All nodes can be mutated.
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define OPEN {
+#define CLOSE }
+
+void test() {
+  OPEN
+1;
+  CLOSE
+
+  OPEN
+2;
+  }
+}
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen
+|-CompoundStatement Statement
+| |-'{' OpenParen
+| |-ExpressionStatement Statement
+| | |-IntegerLiteralExpression Expression
+| | | `-'1' LiteralToken
+| | `-';'
+| `-'}' CloseParen
+|-CompoundStatement Statement
+| |-'{' OpenParen
+| |-ExpressionStatement Statement
+| | |-IntegerLiteralExpression Expression
+| | | `-'2' LiteralToken
+| | `-';'
+| `-'}' CloseParen
+`-'}' CloseParen
+)txt"));
+}
+
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_MatchTree) {
+  // Some nodes are unmodifiable, they are marked with 'unmodifiable'.
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define BRACES {}
+
+void test() BRACES
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen unmodifiable
+`-'}' CloseParen unmodifiable
+)txt"));
+}
+
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_MismatchTree) {
   EXPECT_TRUE(treeDumpEqual(
   R"cpp(
 #define HALF_IF if (1+
@@ -3896,21 +3964,16 @@
 )txt"));
 }
 
-TEST_P(BuildSyntaxTreeTest, ModifiableNodes) {
-  // All nodes can be mutated.
+TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_ModifiableArguments) {
+  // FIXME: Note that the substitutions for `X` and `Y` are marked modifiable.
+  // However we cannot change `X` freely. Indeed if we change its substitution
+  // in the condition we should also change it the then-branch.
   EXPECT_TRUE(treeDumpEqual(
   R"cpp(
-#define OPEN {
-#define CLOSE }
+#define MIN(X,Y) X < Y ? X : Y
 
 void test() {
-  OPEN
-1;
-  CLOSE
-
-  OPEN
-2;
-  }
+  MIN(1,2);
 }
 )cpp",
   R"txt(
@@ -3924,24 +3987,109 @@
   |   `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
-|-CompoundStatement Statement
-| |-'{' OpenParen
-| |-ExpressionStatement Statement
-| | |-IntegerLiteralExpression Expression
+|-ExpressionStatement Statement
+| |-UnknownExpression Expression
+| | |-BinaryOperatorExpression unmodifiable
+| | | |-IntegerLiteralExpression LeftHandSide
+| | | | `-'1' LiteralToken
+| | | |-'<' OperatorToken unmodifiable
+| | | `-IntegerLiteralExpression RightHandSide
+| | |   `-'2' LiteralToken
+| | |-'?' unmodifiable
+| | |-IntegerLiteralExpression
 | | | `-'1' LiteralToken
-| | `-';'
-| `-'}' CloseParen
-|-CompoundStatement Statement
-| |-'{' OpenParen
-| |-ExpressionStatement Statement
-| | |-IntegerLiteralExpression Expression
-| | | `-'2' LiteralToken
-| | `-';'
-| `-'}' CloseParen
+| | |-':' unmodifiable
+| | `-IntegerLiteralExpression
+| |   `-'2' LiteralToken
+| `-';'
 `-'}' CloseParen
 )txt"));
 }
 
+TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_MismatchTree) {
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define HALF_IF(X) if (X &&
+#define HALF_IF_2(Y) Y) {}
+void test() {
+  HALF_IF(1) HALF_IF_2(0) else {}
+})cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen
+|-IfStatement Statement
+| |-'if' IntroducerKeyword unmodifiable
+| |-'(' unmodifiable
+| |-BinaryOperatorExpression unmodifiable
+| | |-IntegerLiteralExpression LeftHandSide
+| | | `-'1' LiteralToken
+| | |-'&&' OperatorToken unmodifiable
+| | `-IntegerLiteralExpression RightHandSide
+| |   `-'0' LiteralToken
+| |-')' unmodifiable
+| |-CompoundStatement ThenStatement unmodifiable
+| | |-'{' OpenParen 

[PATCH] D88077: [SyntaxTree] Add tests for the assignment of the `canModify` tag.

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a comment.

Looking for feedback, specially on the names I used. 
Also if you have ideas of interesting tests they will be gladly accepted :)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88077/new/

https://reviews.llvm.org/D88077

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


[PATCH] D87839: [SyntaxTree] Test the List API

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a reviewer: gribozavr2.
eduucaldas added a comment.

I made a separate class for the tests on Lists, as it didn't share any methods 
with the tests for Trees. What do you think about that?

Should I also put the tests for lists in a different file, even though 
`TreeTest.cpp` could mean test `Tree.h`?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87839/new/

https://reviews.llvm.org/D87839

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


[PATCH] D88077: [SyntaxTree] Add tests for the assignment of the `canModify` tag.

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added a reviewer: gribozavr2.
eduucaldas added inline comments.



Comment at: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp:3858
 
-TEST_P(BuildSyntaxTreeTest, NonModifiableNodes) {
-  // Some nodes are non-modifiable, they are marked with 'I:'.
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_Leaf) {
+  // All nodes can be mutated.

This is the same as `ModifiableNodes` I just changed the order, to follow a 
sequence of complexity


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88077/new/

https://reviews.llvm.org/D88077

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


[PATCH] D88077: [SyntaxTree] Add tests for the assignment of the `canModify` tag.

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293389.
eduucaldas added a comment.

Add FIXME for `MIN(X, Y)`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88077/new/

https://reviews.llvm.org/D88077

Files:
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp

Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -3855,8 +3855,76 @@
 )txt"));
 }
 
-TEST_P(BuildSyntaxTreeTest, NonModifiableNodes) {
-  // Some nodes are non-modifiable, they are marked with 'I:'.
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_Leaf) {
+  // All nodes can be mutated.
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define OPEN {
+#define CLOSE }
+
+void test() {
+  OPEN
+1;
+  CLOSE
+
+  OPEN
+2;
+  }
+}
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen
+|-CompoundStatement Statement
+| |-'{' OpenParen
+| |-ExpressionStatement Statement
+| | |-IntegerLiteralExpression Expression
+| | | `-'1' LiteralToken
+| | `-';'
+| `-'}' CloseParen
+|-CompoundStatement Statement
+| |-'{' OpenParen
+| |-ExpressionStatement Statement
+| | |-IntegerLiteralExpression Expression
+| | | `-'2' LiteralToken
+| | `-';'
+| `-'}' CloseParen
+`-'}' CloseParen
+)txt"));
+}
+
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_MatchTree) {
+  // Some nodes are unmodifiable, they are marked with 'unmodifiable'.
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define BRACES {}
+
+void test() BRACES
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen unmodifiable
+`-'}' CloseParen unmodifiable
+)txt"));
+}
+
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_MismatchTree) {
   EXPECT_TRUE(treeDumpEqual(
   R"cpp(
 #define HALF_IF if (1+
@@ -3896,21 +3964,16 @@
 )txt"));
 }
 
-TEST_P(BuildSyntaxTreeTest, ModifiableNodes) {
-  // All nodes can be mutated.
+TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_ModifiableArguments) {
+  // FIXME: Note that the substitutions for `X` and `Y` are marked modifiable.
+  // However we cannot change `X` freely. Indeed if we change its substitution
+  // in the condition we should also change it the then-branch.
   EXPECT_TRUE(treeDumpEqual(
   R"cpp(
-#define OPEN {
-#define CLOSE }
+#define MIN(X,Y) X < Y ? X : Y
 
 void test() {
-  OPEN
-1;
-  CLOSE
-
-  OPEN
-2;
-  }
+  MIN(1,2);
 }
 )cpp",
   R"txt(
@@ -3924,24 +3987,109 @@
   |   `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
-|-CompoundStatement Statement
-| |-'{' OpenParen
-| |-ExpressionStatement Statement
-| | |-IntegerLiteralExpression Expression
+|-ExpressionStatement Statement
+| |-UnknownExpression Expression
+| | |-BinaryOperatorExpression unmodifiable
+| | | |-IntegerLiteralExpression LeftHandSide
+| | | | `-'1' LiteralToken
+| | | |-'<' OperatorToken unmodifiable
+| | | `-IntegerLiteralExpression RightHandSide
+| | |   `-'2' LiteralToken
+| | |-'?' unmodifiable
+| | |-IntegerLiteralExpression
 | | | `-'1' LiteralToken
-| | `-';'
-| `-'}' CloseParen
-|-CompoundStatement Statement
-| |-'{' OpenParen
-| |-ExpressionStatement Statement
-| | |-IntegerLiteralExpression Expression
-| | | `-'2' LiteralToken
-| | `-';'
-| `-'}' CloseParen
+| | |-':' unmodifiable
+| | `-IntegerLiteralExpression
+| |   `-'2' LiteralToken
+| `-';'
 `-'}' CloseParen
 )txt"));
 }
 
+TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_MismatchTree) {
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define HALF_IF(X) if (X &&
+#define HALF_IF_2(Y) Y) {}
+void test() {
+  HALF_IF(1) HALF_IF_2(0) else {}
+})cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen
+|-IfStatement Statement
+| |-'if' IntroducerKeyword unmodifiable
+| |-'(' unmodifiable
+| |-BinaryOperatorExpression unmodifiable
+| | |-IntegerLiteralExpression LeftHandSide
+| | | `-'1' LiteralToken
+| | |-'&&' OperatorToken unmodifiable
+| | `-IntegerLiteralExpression RightHandSide
+| |   `-'0' LiteralToken
+| |-')' unmodifiable
+| |-CompoundStatement ThenStatement unmodifiable
+| | |-'{' OpenParen unmodifiable
+| | `-'}' CloseParen unmodifiable
+| |-'else' ElseKeyword
+| 

[PATCH] D87839: [SyntaxTree] Test the List API

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293386.
eduucaldas added a comment.

- [SyntaxTree] Split `TreeTest` and `ListTest` testing fixtures.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87839/new/

https://reviews.llvm.org/D87839

Files:
  clang/include/clang/Tooling/Syntax/Tree.h
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Tooling/Syntax/Tree.h"
 #include "TreeTestBase.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
+#include "clang/Tooling/Syntax/Nodes.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -122,4 +123,210 @@
   }
 }
 
+class ListTest : public SyntaxTreeTest {
+private:
+  std::string dump(const List::ElementAndDelimiter ) {
+auto Format = [](StringRef s) { return "'" + s.trim().str() + "'"; };
+
+std::string Delimiter =
+ED.delimiter
+? Format(ED.delimiter->dumpTokens(Arena->getSourceManager()))
+: "nul";
+
+std::string Element =
+ED.element ? Format(ED.element->dumpTokens(Arena->getSourceManager()))
+   : "nul";
+
+return "(" + Element + ", " + Delimiter + ")";
+  }
+
+protected:
+  std::string dump(ArrayRef> EDs) {
+if (EDs.empty())
+  return "[]";
+
+std::string Storage;
+llvm::raw_string_ostream OS(Storage);
+
+OS << "[" << dump(EDs.front());
+for (auto ED = std::next(EDs.begin()), End = EDs.end(); ED != End; ++ED)
+  OS << ", " << dump(*ED);
+OS << "]";
+return OS.str();
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(TreeTests, ListTest,
+::testing::ValuesIn(allTestClangConfigs()), );
+
+/// "a, b, c"  <=> [("a", ","), ("b", ","), ("c", nul)]
+TEST_P(ListTest, List_Separated_WellFormed) {
+  buildTree("", GetParam());
+
+  // "a, b, c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dump(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', ','), ('c', nul)]");
+}
+
+/// "a,  , c"  <=> [("a", ","), (nul, ","), ("c", nul)]
+TEST_P(ListTest, List_Separated_MissingElement) {
+  buildTree("", GetParam());
+
+  // "a,  , c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dump(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), (nul, ','), ('c', nul)]");
+}
+
+/// "a, b  c"  <=> [("a", ","), ("b", nul), ("c", nul)]
+TEST_P(ListTest, List_Separated_MissingDelimiter) {
+  buildTree("", GetParam());
+
+  // "a, b  c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::identifier, "c"), NodeRole::ListElement},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dump(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', nul), ('c', nul)]");
+}
+
+/// "a, b,"<=> [("a", ","), ("b", ","), (nul, nul)]
+TEST_P(ListTest, List_Separated_MissingLastElement) {
+  buildTree("", GetParam());
+
+  // "a, b, c"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  {createLeaf(*Arena, tok::identifier, "a"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  {createLeaf(*Arena, tok::identifier, "b"), NodeRole::ListElement},
+  {createLeaf(*Arena, tok::comma), NodeRole::ListDelimiter},
+  },
+  NodeKind::CallArguments));
+
+  EXPECT_EQ(dump(List->getElementsAsNodesAndDelimiters()),
+"[('a', ','), ('b', ','), (nul, nul)]");
+}
+
+/// "a:: b:: c::" <=> [("a", "::"), ("b", "::"), ("c", "::")]
+TEST_P(ListTest, List_Terminated_WellFormed) {
+  if (!GetParam().isCXX()) {
+return;
+  }
+  buildTree("", GetParam());
+
+  // "a:: b:: c::"
+  auto *List = dyn_cast(syntax::createTree(
+  *Arena,
+  {
+  

[PATCH] D88034: [SyntaxTree][Synthesis] Fix: `deepCopy` -> `deepCopyExpandingMacros`.

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG66bcb14312a0: [SyntaxTree][Synthesis] Fix: `deepCopy` - 
`deepCopyExpandingMacros`. (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88034/new/

https://reviews.llvm.org/D88034

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -22,8 +22,8 @@
 std::vector> ChildrenWithRoles;
 ChildrenWithRoles.reserve(Children.size());
 for (const auto *Child : Children) {
-  ChildrenWithRoles.push_back(
-  std::make_pair(deepCopy(*Arena, Child), NodeRole::Unknown));
+  ChildrenWithRoles.push_back(std::make_pair(
+  deepCopyExpandingMacros(*Arena, Child), NodeRole::Unknown));
 }
 return clang::syntax::createTree(*Arena, ChildrenWithRoles,
  NodeKind::UnknownExpression);
Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -173,7 +173,7 @@
 {LeafSemiColon, NodeRole::Unknown}},
NodeKind::ContinueStatement);
 
-  auto *Copy = deepCopy(*Arena, StatementContinue);
+  auto *Copy = deepCopyExpandingMacros(*Arena, StatementContinue);
   EXPECT_TRUE(
   treeDumpEqual(Copy, StatementContinue->dump(Arena->getSourceManager(;
   // FIXME: Test that copy is independent of original, once the Mutations API is
@@ -183,7 +183,7 @@
 TEST_P(SynthesisTest, DeepCopy_Original) {
   auto *OriginalTree = buildTree("int a;", GetParam());
 
-  auto *Copy = deepCopy(*Arena, OriginalTree);
+  auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree);
   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
 TranslationUnit Detached synthesized
 `-SimpleDeclaration synthesized
@@ -197,7 +197,7 @@
 TEST_P(SynthesisTest, DeepCopy_Child) {
   auto *OriginalTree = buildTree("int a;", GetParam());
 
-  auto *Copy = deepCopy(*Arena, OriginalTree->getFirstChild());
+  auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree->getFirstChild());
   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
 SimpleDeclaration Detached synthesized
 |-'int' synthesized
@@ -216,8 +216,41 @@
 })cpp",
  GetParam());
 
-  auto *Copy = deepCopy(*Arena, OriginalTree);
-  EXPECT_TRUE(Copy == nullptr);
+  auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree);
+
+  // The syntax tree stores already expanded Tokens, we can only see whether the
+  // macro was expanded when computing replacements. The dump does show that
+  // nodes in the copy are `modifiable`.
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+TranslationUnit Detached synthesized
+`-SimpleDeclaration synthesized
+  |-'void' synthesized
+  |-SimpleDeclarator Declarator synthesized
+  | |-'test' synthesized
+  | `-ParametersAndQualifiers synthesized
+  |   |-'(' OpenParen synthesized
+  |   `-')' CloseParen synthesized
+  `-CompoundStatement synthesized
+|-'{' OpenParen synthesized
+|-IfStatement Statement synthesized
+| |-'if' IntroducerKeyword synthesized
+| |-'(' synthesized
+| |-BinaryOperatorExpression synthesized
+| | |-IntegerLiteralExpression LeftHandSide synthesized
+| | | `-'1' LiteralToken synthesized
+| | |-'+' OperatorToken synthesized
+| | `-IntegerLiteralExpression RightHandSide synthesized
+| |   `-'1' LiteralToken synthesized
+| |-')' synthesized
+| |-CompoundStatement ThenStatement synthesized
+| | |-'{' OpenParen synthesized
+| | `-'}' CloseParen synthesized
+| |-'else' ElseKeyword synthesized
+| `-CompoundStatement ElseStatement synthesized
+|   |-'{' OpenParen synthesized
+|   `-'}' CloseParen synthesized
+`-'}' CloseParen synthesized
+  )txt"));
 }
 
 TEST_P(SynthesisTest, Statement_EmptyStatement) {
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -201,25 +201,11 @@
   return T;
 }
 
-namespace {
-bool canModifyAllDescendants(const syntax::Node *N) {
-  if (const auto *L = dyn_cast(N))
-return L->canModify();
-
-  const auto *T = cast(N);
-
-  if (!T->canModify())
-return false;
-  for (const auto *Child = T->getFirstChild(); Child;
-   Child = Child->getNextSibling())
-if (!canModifyAllDescendants(Child))
-  return false;
-
-  return true;
-}
-
-syntax::Node *deepCopyImpl(syntax::Arena 

[PATCH] D88077: [SyntaxTree] Add tests for the assignment of the `canModify` tag.

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88077

Files:
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp

Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -3855,8 +3855,76 @@
 )txt"));
 }
 
-TEST_P(BuildSyntaxTreeTest, NonModifiableNodes) {
-  // Some nodes are non-modifiable, they are marked with 'I:'.
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_Leaf) {
+  // All nodes can be mutated.
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define OPEN {
+#define CLOSE }
+
+void test() {
+  OPEN
+1;
+  CLOSE
+
+  OPEN
+2;
+  }
+}
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen
+|-CompoundStatement Statement
+| |-'{' OpenParen
+| |-ExpressionStatement Statement
+| | |-IntegerLiteralExpression Expression
+| | | `-'1' LiteralToken
+| | `-';'
+| `-'}' CloseParen
+|-CompoundStatement Statement
+| |-'{' OpenParen
+| |-ExpressionStatement Statement
+| | |-IntegerLiteralExpression Expression
+| | | `-'2' LiteralToken
+| | `-';'
+| `-'}' CloseParen
+`-'}' CloseParen
+)txt"));
+}
+
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_MatchTree) {
+  // Some nodes are unmodifiable, they are marked with 'unmodifiable'.
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define BRACES {}
+
+void test() BRACES
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen unmodifiable
+`-'}' CloseParen unmodifiable
+)txt"));
+}
+
+TEST_P(BuildSyntaxTreeTest, Macro_ObjectLike_MismatchTree) {
   EXPECT_TRUE(treeDumpEqual(
   R"cpp(
 #define HALF_IF if (1+
@@ -3896,21 +3964,14 @@
 )txt"));
 }
 
-TEST_P(BuildSyntaxTreeTest, ModifiableNodes) {
+TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_ModifiableArguments) {
   // All nodes can be mutated.
   EXPECT_TRUE(treeDumpEqual(
   R"cpp(
-#define OPEN {
-#define CLOSE }
+#define MIN(X,Y) X < Y ? X : Y
 
 void test() {
-  OPEN
-1;
-  CLOSE
-
-  OPEN
-2;
-  }
+  MIN(1,2);
 }
 )cpp",
   R"txt(
@@ -3924,24 +3985,109 @@
   |   `-')' CloseParen
   `-CompoundStatement
 |-'{' OpenParen
-|-CompoundStatement Statement
-| |-'{' OpenParen
-| |-ExpressionStatement Statement
-| | |-IntegerLiteralExpression Expression
+|-ExpressionStatement Statement
+| |-UnknownExpression Expression
+| | |-BinaryOperatorExpression unmodifiable
+| | | |-IntegerLiteralExpression LeftHandSide
+| | | | `-'1' LiteralToken
+| | | |-'<' OperatorToken unmodifiable
+| | | `-IntegerLiteralExpression RightHandSide
+| | |   `-'2' LiteralToken
+| | |-'?' unmodifiable
+| | |-IntegerLiteralExpression
 | | | `-'1' LiteralToken
-| | `-';'
-| `-'}' CloseParen
-|-CompoundStatement Statement
-| |-'{' OpenParen
-| |-ExpressionStatement Statement
-| | |-IntegerLiteralExpression Expression
-| | | `-'2' LiteralToken
-| | `-';'
-| `-'}' CloseParen
+| | |-':' unmodifiable
+| | `-IntegerLiteralExpression
+| |   `-'2' LiteralToken
+| `-';'
+`-'}' CloseParen
+)txt"));
+}
+
+TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_MismatchTree) {
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+#define HALF_IF(X) if (X &&
+#define HALF_IF_2(Y) Y) {}
+void test() {
+  HALF_IF(1) HALF_IF_2(0) else {}
+})cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'void'
+  |-SimpleDeclarator Declarator
+  | |-'test'
+  | `-ParametersAndQualifiers
+  |   |-'(' OpenParen
+  |   `-')' CloseParen
+  `-CompoundStatement
+|-'{' OpenParen
+|-IfStatement Statement
+| |-'if' IntroducerKeyword unmodifiable
+| |-'(' unmodifiable
+| |-BinaryOperatorExpression unmodifiable
+| | |-IntegerLiteralExpression LeftHandSide
+| | | `-'1' LiteralToken
+| | |-'&&' OperatorToken unmodifiable
+| | `-IntegerLiteralExpression RightHandSide
+| |   `-'0' LiteralToken
+| |-')' unmodifiable
+| |-CompoundStatement ThenStatement unmodifiable
+| | |-'{' OpenParen unmodifiable
+| | `-'}' CloseParen unmodifiable
+| |-'else' ElseKeyword
+| `-CompoundStatement ElseStatement
+|   |-'{' OpenParen
+|   `-'}' CloseParen
 `-'}' CloseParen
 )txt"));
 }
 
+TEST_P(BuildSyntaxTreeTest, Macro_FunctionLike_Variadic) {
+  EXPECT_TRUE(treeDumpEqualOnAnnotations(
+  R"cpp(
+#define 

[PATCH] D88034: [SyntaxTree][Synthesis] Implement `deepCopyExpandingMacros`

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293363.
eduucaldas added a comment.

.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88034/new/

https://reviews.llvm.org/D88034

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -22,8 +22,8 @@
 std::vector> ChildrenWithRoles;
 ChildrenWithRoles.reserve(Children.size());
 for (const auto *Child : Children) {
-  ChildrenWithRoles.push_back(
-  std::make_pair(deepCopy(*Arena, Child), NodeRole::Unknown));
+  ChildrenWithRoles.push_back(std::make_pair(
+  deepCopyExpandingMacros(*Arena, Child), NodeRole::Unknown));
 }
 return clang::syntax::createTree(*Arena, ChildrenWithRoles,
  NodeKind::UnknownExpression);
Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -173,7 +173,7 @@
 {LeafSemiColon, NodeRole::Unknown}},
NodeKind::ContinueStatement);
 
-  auto *Copy = deepCopy(*Arena, StatementContinue);
+  auto *Copy = deepCopyExpandingMacros(*Arena, StatementContinue);
   EXPECT_TRUE(
   treeDumpEqual(Copy, StatementContinue->dump(Arena->getSourceManager(;
   // FIXME: Test that copy is independent of original, once the Mutations API is
@@ -183,7 +183,7 @@
 TEST_P(SynthesisTest, DeepCopy_Original) {
   auto *OriginalTree = buildTree("int a;", GetParam());
 
-  auto *Copy = deepCopy(*Arena, OriginalTree);
+  auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree);
   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
 TranslationUnit Detached synthesized
 `-SimpleDeclaration synthesized
@@ -197,7 +197,7 @@
 TEST_P(SynthesisTest, DeepCopy_Child) {
   auto *OriginalTree = buildTree("int a;", GetParam());
 
-  auto *Copy = deepCopy(*Arena, OriginalTree->getFirstChild());
+  auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree->getFirstChild());
   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
 SimpleDeclaration Detached synthesized
 |-'int' synthesized
@@ -216,8 +216,41 @@
 })cpp",
  GetParam());
 
-  auto *Copy = deepCopy(*Arena, OriginalTree);
-  EXPECT_TRUE(Copy == nullptr);
+  auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree);
+
+  // The syntax tree stores already expanded Tokens, we can only see whether the
+  // macro was expanded when computing replacements. The dump does show that
+  // nodes in the copy are `modifiable`.
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+TranslationUnit Detached synthesized
+`-SimpleDeclaration synthesized
+  |-'void' synthesized
+  |-SimpleDeclarator Declarator synthesized
+  | |-'test' synthesized
+  | `-ParametersAndQualifiers synthesized
+  |   |-'(' OpenParen synthesized
+  |   `-')' CloseParen synthesized
+  `-CompoundStatement synthesized
+|-'{' OpenParen synthesized
+|-IfStatement Statement synthesized
+| |-'if' IntroducerKeyword synthesized
+| |-'(' synthesized
+| |-BinaryOperatorExpression synthesized
+| | |-IntegerLiteralExpression LeftHandSide synthesized
+| | | `-'1' LiteralToken synthesized
+| | |-'+' OperatorToken synthesized
+| | `-IntegerLiteralExpression RightHandSide synthesized
+| |   `-'1' LiteralToken synthesized
+| |-')' synthesized
+| |-CompoundStatement ThenStatement synthesized
+| | |-'{' OpenParen synthesized
+| | `-'}' CloseParen synthesized
+| |-'else' ElseKeyword synthesized
+| `-CompoundStatement ElseStatement synthesized
+|   |-'{' OpenParen synthesized
+|   `-'}' CloseParen synthesized
+`-'}' CloseParen synthesized
+  )txt"));
 }
 
 TEST_P(SynthesisTest, Statement_EmptyStatement) {
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -201,25 +201,11 @@
   return T;
 }
 
-namespace {
-bool canModifyAllDescendants(const syntax::Node *N) {
-  if (const auto *L = dyn_cast(N))
-return L->canModify();
-
-  const auto *T = cast(N);
-
-  if (!T->canModify())
-return false;
-  for (const auto *Child = T->getFirstChild(); Child;
-   Child = Child->getNextSibling())
-if (!canModifyAllDescendants(Child))
-  return false;
-
-  return true;
-}
-
-syntax::Node *deepCopyImpl(syntax::Arena , const syntax::Node *N) {
+syntax::Node *clang::syntax::deepCopyExpandingMacros(syntax::Arena ,
+

[PATCH] D88034: [SyntaxTree][Synthesis] Implement `deepCopyExpandingMacros`

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293361.
eduucaldas added a comment.

Remove buggy `deepCopy`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88034/new/

https://reviews.llvm.org/D88034

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -217,7 +217,53 @@
  GetParam());
 
   auto *Copy = deepCopy(*Arena, OriginalTree);
-  EXPECT_TRUE(Copy == nullptr);
+  EXPECT_EQ(Copy, nullptr);
+}
+
+TEST_P(SynthesisTest, DeepCopy_MacroExpanding) {
+  auto *OriginalTree = buildTree(R"cpp(
+#define HALF_IF if (1+
+#define HALF_IF_2 1) {}
+void test() {
+  HALF_IF HALF_IF_2 else {}
+})cpp",
+ GetParam());
+
+  auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree);
+
+  // The syntax tree stores expanded Tokens already, as a result we can only see
+  // the macro expansion when computing replacements. The dump does show that
+  // nodes are `modifiable` now.
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+TranslationUnit Detached synthesized
+`-SimpleDeclaration synthesized
+  |-'void' synthesized
+  |-SimpleDeclarator Declarator synthesized
+  | |-'test' synthesized
+  | `-ParametersAndQualifiers synthesized
+  |   |-'(' OpenParen synthesized
+  |   `-')' CloseParen synthesized
+  `-CompoundStatement synthesized
+|-'{' OpenParen synthesized
+|-IfStatement Statement synthesized
+| |-'if' IntroducerKeyword synthesized
+| |-'(' synthesized
+| |-BinaryOperatorExpression synthesized
+| | |-IntegerLiteralExpression LeftHandSide synthesized
+| | | `-'1' LiteralToken synthesized
+| | |-'+' OperatorToken synthesized
+| | `-IntegerLiteralExpression RightHandSide synthesized
+| |   `-'1' LiteralToken synthesized
+| |-')' synthesized
+| |-CompoundStatement ThenStatement synthesized
+| | |-'{' OpenParen synthesized
+| | `-'}' CloseParen synthesized
+| |-'else' ElseKeyword synthesized
+| `-CompoundStatement ElseStatement synthesized
+|   |-'{' OpenParen synthesized
+|   `-'}' CloseParen synthesized
+`-'}' CloseParen synthesized
+  )txt"));
 }
 
 TEST_P(SynthesisTest, Statement_EmptyStatement) {
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -201,25 +201,11 @@
   return T;
 }
 
-namespace {
-bool canModifyAllDescendants(const syntax::Node *N) {
-  if (const auto *L = dyn_cast(N))
-return L->canModify();
-
-  const auto *T = cast(N);
-
-  if (!T->canModify())
-return false;
-  for (const auto *Child = T->getFirstChild(); Child;
-   Child = Child->getNextSibling())
-if (!canModifyAllDescendants(Child))
-  return false;
-
-  return true;
-}
-
-syntax::Node *deepCopyImpl(syntax::Arena , const syntax::Node *N) {
+syntax::Node *clang::syntax::deepCopyExpandingMacros(syntax::Arena ,
+ const syntax::Node *N) {
   if (const auto *L = dyn_cast(N))
+// `L->getToken()` gives us the expanded token, thus we implicitly expand
+// any macros here.
 return createLeaf(A, L->getToken()->kind(),
   L->getToken()->text(A.getSourceManager()));
 
@@ -227,18 +213,10 @@
   std::vector> Children;
   for (const auto *Child = T->getFirstChild(); Child;
Child = Child->getNextSibling())
-Children.push_back({deepCopyImpl(A, Child), Child->getRole()});
+Children.push_back({deepCopyExpandingMacros(A, Child), Child->getRole()});
 
   return createTree(A, Children, N->getKind());
 }
-} // namespace
-
-syntax::Node *clang::syntax::deepCopy(syntax::Arena , const Node *N) {
-  if (!canModifyAllDescendants(N))
-return nullptr;
-
-  return deepCopyImpl(A, N);
-}
 
 syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena ) {
   return cast(
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ clang/include/clang/Tooling/Syntax/BuildTree.h
@@ -45,17 +45,13 @@
 // Synthesis of Syntax Nodes
 syntax::EmptyStatement *createEmptyStatement(syntax::Arena );
 
-/// Creates a completely independent copy of `N` (a deep copy).
+/// Creates a completely independent copy of `N` with its macros expanded.
 ///
 /// The copy is:
 /// * Detached, i.e. `Parent == NextSibling == nullptr` and
 /// `Role == Detached`.
 /// * Synthesized, i.e. `Original == false`.
-///
-/// `N` might be backed by source code but if any descendants of `N` are
-/// unmodifiable returns 

[PATCH] D87779: [SyntaxTree] Test `findFirstLeaf` and `findLastLeaf`

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGaf582c9b0f3a: [SyntaxTree] Test `findFirstLeaf` and 
`findLastLeaf` (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87779/new/

https://reviews.llvm.org/D87779

Files:
  clang/lib/Tooling/Syntax/Tree.cpp
  clang/unittests/Tooling/Syntax/CMakeLists.txt
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -0,0 +1,125 @@
+//===- TreeTest.cpp -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Syntax/Tree.h"
+#include "TreeTestBase.h"
+#include "clang/Tooling/Syntax/BuildTree.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::syntax;
+
+namespace {
+
+class TreeTest : public SyntaxTreeTest {
+private:
+  Tree *createTree(ArrayRef Children) {
+std::vector> ChildrenWithRoles;
+ChildrenWithRoles.reserve(Children.size());
+for (const auto *Child : Children) {
+  ChildrenWithRoles.push_back(
+  std::make_pair(deepCopy(*Arena, Child), NodeRole::Unknown));
+}
+return clang::syntax::createTree(*Arena, ChildrenWithRoles,
+ NodeKind::UnknownExpression);
+  }
+
+  // Generate Forests by combining `Children` into `ParentCount` Trees.
+  //
+  // We do this recursively.
+  std::vector>
+  generateAllForests(ArrayRef Children, unsigned ParentCount) {
+assert(ParentCount > 0);
+// If there is only one Parent node, then combine `Children` under
+// this Parent.
+if (ParentCount == 1)
+  return {{createTree(Children)}};
+
+// Otherwise, combine `ChildrenCount` children under the last parent and
+// solve the smaller problem without these children and this parent. Do this
+// for every `ChildrenCount` and combine the results.
+std::vector> AllForests;
+for (unsigned ChildrenCount = 0; ChildrenCount <= Children.size();
+ ++ChildrenCount) {
+  auto *LastParent = createTree(Children.take_back(ChildrenCount));
+  for (auto  : generateAllForests(Children.drop_back(ChildrenCount),
+ ParentCount - 1)) {
+Forest.push_back(LastParent);
+AllForests.push_back(Forest);
+  }
+}
+return AllForests;
+  }
+
+protected:
+  // Generates all trees with a `Base` of `Node`s and `NodeCountPerLayer`
+  // `Node`s per layer. An example of Tree with `Base` = {`(`, `)`} and
+  // `NodeCountPerLayer` = {2, 2}:
+  //  Tree
+  //  |-Tree
+  //  `-Tree
+  //|-Tree
+  //| `-'('
+  //`-Tree
+  //  `-')'
+  std::vector
+  generateAllTreesWithShape(ArrayRef Base,
+ArrayRef NodeCountPerLayer) {
+// We compute the solution per layer. A layer is a collection of bases,
+// where each base has the same number of nodes, given by
+// `NodeCountPerLayer`.
+auto GenerateNextLayer = [this](ArrayRef> Layer,
+unsigned NextLayerNodeCount) {
+  std::vector> NextLayer;
+  for (const auto  : Layer) {
+for (const auto  :
+ generateAllForests(Base, NextLayerNodeCount)) {
+  NextLayer.push_back(
+  std::vector(NextBase.begin(), NextBase.end()));
+}
+  }
+  return NextLayer;
+};
+
+std::vector> Layer = {Base};
+for (auto NodeCount : NodeCountPerLayer)
+  Layer = GenerateNextLayer(Layer, NodeCount);
+
+std::vector AllTrees;
+AllTrees.reserve(Layer.size());
+for (const auto  : Layer)
+  AllTrees.push_back(createTree(Base));
+
+return AllTrees;
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(TreeTests, TreeTest,
+::testing::ValuesIn(allTestClangConfigs()), );
+
+TEST_P(TreeTest, FirstLeaf) {
+  buildTree("", GetParam());
+  std::vector Leafs = {createLeaf(*Arena, tok::l_paren),
+ createLeaf(*Arena, tok::r_paren)};
+  for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) {
+ASSERT_TRUE(Tree->findFirstLeaf() != nullptr);
+EXPECT_EQ(Tree->findFirstLeaf()->getToken()->kind(), tok::l_paren);
+  }
+}
+
+TEST_P(TreeTest, LastLeaf) {
+  buildTree("", GetParam());
+  std::vector Leafs = {createLeaf(*Arena, tok::l_paren),
+ createLeaf(*Arena, tok::r_paren)};
+  for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) {
+ASSERT_TRUE(Tree->findLastLeaf() != nullptr);
+

[PATCH] D88024: [SyntaxTree][Nit] Take `ArrayRef` instead of `std::vector` as argument for `createTree`

2020-09-22 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG1dc7836aed13: [SyntaxTree][Nit] Take `ArrayRef` instead of 
`std::vector` as argument for… (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88024/new/

https://reviews.llvm.org/D88024

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp


Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -190,7 +190,7 @@
 
 syntax::Tree *clang::syntax::createTree(
 syntax::Arena ,
-std::vector> Children,
+ArrayRef> Children,
 syntax::NodeKind K) {
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ clang/include/clang/Tooling/Syntax/BuildTree.h
@@ -39,7 +39,7 @@
 /// Returns it as a pointer to the base class `Tree`.
 syntax::Tree *
 createTree(syntax::Arena ,
-   std::vector> Children,
+   ArrayRef> Children,
syntax::NodeKind K);
 
 // Synthesis of Syntax Nodes


Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -190,7 +190,7 @@
 
 syntax::Tree *clang::syntax::createTree(
 syntax::Arena ,
-std::vector> Children,
+ArrayRef> Children,
 syntax::NodeKind K) {
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ clang/include/clang/Tooling/Syntax/BuildTree.h
@@ -39,7 +39,7 @@
 /// Returns it as a pointer to the base class `Tree`.
 syntax::Tree *
 createTree(syntax::Arena ,
-   std::vector> Children,
+   ArrayRef> Children,
syntax::NodeKind K);
 
 // Synthesis of Syntax Nodes
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D88034: [SyntaxTree][Synthesis] Implement `deepCopyExpandingMacros`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88034

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -217,7 +217,53 @@
  GetParam());
 
   auto *Copy = deepCopy(*Arena, OriginalTree);
-  EXPECT_TRUE(Copy == nullptr);
+  EXPECT_EQ(Copy, nullptr);
+}
+
+TEST_P(SynthesisTest, DeepCopy_MacroExpanding) {
+  auto *OriginalTree = buildTree(R"cpp(
+#define HALF_IF if (1+
+#define HALF_IF_2 1) {}
+void test() {
+  HALF_IF HALF_IF_2 else {}
+})cpp",
+ GetParam());
+
+  auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree);
+
+  // The syntax tree stores expanded Tokens already, as a result we can only see
+  // the macro expansion when computing replacements. The dump does show that
+  // nodes are `modifiable` now.
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+TranslationUnit Detached synthesized
+`-SimpleDeclaration synthesized
+  |-'void' synthesized
+  |-SimpleDeclarator Declarator synthesized
+  | |-'test' synthesized
+  | `-ParametersAndQualifiers synthesized
+  |   |-'(' OpenParen synthesized
+  |   `-')' CloseParen synthesized
+  `-CompoundStatement synthesized
+|-'{' OpenParen synthesized
+|-IfStatement Statement synthesized
+| |-'if' IntroducerKeyword synthesized
+| |-'(' synthesized
+| |-BinaryOperatorExpression synthesized
+| | |-IntegerLiteralExpression LeftHandSide synthesized
+| | | `-'1' LiteralToken synthesized
+| | |-'+' OperatorToken synthesized
+| | `-IntegerLiteralExpression RightHandSide synthesized
+| |   `-'1' LiteralToken synthesized
+| |-')' synthesized
+| |-CompoundStatement ThenStatement synthesized
+| | |-'{' OpenParen synthesized
+| | `-'}' CloseParen synthesized
+| |-'else' ElseKeyword synthesized
+| `-CompoundStatement ElseStatement synthesized
+|   |-'{' OpenParen synthesized
+|   `-'}' CloseParen synthesized
+`-'}' CloseParen synthesized
+  )txt"));
 }
 
 TEST_P(SynthesisTest, Statement_EmptyStatement) {
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -218,8 +218,13 @@
   return true;
 }
 
-syntax::Node *deepCopyImpl(syntax::Arena , const syntax::Node *N) {
+} // namespace
+
+syntax::Node *clang::syntax::deepCopyExpandingMacros(syntax::Arena ,
+ const syntax::Node *N) {
   if (const auto *L = dyn_cast(N))
+// For original nodes `L->getToken()` gives us the expanded token,
+// thus we implicitly expand any macros here.
 return createLeaf(A, L->getToken()->kind(),
   L->getToken()->text(A.getSourceManager()));
 
@@ -227,17 +232,16 @@
   std::vector> Children;
   for (const auto *Child = T->getFirstChild(); Child;
Child = Child->getNextSibling())
-Children.push_back({deepCopyImpl(A, Child), Child->getRole()});
+Children.push_back({deepCopyExpandingMacros(A, Child), Child->getRole()});
 
   return createTree(A, Children, N->getKind());
 }
-} // namespace
 
 syntax::Node *clang::syntax::deepCopy(syntax::Arena , const Node *N) {
   if (!canModifyAllDescendants(N))
 return nullptr;
 
-  return deepCopyImpl(A, N);
+  return deepCopyExpandingMacros(A, N);
 }
 
 syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena ) {
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ clang/include/clang/Tooling/Syntax/BuildTree.h
@@ -56,6 +56,16 @@
 /// unmodifiable returns `nullptr`.
 syntax::Node *deepCopy(syntax::Arena , const syntax::Node *N);
 
+/// Creates a completely independent copy of `N` with its macros expanded.
+///
+/// Like `deepCopy` the copy is:
+/// * Detached, i.e. `Parent == NextSibling == nullptr` and
+/// `Role == Detached`.
+/// * Synthesized, i.e. `Original == false`.
+///
+/// However, `N` might have descendants coming from macros, in this case those
+/// macros are expanded.
+syntax::Node *deepCopyExpandingMacros(syntax::Arena , const syntax::Node *N);
 } // namespace syntax
 } // namespace clang
 #endif
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D88024: [SyntaxTree][Nit] Take `ArrayRef` instead of `std::vector` as argument for `createTree`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
eduucaldas added a reviewer: gribozavr2.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

I also assured that there are no other functions unnecessarily using 
std::vector as argument.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88024

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp


Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -190,7 +190,7 @@
 
 syntax::Tree *clang::syntax::createTree(
 syntax::Arena ,
-std::vector> Children,
+ArrayRef> Children,
 syntax::NodeKind K) {
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ clang/include/clang/Tooling/Syntax/BuildTree.h
@@ -39,7 +39,7 @@
 /// Returns it as a pointer to the base class `Tree`.
 syntax::Tree *
 createTree(syntax::Arena ,
-   std::vector> Children,
+   ArrayRef> Children,
syntax::NodeKind K);
 
 // Synthesis of Syntax Nodes


Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -190,7 +190,7 @@
 
 syntax::Tree *clang::syntax::createTree(
 syntax::Arena ,
-std::vector> Children,
+ArrayRef> Children,
 syntax::NodeKind K) {
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ clang/include/clang/Tooling/Syntax/BuildTree.h
@@ -39,7 +39,7 @@
 /// Returns it as a pointer to the base class `Tree`.
 syntax::Tree *
 createTree(syntax::Arena ,
-   std::vector> Children,
+   ArrayRef> Children,
syntax::NodeKind K);
 
 // Synthesis of Syntax Nodes
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D87779: [SyntaxTree] Test `findFirstLeaf` and `findLastLeaf`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293167.
eduucaldas added a comment.

Comment `generateAllTreesWithShape`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87779/new/

https://reviews.llvm.org/D87779

Files:
  clang/lib/Tooling/Syntax/Tree.cpp
  clang/unittests/Tooling/Syntax/CMakeLists.txt
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -0,0 +1,125 @@
+//===- TreeTest.cpp -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Syntax/Tree.h"
+#include "TreeTestBase.h"
+#include "clang/Tooling/Syntax/BuildTree.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::syntax;
+
+namespace {
+
+class TreeTest : public SyntaxTreeTest {
+private:
+  Tree *createTree(ArrayRef Children) {
+std::vector> ChildrenWithRoles;
+ChildrenWithRoles.reserve(Children.size());
+for (const auto *Child : Children) {
+  ChildrenWithRoles.push_back(
+  std::make_pair(deepCopy(*Arena, Child), NodeRole::Unknown));
+}
+return clang::syntax::createTree(*Arena, ChildrenWithRoles,
+ NodeKind::UnknownExpression);
+  }
+
+  // Generate Forests by combining `Children` into `ParentCount` Trees.
+  //
+  // We do this recursively.
+  std::vector>
+  generateAllForests(ArrayRef Children, unsigned ParentCount) {
+assert(ParentCount > 0);
+// If there is only one Parent node, then combine `Children` under
+// this Parent.
+if (ParentCount == 1)
+  return {{createTree(Children)}};
+
+// Otherwise, combine `ChildrenCount` children under the last parent and
+// solve the smaller problem without these children and this parent. Do this
+// for every `ChildrenCount` and combine the results.
+std::vector> AllForests;
+for (unsigned ChildrenCount = 0; ChildrenCount <= Children.size();
+ ++ChildrenCount) {
+  auto *LastParent = createTree(Children.take_back(ChildrenCount));
+  for (auto  : generateAllForests(Children.drop_back(ChildrenCount),
+ ParentCount - 1)) {
+Forest.push_back(LastParent);
+AllForests.push_back(Forest);
+  }
+}
+return AllForests;
+  }
+
+protected:
+  // Generates all trees with a `Base` of `Node`s and `NodeCountPerLayer`
+  // `Node`s per layer. An example of Tree with `Base` = {`(`, `)`} and
+  // `NodeCountPerLayer` = {2, 2}:
+  //  Tree
+  //  |-Tree
+  //  `-Tree
+  //|-Tree
+  //| `-'('
+  //`-Tree
+  //  `-')'
+  std::vector
+  generateAllTreesWithShape(ArrayRef Base,
+ArrayRef NodeCountPerLayer) {
+// We compute the solution per layer. A layer is a collection of bases,
+// where each base has the same number of nodes, given by
+// `NodeCountPerLayer`.
+auto GenerateNextLayer = [this](ArrayRef> Layer,
+unsigned NextLayerNodeCount) {
+  std::vector> NextLayer;
+  for (const auto  : Layer) {
+for (const auto  :
+ generateAllForests(Base, NextLayerNodeCount)) {
+  NextLayer.push_back(
+  std::vector(NextBase.begin(), NextBase.end()));
+}
+  }
+  return NextLayer;
+};
+
+std::vector> Layer = {Base};
+for (auto NodeCount : NodeCountPerLayer)
+  Layer = GenerateNextLayer(Layer, NodeCount);
+
+std::vector AllTrees;
+AllTrees.reserve(Layer.size());
+for (const auto  : Layer)
+  AllTrees.push_back(createTree(Base));
+
+return AllTrees;
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(TreeTests, TreeTest,
+::testing::ValuesIn(allTestClangConfigs()), );
+
+TEST_P(TreeTest, FirstLeaf) {
+  buildTree("", GetParam());
+  std::vector Leafs = {createLeaf(*Arena, tok::l_paren),
+ createLeaf(*Arena, tok::r_paren)};
+  for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) {
+ASSERT_TRUE(Tree->findFirstLeaf() != nullptr);
+EXPECT_EQ(Tree->findFirstLeaf()->getToken()->kind(), tok::l_paren);
+  }
+}
+
+TEST_P(TreeTest, LastLeaf) {
+  buildTree("", GetParam());
+  std::vector Leafs = {createLeaf(*Arena, tok::l_paren),
+ createLeaf(*Arena, tok::r_paren)};
+  for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) {
+ASSERT_TRUE(Tree->findLastLeaf() != nullptr);
+EXPECT_EQ(Tree->findLastLeaf()->getToken()->kind(), tok::r_paren);
+  }
+}
+
+} // namespace
Index: 

[PATCH] D87779: [SyntaxTree] Test `findFirstLeaf` and `findLastLeaf`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293163.
eduucaldas marked 3 inline comments as done.
eduucaldas added a comment.

- Answer Review
- Change names in `generateAllTreesWithShape`
- `auto x = vector()` -> `vector x;`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87779/new/

https://reviews.llvm.org/D87779

Files:
  clang/lib/Tooling/Syntax/Tree.cpp
  clang/unittests/Tooling/Syntax/CMakeLists.txt
  clang/unittests/Tooling/Syntax/TreeTest.cpp

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -0,0 +1,123 @@
+//===- TreeTest.cpp -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Syntax/Tree.h"
+#include "TreeTestBase.h"
+#include "clang/Tooling/Syntax/BuildTree.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::syntax;
+
+namespace {
+
+class TreeTest : public SyntaxTreeTest {
+private:
+  Tree *createTree(ArrayRef Children) {
+std::vector> ChildrenWithRoles;
+ChildrenWithRoles.reserve(Children.size());
+for (const auto *Child : Children) {
+  ChildrenWithRoles.push_back(
+  std::make_pair(deepCopy(*Arena, Child), NodeRole::Unknown));
+}
+return clang::syntax::createTree(*Arena, ChildrenWithRoles,
+ NodeKind::UnknownExpression);
+  }
+
+  // Generate Forests by combining `Children` into `ParentCount` Trees.
+  //
+  // We do this recursively.
+  std::vector>
+  generateAllForests(ArrayRef Children, unsigned ParentCount) {
+assert(ParentCount > 0);
+// If there is only one Parent node, then combine `Children` under
+// this Parent.
+if (ParentCount == 1)
+  return {{createTree(Children)}};
+
+// Otherwise, combine `ChildrenCount` children under the last parent and
+// solve the smaller problem without these children and this parent. Do this
+// for every `ChildrenCount` and combine the results.
+std::vector> AllForests;
+for (unsigned ChildrenCount = 0; ChildrenCount <= Children.size();
+ ++ChildrenCount) {
+  auto *LastParent = createTree(Children.take_back(ChildrenCount));
+  for (auto  : generateAllForests(Children.drop_back(ChildrenCount),
+ ParentCount - 1)) {
+Forest.push_back(LastParent);
+AllForests.push_back(Forest);
+  }
+}
+return AllForests;
+  }
+
+protected:
+  // Generates all trees with a `Base` of `Node`s and `NodeCountPerLayer`
+  // `Node`s per layer. An example of Tree with `Base` = {`(`, `)`} and
+  // `NodeCountPerLayer` = {2, 2}:
+  //  Tree
+  //  |-Tree
+  //  `-Tree
+  //|-Tree
+  //| `-'('
+  //`-Tree
+  //  `-')'
+  std::vector
+  generateAllTreesWithShape(ArrayRef Base,
+ArrayRef NodeCountPerLayer) {
+auto GenerateNextLayer = [this](ArrayRef> Layer,
+unsigned NextLayerNodeCount) {
+  std::vector> NextLayer;
+  for (const auto  : Layer) {
+for (const auto  :
+ generateAllForests(Base, NextLayerNodeCount)) {
+  NextLayer.push_back(
+  std::vector(NextBase.begin(), NextBase.end()));
+}
+  }
+  return NextLayer;
+};
+
+std::vector> Layer = {Base};
+for (auto NodeCount : NodeCountPerLayer) {
+  Layer = GenerateNextLayer(Layer, NodeCount);
+}
+
+std::vector AllTrees;
+AllTrees.reserve(Layer.size());
+for (const auto  : Layer) {
+  AllTrees.push_back(createTree(Base));
+}
+return AllTrees;
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(TreeTests, TreeTest,
+::testing::ValuesIn(allTestClangConfigs()), );
+
+TEST_P(TreeTest, FirstLeaf) {
+  buildTree("", GetParam());
+  std::vector Leafs = {createLeaf(*Arena, tok::l_paren),
+ createLeaf(*Arena, tok::r_paren)};
+  for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) {
+ASSERT_TRUE(Tree->findFirstLeaf() != nullptr);
+EXPECT_EQ(Tree->findFirstLeaf()->getToken()->kind(), tok::l_paren);
+  }
+}
+
+TEST_P(TreeTest, LastLeaf) {
+  buildTree("", GetParam());
+  std::vector Leafs = {createLeaf(*Arena, tok::l_paren),
+ createLeaf(*Arena, tok::r_paren)};
+  for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) {
+ASSERT_TRUE(Tree->findLastLeaf() != nullptr);
+EXPECT_EQ(Tree->findLastLeaf()->getToken()->kind(), tok::r_paren);
+  }
+}
+
+} // namespace
Index: clang/unittests/Tooling/Syntax/CMakeLists.txt

[PATCH] D88004: [SyntaxTree][NFC] follow naming convention + remove auto on empty vector declaration

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG87f0b51d68de: [SyntaxTree][NFC] follow naming convention + 
remove auto on empty vector… (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88004/new/

https://reviews.llvm.org/D88004

Files:
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -153,7 +153,7 @@
 
 static void dumpNode(raw_ostream , const syntax::Node *N,
  const SourceManager , std::vector IndentMask) {
-  auto dumpExtraInfo = [](const syntax::Node *N) {
+  auto DumpExtraInfo = [](const syntax::Node *N) {
 if (N->getRole() != syntax::NodeRole::Unknown)
   OS << " " << N->getRole();
 if (!N->isOriginal())
@@ -167,14 +167,14 @@
 OS << "'";
 dumpLeaf(OS, L, SM);
 OS << "'";
-dumpExtraInfo(N);
+DumpExtraInfo(N);
 OS << "\n";
 return;
   }
 
   const auto *T = cast(N);
   OS << T->getKind();
-  dumpExtraInfo(N);
+  DumpExtraInfo(N);
   OS << "\n";
 
   for (const auto *It = T->getFirstChild(); It; It = It->getNextSibling()) {
@@ -302,20 +302,20 @@
   if (!getFirstChild())
 return {};
 
-  auto children = std::vector>();
-  syntax::Node *elementWithoutDelimiter = nullptr;
+  std::vector> Children;
+  syntax::Node *ElementWithoutDelimiter = nullptr;
   for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
 switch (C->getRole()) {
 case syntax::NodeRole::ListElement: {
-  if (elementWithoutDelimiter) {
-children.push_back({elementWithoutDelimiter, nullptr});
+  if (ElementWithoutDelimiter) {
+Children.push_back({ElementWithoutDelimiter, nullptr});
   }
-  elementWithoutDelimiter = C;
+  ElementWithoutDelimiter = C;
   break;
 }
 case syntax::NodeRole::ListDelimiter: {
-  children.push_back({elementWithoutDelimiter, cast(C)});
-  elementWithoutDelimiter = nullptr;
+  Children.push_back({ElementWithoutDelimiter, cast(C)});
+  ElementWithoutDelimiter = nullptr;
   break;
 }
 default:
@@ -326,19 +326,19 @@
 
   switch (getTerminationKind()) {
   case syntax::List::TerminationKind::Separated: {
-children.push_back({elementWithoutDelimiter, nullptr});
+Children.push_back({ElementWithoutDelimiter, nullptr});
 break;
   }
   case syntax::List::TerminationKind::Terminated:
   case syntax::List::TerminationKind::MaybeTerminated: {
-if (elementWithoutDelimiter) {
-  children.push_back({elementWithoutDelimiter, nullptr});
+if (ElementWithoutDelimiter) {
+  Children.push_back({ElementWithoutDelimiter, nullptr});
 }
 break;
   }
   }
 
-  return children;
+  return Children;
 }
 
 // Almost the same implementation of `getElementsAsNodesAndDelimiters` but
@@ -347,20 +347,20 @@
   if (!getFirstChild())
 return {};
 
-  auto children = std::vector();
-  syntax::Node *elementWithoutDelimiter = nullptr;
+  std::vector Children;
+  syntax::Node *ElementWithoutDelimiter = nullptr;
   for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
 switch (C->getRole()) {
 case syntax::NodeRole::ListElement: {
-  if (elementWithoutDelimiter) {
-children.push_back(elementWithoutDelimiter);
+  if (ElementWithoutDelimiter) {
+Children.push_back(ElementWithoutDelimiter);
   }
-  elementWithoutDelimiter = C;
+  ElementWithoutDelimiter = C;
   break;
 }
 case syntax::NodeRole::ListDelimiter: {
-  children.push_back(elementWithoutDelimiter);
-  elementWithoutDelimiter = nullptr;
+  Children.push_back(ElementWithoutDelimiter);
+  ElementWithoutDelimiter = nullptr;
   break;
 }
 default:
@@ -370,19 +370,19 @@
 
   switch (getTerminationKind()) {
   case syntax::List::TerminationKind::Separated: {
-children.push_back(elementWithoutDelimiter);
+Children.push_back(ElementWithoutDelimiter);
 break;
   }
   case syntax::List::TerminationKind::Terminated:
   case syntax::List::TerminationKind::MaybeTerminated: {
-if (elementWithoutDelimiter) {
-  children.push_back(elementWithoutDelimiter);
+if (ElementWithoutDelimiter) {
+  Children.push_back(ElementWithoutDelimiter);
 }
 break;
   }
   }
 
-  return children;
+  return Children;
 }
 
 clang::tok::TokenKind syntax::List::getDelimiterTokenKind() const {
Index: clang/lib/Tooling/Syntax/Nodes.cpp
===
--- clang/lib/Tooling/Syntax/Nodes.cpp
+++ clang/lib/Tooling/Syntax/Nodes.cpp
@@ -226,23 +226,23 @@
 // vector
 std::vector
 syntax::NestedNameSpecifier::getSpecifiers() {
-  auto specifiersAsNodes = getElementsAsNodes();
+  auto SpecifiersAsNodes = 

[PATCH] D87749: [SyntaxTree][Synthesis] Implement `deepCopy`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG4a5cc389c51d: [SyntaxTree][Synthesis] Implement `deepCopy` 
(authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87749/new/

https://reviews.llvm.org/D87749

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -163,6 +163,63 @@
   )txt"));
 }
 
+TEST_P(SynthesisTest, DeepCopy_Synthesized) {
+  buildTree("", GetParam());
+
+  auto *LeafContinue = createLeaf(*Arena, tok::kw_continue);
+  auto *LeafSemiColon = createLeaf(*Arena, tok::semi);
+  auto *StatementContinue = createTree(*Arena,
+   {{LeafContinue, NodeRole::LiteralToken},
+{LeafSemiColon, NodeRole::Unknown}},
+   NodeKind::ContinueStatement);
+
+  auto *Copy = deepCopy(*Arena, StatementContinue);
+  EXPECT_TRUE(
+  treeDumpEqual(Copy, StatementContinue->dump(Arena->getSourceManager(;
+  // FIXME: Test that copy is independent of original, once the Mutations API is
+  // more developed.
+}
+
+TEST_P(SynthesisTest, DeepCopy_Original) {
+  auto *OriginalTree = buildTree("int a;", GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree);
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+TranslationUnit Detached synthesized
+`-SimpleDeclaration synthesized
+  |-'int' synthesized
+  |-SimpleDeclarator Declarator synthesized
+  | `-'a' synthesized
+  `-';' synthesized
+  )txt"));
+}
+
+TEST_P(SynthesisTest, DeepCopy_Child) {
+  auto *OriginalTree = buildTree("int a;", GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree->getFirstChild());
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+SimpleDeclaration Detached synthesized
+|-'int' synthesized
+|-SimpleDeclarator Declarator synthesized
+| `-'a' synthesized
+`-';' synthesized
+  )txt"));
+}
+
+TEST_P(SynthesisTest, DeepCopy_Macro) {
+  auto *OriginalTree = buildTree(R"cpp(
+#define HALF_IF if (1+
+#define HALF_IF_2 1) {}
+void test() {
+  HALF_IF HALF_IF_2 else {}
+})cpp",
+ GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree);
+  EXPECT_TRUE(Copy == nullptr);
+}
+
 TEST_P(SynthesisTest, Statement_EmptyStatement) {
   buildTree("", GetParam());
 
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -28,10 +28,12 @@
   }
 };
 
+// FIXME: `createLeaf` is based on `syntax::tokenize` internally, as such it
+// doesn't support digraphs or line continuations.
 syntax::Leaf *clang::syntax::createLeaf(syntax::Arena , tok::TokenKind K,
 StringRef Spelling) {
   auto Tokens =
-  FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBuffer(Spelling))
+  FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBufferCopy(Spelling))
   .second;
   assert(Tokens.size() == 1);
   assert(Tokens.front().kind() == K &&
@@ -192,14 +194,52 @@
 syntax::NodeKind K) {
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
-  for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend();
-   std::advance(ChildIt, 1))
+  for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend(); ++ChildIt)
 FactoryImpl::prependChildLowLevel(T, ChildIt->first, ChildIt->second);
 
   T->assertInvariants();
   return T;
 }
 
+namespace {
+bool canModifyAllDescendants(const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return L->canModify();
+
+  const auto *T = cast(N);
+
+  if (!T->canModify())
+return false;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+if (!canModifyAllDescendants(Child))
+  return false;
+
+  return true;
+}
+
+syntax::Node *deepCopyImpl(syntax::Arena , const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return createLeaf(A, L->getToken()->kind(),
+  L->getToken()->text(A.getSourceManager()));
+
+  const auto *T = cast(N);
+  std::vector> Children;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+Children.push_back({deepCopyImpl(A, Child), Child->getRole()});
+
+  return createTree(A, Children, N->getKind());
+}
+} // namespace
+
+syntax::Node *clang::syntax::deepCopy(syntax::Arena , const Node *N) {
+  if (!canModifyAllDescendants(N))
+return nullptr;
+
+  return deepCopyImpl(A, N);
+}
+
 syntax::EmptyStatement 

[PATCH] D87749: [SyntaxTree][Synthesis] Implement `deepCopy`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293101.
eduucaldas added a comment.

.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87749/new/

https://reviews.llvm.org/D87749

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -163,6 +163,63 @@
   )txt"));
 }
 
+TEST_P(SynthesisTest, DeepCopy_Synthesized) {
+  buildTree("", GetParam());
+
+  auto *LeafContinue = createLeaf(*Arena, tok::kw_continue);
+  auto *LeafSemiColon = createLeaf(*Arena, tok::semi);
+  auto *StatementContinue = createTree(*Arena,
+   {{LeafContinue, NodeRole::LiteralToken},
+{LeafSemiColon, NodeRole::Unknown}},
+   NodeKind::ContinueStatement);
+
+  auto *Copy = deepCopy(*Arena, StatementContinue);
+  EXPECT_TRUE(
+  treeDumpEqual(Copy, StatementContinue->dump(Arena->getSourceManager(;
+  // FIXME: Test that copy is independent of original, once the Mutations API is
+  // more developed.
+}
+
+TEST_P(SynthesisTest, DeepCopy_Original) {
+  auto *OriginalTree = buildTree("int a;", GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree);
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+TranslationUnit Detached synthesized
+`-SimpleDeclaration synthesized
+  |-'int' synthesized
+  |-SimpleDeclarator Declarator synthesized
+  | `-'a' synthesized
+  `-';' synthesized
+  )txt"));
+}
+
+TEST_P(SynthesisTest, DeepCopy_Child) {
+  auto *OriginalTree = buildTree("int a;", GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree->getFirstChild());
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+SimpleDeclaration Detached synthesized
+|-'int' synthesized
+|-SimpleDeclarator Declarator synthesized
+| `-'a' synthesized
+`-';' synthesized
+  )txt"));
+}
+
+TEST_P(SynthesisTest, DeepCopy_Macro) {
+  auto *OriginalTree = buildTree(R"cpp(
+#define HALF_IF if (1+
+#define HALF_IF_2 1) {}
+void test() {
+  HALF_IF HALF_IF_2 else {}
+})cpp",
+ GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree);
+  EXPECT_TRUE(Copy == nullptr);
+}
+
 TEST_P(SynthesisTest, Statement_EmptyStatement) {
   buildTree("", GetParam());
 
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -28,10 +28,12 @@
   }
 };
 
+// FIXME: `createLeaf` is based on `syntax::tokenize` internally, as such it
+// doesn't support digraphs or line continuations.
 syntax::Leaf *clang::syntax::createLeaf(syntax::Arena , tok::TokenKind K,
 StringRef Spelling) {
   auto Tokens =
-  FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBuffer(Spelling))
+  FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBufferCopy(Spelling))
   .second;
   assert(Tokens.size() == 1);
   assert(Tokens.front().kind() == K &&
@@ -192,14 +194,52 @@
 syntax::NodeKind K) {
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
-  for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend();
-   std::advance(ChildIt, 1))
+  for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend(); ++ChildIt)
 FactoryImpl::prependChildLowLevel(T, ChildIt->first, ChildIt->second);
 
   T->assertInvariants();
   return T;
 }
 
+namespace {
+bool canModifyAllDescendants(const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return L->canModify();
+
+  const auto *T = cast(N);
+
+  if (!T->canModify())
+return false;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+if (!canModifyAllDescendants(Child))
+  return false;
+
+  return true;
+}
+
+syntax::Node *deepCopyImpl(syntax::Arena , const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return createLeaf(A, L->getToken()->kind(),
+  L->getToken()->text(A.getSourceManager()));
+
+  const auto *T = cast(N);
+  std::vector> Children;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+Children.push_back({deepCopyImpl(A, Child), Child->getRole()});
+
+  return createTree(A, Children, N->getKind());
+}
+} // namespace
+
+syntax::Node *clang::syntax::deepCopy(syntax::Arena , const Node *N) {
+  if (!canModifyAllDescendants(N))
+return nullptr;
+
+  return deepCopyImpl(A, N);
+}
+
 syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena ) {
   return cast(
   createTree(A, {{createLeaf(A, tok::semi), NodeRole::Unknown}},
Index: 

[PATCH] D88004: [SyntaxTree][NFC] follow naming convention + remove auto on empty vector declaration

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88004

Files:
  clang/lib/Tooling/Syntax/BuildTree.cpp
  clang/lib/Tooling/Syntax/Nodes.cpp
  clang/lib/Tooling/Syntax/Tree.cpp

Index: clang/lib/Tooling/Syntax/Tree.cpp
===
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -153,7 +153,7 @@
 
 static void dumpNode(raw_ostream , const syntax::Node *N,
  const SourceManager , std::vector IndentMask) {
-  auto dumpExtraInfo = [](const syntax::Node *N) {
+  auto DumpExtraInfo = [](const syntax::Node *N) {
 if (N->getRole() != syntax::NodeRole::Unknown)
   OS << " " << N->getRole();
 if (!N->isOriginal())
@@ -167,14 +167,14 @@
 OS << "'";
 dumpLeaf(OS, L, SM);
 OS << "'";
-dumpExtraInfo(N);
+DumpExtraInfo(N);
 OS << "\n";
 return;
   }
 
   const auto *T = cast(N);
   OS << T->getKind();
-  dumpExtraInfo(N);
+  DumpExtraInfo(N);
   OS << "\n";
 
   for (const auto *It = T->getFirstChild(); It; It = It->getNextSibling()) {
@@ -302,20 +302,20 @@
   if (!getFirstChild())
 return {};
 
-  auto children = std::vector>();
-  syntax::Node *elementWithoutDelimiter = nullptr;
+  std::vector> Children;
+  syntax::Node *ElementWithoutDelimiter = nullptr;
   for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
 switch (C->getRole()) {
 case syntax::NodeRole::ListElement: {
-  if (elementWithoutDelimiter) {
-children.push_back({elementWithoutDelimiter, nullptr});
+  if (ElementWithoutDelimiter) {
+Children.push_back({ElementWithoutDelimiter, nullptr});
   }
-  elementWithoutDelimiter = C;
+  ElementWithoutDelimiter = C;
   break;
 }
 case syntax::NodeRole::ListDelimiter: {
-  children.push_back({elementWithoutDelimiter, cast(C)});
-  elementWithoutDelimiter = nullptr;
+  Children.push_back({ElementWithoutDelimiter, cast(C)});
+  ElementWithoutDelimiter = nullptr;
   break;
 }
 default:
@@ -326,19 +326,19 @@
 
   switch (getTerminationKind()) {
   case syntax::List::TerminationKind::Separated: {
-children.push_back({elementWithoutDelimiter, nullptr});
+Children.push_back({ElementWithoutDelimiter, nullptr});
 break;
   }
   case syntax::List::TerminationKind::Terminated:
   case syntax::List::TerminationKind::MaybeTerminated: {
-if (elementWithoutDelimiter) {
-  children.push_back({elementWithoutDelimiter, nullptr});
+if (ElementWithoutDelimiter) {
+  Children.push_back({ElementWithoutDelimiter, nullptr});
 }
 break;
   }
   }
 
-  return children;
+  return Children;
 }
 
 // Almost the same implementation of `getElementsAsNodesAndDelimiters` but
@@ -347,20 +347,20 @@
   if (!getFirstChild())
 return {};
 
-  auto children = std::vector();
-  syntax::Node *elementWithoutDelimiter = nullptr;
+  std::vector Children;
+  syntax::Node *ElementWithoutDelimiter = nullptr;
   for (auto *C = getFirstChild(); C; C = C->getNextSibling()) {
 switch (C->getRole()) {
 case syntax::NodeRole::ListElement: {
-  if (elementWithoutDelimiter) {
-children.push_back(elementWithoutDelimiter);
+  if (ElementWithoutDelimiter) {
+Children.push_back(ElementWithoutDelimiter);
   }
-  elementWithoutDelimiter = C;
+  ElementWithoutDelimiter = C;
   break;
 }
 case syntax::NodeRole::ListDelimiter: {
-  children.push_back(elementWithoutDelimiter);
-  elementWithoutDelimiter = nullptr;
+  Children.push_back(ElementWithoutDelimiter);
+  ElementWithoutDelimiter = nullptr;
   break;
 }
 default:
@@ -370,19 +370,19 @@
 
   switch (getTerminationKind()) {
   case syntax::List::TerminationKind::Separated: {
-children.push_back(elementWithoutDelimiter);
+Children.push_back(ElementWithoutDelimiter);
 break;
   }
   case syntax::List::TerminationKind::Terminated:
   case syntax::List::TerminationKind::MaybeTerminated: {
-if (elementWithoutDelimiter) {
-  children.push_back(elementWithoutDelimiter);
+if (ElementWithoutDelimiter) {
+  Children.push_back(ElementWithoutDelimiter);
 }
 break;
   }
   }
 
-  return children;
+  return Children;
 }
 
 clang::tok::TokenKind syntax::List::getDelimiterTokenKind() const {
Index: clang/lib/Tooling/Syntax/Nodes.cpp
===
--- clang/lib/Tooling/Syntax/Nodes.cpp
+++ clang/lib/Tooling/Syntax/Nodes.cpp
@@ -226,23 +226,23 @@
 // vector
 std::vector
 syntax::NestedNameSpecifier::getSpecifiers() {
-  auto specifiersAsNodes = getElementsAsNodes();
+  auto SpecifiersAsNodes = getElementsAsNodes();
   std::vector Children;
-  for (const auto  : specifiersAsNodes) {
-Children.push_back(llvm::cast(element));
+ 

[PATCH] D87749: [SyntaxTree][Synthesis] Implement `deepCopy`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293089.
eduucaldas marked 3 inline comments as done.
eduucaldas added a comment.

Fix canModifyAllDescendants, add tests for it


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87749/new/

https://reviews.llvm.org/D87749

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -163,6 +163,63 @@
   )txt"));
 }
 
+TEST_P(SynthesisTest, DeepCopy_Synthesized) {
+  buildTree("", GetParam());
+
+  auto *LeafContinue = createLeaf(*Arena, tok::kw_continue);
+  auto *LeafSemiColon = createLeaf(*Arena, tok::semi);
+  auto *StatementContinue = createTree(*Arena,
+   {{LeafContinue, NodeRole::LiteralToken},
+{LeafSemiColon, NodeRole::Unknown}},
+   NodeKind::ContinueStatement);
+
+  auto *Copy = deepCopy(*Arena, StatementContinue);
+  EXPECT_TRUE(
+  treeDumpEqual(Copy, StatementContinue->dump(Arena->getSourceManager(;
+  // FIXME: Test that copy is independent of original, once the Mutations API is
+  // more developed.
+}
+
+TEST_P(SynthesisTest, DeepCopy_Original) {
+  auto *OriginalTree = buildTree("int a;", GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree);
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+TranslationUnit Detached synthesized
+`-SimpleDeclaration synthesized
+  |-'int' synthesized
+  |-SimpleDeclarator Declarator synthesized
+  | `-'a' synthesized
+  `-';' synthesized
+  )txt"));
+}
+
+TEST_P(SynthesisTest, DeepCopy_Child) {
+  auto *OriginalTree = buildTree("int a;", GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree->getFirstChild());
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+SimpleDeclaration Detached synthesized
+|-'int' synthesized
+|-SimpleDeclarator Declarator synthesized
+| `-'a' synthesized
+`-';' synthesized
+  )txt"));
+}
+
+TEST_P(SynthesisTest, DeepCopy_Macro) {
+  auto *OriginalTree = buildTree(R"cpp(
+#define HALF_IF if (1+
+#define HALF_IF_2 1) {}
+void test() {
+  HALF_IF HALF_IF_2 else {}
+})cpp",
+ GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree);
+  EXPECT_TRUE(Copy == nullptr);
+}
+
 TEST_P(SynthesisTest, Statement_EmptyStatement) {
   buildTree("", GetParam());
 
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -28,10 +28,12 @@
   }
 };
 
+// FIXME: `createLeaf` is based on `syntax::tokenize` internally, as such it
+// doesn't support digraphs or line continuations.
 syntax::Leaf *clang::syntax::createLeaf(syntax::Arena , tok::TokenKind K,
 StringRef Spelling) {
   auto Tokens =
-  FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBuffer(Spelling))
+  FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBufferCopy(Spelling))
   .second;
   assert(Tokens.size() == 1);
   assert(Tokens.front().kind() == K &&
@@ -184,6 +186,7 @@
   }
   llvm_unreachable("unknown node kind");
 }
+
 } // namespace
 
 syntax::Tree *clang::syntax::createTree(
@@ -192,14 +195,52 @@
 syntax::NodeKind K) {
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
-  for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend();
-   std::advance(ChildIt, 1))
+  for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend(); ++ChildIt)
 FactoryImpl::prependChildLowLevel(T, ChildIt->first, ChildIt->second);
 
   T->assertInvariants();
   return T;
 }
 
+namespace {
+bool canModifyAllDescendants(const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return L->canModify();
+
+  const auto *T = cast(N);
+
+  if (!T->canModify())
+return false;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+if (!canModifyAllDescendants(Child))
+  return false;
+
+  return true;
+}
+
+syntax::Node *deepCopyImpl(syntax::Arena , const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return createLeaf(A, L->getToken()->kind(),
+  L->getToken()->text(A.getSourceManager()));
+
+  const auto *T = cast(N);
+  std::vector> Children;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+Children.push_back({deepCopyImpl(A, Child), Child->getRole()});
+
+  return createTree(A, Children, N->getKind());
+}
+} // namespace
+
+syntax::Node *clang::syntax::deepCopy(syntax::Arena , const Node *N) {
+  if (!canModifyAllDescendants(N))
+return nullptr;
+
+  return 

[PATCH] D87749: [SyntaxTree][Synthesis] Implement `deepCopy`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas added inline comments.



Comment at: clang/lib/Tooling/Syntax/Synthesis.cpp:237
+
+syntax::Node *clang::syntax::deepCopy(syntax::Arena , const Node *N) {
+  if (!canModifyAllDescendants(N))

We are ignoring nullability of pointers.

The casting machinery asserts that on `dyn_cast` we don't have `nullptr`. So 
this code crashes on `nullptr`. 

From what I understand `dyn_cast` et al. are intended to be used on pointers. 
Are there other alternatives to this approach? I would like to encode the 
non-nullability in types instead of in asserts



Comment at: clang/lib/Tooling/Syntax/Synthesis.cpp:205
+  if (L->canModify())
+syntax::FactoryImpl::setCanModify(Leaf);
+

gribozavr2 wrote:
> Since we are creating new leaves, why prohibit their mutation sometimes?
> 
> I also don't quite understand the implications of having multiple leaves in a 
> tree that are backed by the same token. I think the algorithm that produces 
> edits can be confused by that.
> 
> If you agree, please change the implementation to use `createLeaf` (or call 
> it directly from `deepCopy`).
> Since we are creating new leaves, why prohibit their mutation sometimes?

The `canModify` says whether the leaf is backed by a macro.

Since the tokens coming from macros are expanded they would be expanded in the 
deepCopy as well. We agreed that this shouldn't be the default behaviour. We 
can have `deepCopyExpandingMacros`, if the user wants this behaviour, but I 
think `deepCopy` should simply forbid macros.

WDYT about `deepCopyExpandingMacros`?

> having multiple leaves in a tree that are backed by the same token

We think the current algorithm wouldn't be confused by that.
However it's easier to reason about `Leaf`s each with their `Token`, and we 
don't think creating additional `Leaf` nodes would affect performance largely.

So we'll call `createLeaf` instead to create a fresh Leaf with the same Token 
as the previous one.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87749/new/

https://reviews.llvm.org/D87749

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


[PATCH] D87749: [SyntaxTree][Synthesis] Implement `deepCopy`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 293085.
eduucaldas marked 3 inline comments as done.
eduucaldas added a comment.

- `deepCopy` returns `nullptr` if copy code with Macro expansions.
- `deepCopy` also deep copies the Tokens.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87749/new/

https://reviews.llvm.org/D87749

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -163,6 +163,50 @@
   )txt"));
 }
 
+TEST_P(SynthesisTest, DeepCopy_Synthesized) {
+  buildTree("", GetParam());
+
+  auto *LeafContinue = createLeaf(*Arena, tok::kw_continue);
+  auto *LeafSemiColon = createLeaf(*Arena, tok::semi);
+  auto *StatementContinue = createTree(*Arena,
+   {{LeafContinue, NodeRole::LiteralToken},
+{LeafSemiColon, NodeRole::Unknown}},
+   NodeKind::ContinueStatement);
+
+  auto *Copy = deepCopy(*Arena, StatementContinue);
+  EXPECT_TRUE(
+  treeDumpEqual(Copy, StatementContinue->dump(Arena->getSourceManager(;
+  // FIXME: Test that copy is independent of original, once the Mutations API is
+  // more developed.
+}
+
+TEST_P(SynthesisTest, DeepCopy_Original) {
+  auto *OriginalTree = buildTree("int a;", GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree);
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+TranslationUnit Detached synthesized
+`-SimpleDeclaration synthesized
+  |-'int' synthesized
+  |-SimpleDeclarator Declarator synthesized
+  | `-'a' synthesized
+  `-';' synthesized
+  )txt"));
+}
+
+TEST_P(SynthesisTest, DeepCopy_Child) {
+  auto *OriginalTree = buildTree("int a;", GetParam());
+
+  auto *Copy = deepCopy(*Arena, OriginalTree->getFirstChild());
+  EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
+SimpleDeclaration Detached synthesized
+|-'int' synthesized
+|-SimpleDeclarator Declarator synthesized
+| `-'a' synthesized
+`-';' synthesized
+  )txt"));
+}
+
 TEST_P(SynthesisTest, Statement_EmptyStatement) {
   buildTree("", GetParam());
 
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -28,10 +28,12 @@
   }
 };
 
+// FIXME: `createLeaf` is based on `syntax::tokenize` internally, as such it
+// doesn't support digraphs or line continuations.
 syntax::Leaf *clang::syntax::createLeaf(syntax::Arena , tok::TokenKind K,
 StringRef Spelling) {
   auto Tokens =
-  FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBuffer(Spelling))
+  FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBufferCopy(Spelling))
   .second;
   assert(Tokens.size() == 1);
   assert(Tokens.front().kind() == K &&
@@ -184,6 +186,7 @@
   }
   llvm_unreachable("unknown node kind");
 }
+
 } // namespace
 
 syntax::Tree *clang::syntax::createTree(
@@ -192,14 +195,52 @@
 syntax::NodeKind K) {
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
-  for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend();
-   std::advance(ChildIt, 1))
+  for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend(); ++ChildIt)
 FactoryImpl::prependChildLowLevel(T, ChildIt->first, ChildIt->second);
 
   T->assertInvariants();
   return T;
 }
 
+namespace {
+bool canModifyAllDescendants(const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return L->canModify();
+
+  const auto *T = cast(N);
+
+  if (!T->canModify())
+return false;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+if (!Child->canModify())
+  return false;
+
+  return true;
+}
+
+syntax::Node *deepCopyImpl(syntax::Arena , const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return createLeaf(A, L->getToken()->kind(),
+  L->getToken()->text(A.getSourceManager()));
+
+  const auto *T = cast(N);
+  auto Children = std::vector>();
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+Children.push_back({deepCopy(A, Child), Child->getRole()});
+
+  return createTree(A, Children, N->getKind());
+}
+} // namespace
+
+syntax::Node *clang::syntax::deepCopy(syntax::Arena , const Node *N) {
+  if (!canModifyAllDescendants(N))
+return nullptr;
+
+  return deepCopyImpl(A, N);
+}
+
 syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena ) {
   return cast(
   createTree(A, {{createLeaf(A, tok::semi), NodeRole::Unknown}},
Index: clang/include/clang/Tooling/Syntax/BuildTree.h

[PATCH] D87895: [SyntaxTree] Test for '\' inside token.

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGe616a4259889: [SyntaxTree] Test for \ inside 
token. (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87895/new/

https://reviews.llvm.org/D87895

Files:
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp


Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -167,6 +167,23 @@
 )txt"));
 }
 
+TEST_P(BuildSyntaxTreeTest, Simple_BackslashInsideToken) {
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+in\
+t a;
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'in\
+t'
+  |-SimpleDeclarator Declarator
+  | `-'a'
+  `-';'
+)txt"));
+}
+
 TEST_P(BuildSyntaxTreeTest, If) {
   EXPECT_TRUE(treeDumpEqualOnAnnotations(
   R"cpp(


Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -167,6 +167,23 @@
 )txt"));
 }
 
+TEST_P(BuildSyntaxTreeTest, Simple_BackslashInsideToken) {
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+in\
+t a;
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'in\
+t'
+  |-SimpleDeclarator Declarator
+  | `-'a'
+  `-';'
+)txt"));
+}
+
 TEST_P(BuildSyntaxTreeTest, If) {
   EXPECT_TRUE(treeDumpEqualOnAnnotations(
   R"cpp(
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D87896: [SyntaxTree][Synthesis] Improve testing `createLeaf`

2020-09-21 Thread Eduardo Caldas via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGbb5b28f12fbd: [SyntaxTree][Synthesis] Improve testing 
`createLeaf` (authored by eduucaldas).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87896/new/

https://reviews.llvm.org/D87896

Files:
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp


Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -51,6 +51,19 @@
   )txt"));
 }
 
+TEST_P(SynthesisTest, Leaf_Punctuation_CXX) {
+  if (!GetParam().isCXX())
+return;
+
+  buildTree("", GetParam());
+
+  auto *Leaf = createLeaf(*Arena, tok::coloncolon);
+
+  EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
+'::' Detached synthesized
+  )txt"));
+}
+
 TEST_P(SynthesisTest, Leaf_Keyword) {
   buildTree("", GetParam());
 
@@ -61,6 +74,19 @@
   )txt"));
 }
 
+TEST_P(SynthesisTest, Leaf_Keyword_CXX11) {
+  if (!GetParam().isCXX11OrLater())
+return;
+
+  buildTree("", GetParam());
+
+  auto *Leaf = createLeaf(*Arena, tok::kw_nullptr);
+
+  EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
+'nullptr' Detached synthesized
+  )txt"));
+}
+
 TEST_P(SynthesisTest, Leaf_Identifier) {
   buildTree("", GetParam());
 


Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ clang/unittests/Tooling/Syntax/SynthesisTest.cpp
@@ -51,6 +51,19 @@
   )txt"));
 }
 
+TEST_P(SynthesisTest, Leaf_Punctuation_CXX) {
+  if (!GetParam().isCXX())
+return;
+
+  buildTree("", GetParam());
+
+  auto *Leaf = createLeaf(*Arena, tok::coloncolon);
+
+  EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
+'::' Detached synthesized
+  )txt"));
+}
+
 TEST_P(SynthesisTest, Leaf_Keyword) {
   buildTree("", GetParam());
 
@@ -61,6 +74,19 @@
   )txt"));
 }
 
+TEST_P(SynthesisTest, Leaf_Keyword_CXX11) {
+  if (!GetParam().isCXX11OrLater())
+return;
+
+  buildTree("", GetParam());
+
+  auto *Leaf = createLeaf(*Arena, tok::kw_nullptr);
+
+  EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
+'nullptr' Detached synthesized
+  )txt"));
+}
+
 TEST_P(SynthesisTest, Leaf_Identifier) {
   buildTree("", GetParam());
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D87925: [Synthesis] Fix: `createTree` only from children that are not backed by source code

2020-09-18 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D87925

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp


Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -8,6 +8,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
 #include "clang/Tooling/Syntax/Tree.h"
+#include 
 
 using namespace clang;
 
@@ -184,12 +185,48 @@
   }
   llvm_unreachable("unknown node kind");
 }
+
+bool canModifyAllDescendants(const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return L->canModify();
+
+  const auto *T = cast(N);
+
+  if (!T->canModify())
+return false;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+if (!Child->canModify())
+  return false;
+
+  return true;
+}
+
+bool areAllSynthesized(const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return !L->isOriginal();
+
+  const auto *T = cast(N);
+
+  if (T->isOriginal())
+return false;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+if (Child->isOriginal())
+  return false;
+
+  return true;
+}
 } // namespace
 
 syntax::Tree *clang::syntax::createTree(
 syntax::Arena ,
 std::vector> Children,
 syntax::NodeKind K) {
+  if (std::all_of(Children.begin(), Children.end(),
+  [](auto p) { return areAllSynthesized(p.first); }))
+return nullptr;
+
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
   for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend();
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ clang/include/clang/Tooling/Syntax/BuildTree.h
@@ -37,6 +37,9 @@
 // Synthesis of Trees
 /// Creates the concrete syntax node according to the specified `NodeKind` `K`.
 /// Returns it as a pointer to the base class `Tree`.
+///
+/// EXPECT: Nodes in `Children` are all synthesized, i.e. not backed by source
+/// code.
 syntax::Tree *
 createTree(syntax::Arena ,
std::vector> Children,


Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ clang/lib/Tooling/Syntax/Synthesis.cpp
@@ -8,6 +8,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Tooling/Syntax/BuildTree.h"
 #include "clang/Tooling/Syntax/Tree.h"
+#include 
 
 using namespace clang;
 
@@ -184,12 +185,48 @@
   }
   llvm_unreachable("unknown node kind");
 }
+
+bool canModifyAllDescendants(const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return L->canModify();
+
+  const auto *T = cast(N);
+
+  if (!T->canModify())
+return false;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+if (!Child->canModify())
+  return false;
+
+  return true;
+}
+
+bool areAllSynthesized(const syntax::Node *N) {
+  if (const auto *L = dyn_cast(N))
+return !L->isOriginal();
+
+  const auto *T = cast(N);
+
+  if (T->isOriginal())
+return false;
+  for (const auto *Child = T->getFirstChild(); Child;
+   Child = Child->getNextSibling())
+if (Child->isOriginal())
+  return false;
+
+  return true;
+}
 } // namespace
 
 syntax::Tree *clang::syntax::createTree(
 syntax::Arena ,
 std::vector> Children,
 syntax::NodeKind K) {
+  if (std::all_of(Children.begin(), Children.end(),
+  [](auto p) { return areAllSynthesized(p.first); }))
+return nullptr;
+
   auto *T = allocateTree(A, K);
   FactoryImpl::setCanModify(T);
   for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend();
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ clang/include/clang/Tooling/Syntax/BuildTree.h
@@ -37,6 +37,9 @@
 // Synthesis of Trees
 /// Creates the concrete syntax node according to the specified `NodeKind` `K`.
 /// Returns it as a pointer to the base class `Tree`.
+///
+/// EXPECT: Nodes in `Children` are all synthesized, i.e. not backed by source
+/// code.
 syntax::Tree *
 createTree(syntax::Arena ,
std::vector> Children,
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D87895: [SyntaxTree] Test for '\' inside token.

2020-09-18 Thread Eduardo Caldas via Phabricator via cfe-commits
eduucaldas updated this revision to Diff 292832.
eduucaldas added a comment.

Fix typo


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87895/new/

https://reviews.llvm.org/D87895

Files:
  clang/unittests/Tooling/Syntax/BuildTreeTest.cpp


Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -167,6 +167,23 @@
 )txt"));
 }
 
+TEST_P(BuildSyntaxTreeTest, Simple_BackslashInsideToken) {
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+in\
+t a;
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'in\
+t'
+  |-SimpleDeclarator Declarator
+  | `-'a'
+  `-';'
+)txt"));
+}
+
 TEST_P(BuildSyntaxTreeTest, If) {
   EXPECT_TRUE(treeDumpEqualOnAnnotations(
   R"cpp(


Index: clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
===
--- clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
+++ clang/unittests/Tooling/Syntax/BuildTreeTest.cpp
@@ -167,6 +167,23 @@
 )txt"));
 }
 
+TEST_P(BuildSyntaxTreeTest, Simple_BackslashInsideToken) {
+  EXPECT_TRUE(treeDumpEqual(
+  R"cpp(
+in\
+t a;
+)cpp",
+  R"txt(
+TranslationUnit Detached
+`-SimpleDeclaration
+  |-'in\
+t'
+  |-SimpleDeclarator Declarator
+  | `-'a'
+  `-';'
+)txt"));
+}
+
 TEST_P(BuildSyntaxTreeTest, If) {
   EXPECT_TRUE(treeDumpEqualOnAnnotations(
   R"cpp(
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


  1   2   3   4   5   >