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<Node> ED) {
+    std::string Storage;
+    llvm::raw_string_ostream OS(Storage);
+    OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
+       << dumpQuotedTokensOrNull(ED.delimiter) << ")";
+    return OS.str();
+  }
+
   std::string
   dumpElementsAndDelimiters(ArrayRef<List::ElementAndDelimiter<Node>> EDs) {
     std::string Storage;
@@ -145,8 +154,7 @@
 
     llvm::interleaveComma(
         EDs, OS, [&OS, this](const List::ElementAndDelimiter<Node> &ED) {
-          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::NestedNameSpecifier>(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::NestedNameSpecifier>(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::CallArguments>(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::CallArguments>(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));
+
+  // 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());
+}
 } // namespace
Index: clang/lib/Tooling/Syntax/Tree.cpp
===================================================================
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ clang/lib/Tooling/Syntax/Tree.cpp
@@ -311,92 +311,60 @@
   }
 }
 
+syntax::List::ElementAndDelimiterIterator<syntax::Node>
+syntax::List::getElementsAsNodesAndDelimitersBeforeBegin() {
+  return syntax::List::ElementAndDelimiterIterator<syntax::Node>::beforeBegin(
+      this);
+}
+syntax::List::ElementAndDelimiterIterator<syntax::Node>
+syntax::List::getElementsAsNodesAndDelimitersBegin() {
+  return syntax::List::ElementAndDelimiterIterator<syntax::Node>::begin(this);
+}
+syntax::List::ElementAndDelimiterIterator<syntax::Node>
+syntax::List::getElementsAsNodesAndDelimitersEnd() {
+  return ElementAndDelimiterIterator<syntax::Node>::end(this);
+}
+
+// Returns the elements of the list, and their corresponding delimiters.
+// Missing elements are represented as null pointers.
 std::vector<syntax::List::ElementAndDelimiter<syntax::Node>>
 syntax::List::getElementsAsNodesAndDelimiters() {
-  if (!getFirstChild())
-    return {};
-
-  std::vector<syntax::List::ElementAndDelimiter<Node>> 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<syntax::Leaf>(C)});
-      ElementWithoutDelimiter = nullptr;
-      break;
-    }
-    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});
-    }
-    break;
-  }
-  }
-
-  return Children;
+  return getElementsAndDelimiters<syntax::Node>();
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
+/// Returns the elements of the list. Missing elements are represented
+/// as null pointers in the same way as in the return value of
+/// `getElementsAndDelimiters()`.
 std::vector<syntax::Node *> syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-    return {};
+  return getElements<syntax::Node>();
+}
 
-  std::vector<syntax::Node *> 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.");
-    }
+/// Expects a List child.
+bool syntax::List::isElement(syntax::Node *N) {
+  assert(N);
+  assert(isa<syntax::List>(N->getParent()));
+  switch (N->getRole()) {
+  case NodeRole::ListElement:
+    return true;
+  case NodeRole::ListDelimiter:
+    return false;
+  default:
+    llvm_unreachable("List children can only be elements or delimiters.");
   }
+}
 
-  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;
-  }
+/// Expects a List child.
+bool syntax::List::isDelimiter(syntax::Node *N) {
+  assert(N);
+  assert(isa<syntax::List>(N->getParent()));
+  switch (N->getRole()) {
+  case NodeRole::ListElement:
+    return false;
+  case NodeRole::ListDelimiter:
+    return true;
+  default:
+    llvm_unreachable("List children can only be elements or delimiters.");
   }
-
-  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
@@ -230,92 +230,41 @@
 // vector
 std::vector<syntax::NameSpecifier *>
 syntax::NestedNameSpecifier::getSpecifiers() {
-  auto SpecifiersAsNodes = getElementsAsNodes();
-  std::vector<syntax::NameSpecifier *> Children;
-  for (const auto &Element : SpecifiersAsNodes) {
-    Children.push_back(llvm::cast<syntax::NameSpecifier>(Element));
-  }
-  return Children;
+  return getElements<NameSpecifier>();
 }
 
 std::vector<syntax::List::ElementAndDelimiter<syntax::NameSpecifier>>
 syntax::NestedNameSpecifier::getSpecifiersAndDoubleColons() {
-  auto SpecifiersAsNodesAndDoubleColons = getElementsAsNodesAndDelimiters();
-  std::vector<syntax::List::ElementAndDelimiter<syntax::NameSpecifier>>
-      Children;
-  for (const auto &SpecifierAndDoubleColon : SpecifiersAsNodesAndDoubleColons) {
-    Children.push_back(
-        {llvm::cast<syntax::NameSpecifier>(SpecifierAndDoubleColon.element),
-         SpecifierAndDoubleColon.delimiter});
-  }
-  return Children;
+  return getElementsAndDelimiters<NameSpecifier>();
 }
 
 std::vector<syntax::Expression *> syntax::CallArguments::getArguments() {
-  auto ArgumentsAsNodes = getElementsAsNodes();
-  std::vector<syntax::Expression *> Children;
-  for (const auto &ArgumentAsNode : ArgumentsAsNodes) {
-    Children.push_back(llvm::cast<syntax::Expression>(ArgumentAsNode));
-  }
-  return Children;
+  return getElements<Expression>();
 }
 
 std::vector<syntax::List::ElementAndDelimiter<syntax::Expression>>
 syntax::CallArguments::getArgumentsAndCommas() {
-  auto ArgumentsAsNodesAndCommas = getElementsAsNodesAndDelimiters();
-  std::vector<syntax::List::ElementAndDelimiter<syntax::Expression>> Children;
-  for (const auto &ArgumentAsNodeAndComma : ArgumentsAsNodesAndCommas) {
-    Children.push_back(
-        {llvm::cast<syntax::Expression>(ArgumentAsNodeAndComma.element),
-         ArgumentAsNodeAndComma.delimiter});
-  }
-  return Children;
+  return getElementsAndDelimiters<Expression>();
 }
 
 std::vector<syntax::SimpleDeclaration *>
 syntax::ParameterDeclarationList::getParameterDeclarations() {
-  auto ParametersAsNodes = getElementsAsNodes();
-  std::vector<syntax::SimpleDeclaration *> Children;
-  for (const auto &ParameterAsNode : ParametersAsNodes) {
-    Children.push_back(llvm::cast<syntax::SimpleDeclaration>(ParameterAsNode));
-  }
-  return Children;
+  return getElements<SimpleDeclaration>();
 }
 
 std::vector<syntax::List::ElementAndDelimiter<syntax::SimpleDeclaration>>
 syntax::ParameterDeclarationList::getParametersAndCommas() {
-  auto ParametersAsNodesAndCommas = getElementsAsNodesAndDelimiters();
-  std::vector<syntax::List::ElementAndDelimiter<syntax::SimpleDeclaration>>
-      Children;
-  for (const auto &ParameterAsNodeAndComma : ParametersAsNodesAndCommas) {
-    Children.push_back(
-        {llvm::cast<syntax::SimpleDeclaration>(ParameterAsNodeAndComma.element),
-         ParameterAsNodeAndComma.delimiter});
-  }
-  return Children;
+  return getElementsAndDelimiters<SimpleDeclaration>();
 }
 
 std::vector<syntax::SimpleDeclarator *>
 syntax::DeclaratorList::getDeclarators() {
-  auto DeclaratorsAsNodes = getElementsAsNodes();
-  std::vector<syntax::SimpleDeclarator *> Children;
-  for (const auto &DeclaratorAsNode : DeclaratorsAsNodes) {
-    Children.push_back(llvm::cast<syntax::SimpleDeclarator>(DeclaratorAsNode));
-  }
-  return Children;
+  return getElements<SimpleDeclarator>();
 }
 
 std::vector<syntax::List::ElementAndDelimiter<syntax::SimpleDeclarator>>
 syntax::DeclaratorList::getDeclaratorsAndCommas() {
-  auto DeclaratorsAsNodesAndCommas = getElementsAsNodesAndDelimiters();
-  std::vector<syntax::List::ElementAndDelimiter<syntax::SimpleDeclarator>>
-      Children;
-  for (const auto &DeclaratorAsNodeAndComma : DeclaratorsAsNodesAndCommas) {
-    Children.push_back(
-        {llvm::cast<syntax::SimpleDeclarator>(DeclaratorAsNodeAndComma.element),
-         DeclaratorAsNodeAndComma.delimiter});
-  }
-  return Children;
+  return getElementsAndDelimiters<SimpleDeclarator>();
 }
 
 syntax::Expression *syntax::MemberExpression::getObject() {
Index: clang/include/clang/Tooling/Syntax/Tree.h
===================================================================
--- clang/include/clang/Tooling/Syntax/Tree.h
+++ clang/include/clang/Tooling/Syntax/Tree.h
@@ -201,23 +201,19 @@
 /// delimited-list(element, delimiter, termination, canBeEmpty)
 class List : public Tree {
 public:
+  using Tree::Tree;
+  static bool classof(const Node *N);
+
   template <typename Element> struct ElementAndDelimiter {
     Element *element;
     Leaf *delimiter;
   };
 
-  enum class TerminationKind {
-    Terminated,
-    MaybeTerminated,
-    Separated,
-  };
-
-  using Tree::Tree;
-  static bool classof(const Node *N);
-  /// Returns the elements and corresponding delimiters. Missing elements
-  /// and delimiters are represented as null pointers.
+  /// Iterator through elements and their corresponding delimiters. Missing
+  /// elements and delimiters are represented as null pointers. Below we give
+  /// examples of what this iteration looks like.
   ///
-  /// For example, in a separated list:
+  /// In a separated list:
   /// "a, b, c"  <=> [("a" , ","), ("b" , "," ), ("c" , null)]
   /// "a,  , c"  <=> [("a" , ","), (null, "," ), ("c" , null)]
   /// "a, b  c"  <=> [("a" , ","), ("b" , null), ("c" , null)]
@@ -228,14 +224,235 @@
   /// "a;  ; c;" <=> [("a" , ";"), (null, ";" ), ("c" , ";" )]
   /// "a; b  c;" <=> [("a" , ";"), ("b" , null), ("c" , ";" )]
   /// "a; b; c"  <=> [("a" , ";"), ("b" , ";" ), ("c" , null)]
-  std::vector<ElementAndDelimiter<Node>> getElementsAsNodesAndDelimiters();
+  ///
+  /// NOTE: This iterator *should* be a standard iterator, however until C++17
+  /// we can only have reference proxies for input iterators, and
+  /// `ElementAndDelimiter` acts as one.
+  template <typename ElementType> class ElementAndDelimiterIterator {
+  public:
+    ElementAndDelimiterIterator &operator++() {
+      switch (getKind()) {
+      case IteratorKind::BeforeBegin: {
+        if (auto *Begin = getParent()->getFirstChild())
+          setElementOrDelimiter(Begin);
+        else
+          setSentinel(IteratorKind::End);
+        return *this;
+      }
+      case IteratorKind::NotSentinel: {
+        auto *DelimiterOrElement = getDelimiterOrElement();
+        assert(DelimiterOrElement);
+        auto *Next = DelimiterOrElement->getNextSibling();
+        if (Next) {
+          setElementOrDelimiter(Next);
+          return *this;
+        }
+        if (getParent()->getTerminationKind() == TerminationKind::Separated &&
+            isDelimiter(DelimiterOrElement))
+          setSentinel(IteratorKind::MissingLastInSeparated);
+        else
+          setSentinel(IteratorKind::End);
+        return *this;
+      }
+      case IteratorKind::MissingLastInSeparated: {
+        setSentinel(IteratorKind::End);
+        return *this;
+      }
+      case IteratorKind::End:
+        llvm_unreachable("Incrementing end iterator.");
+      }
+    }
+
+    ElementAndDelimiterIterator operator++(int) {
+      auto tmp = *this;
+      ++*this;
+      return tmp;
+    }
+
+    // An `operator*` would return an `ElementAndDelimiter`, but it would return
+    // it as a value instead of the expected reference, since this
+    // `ElementAndDelimiter` isn't stored anywhere. Returning a value for the
+    // operator* breaks the specification of a common iterator. As such we
+    // prefer to not provide this operator at all to not create false
+    // expectations on the user.
+    //
+    // Instead we define `getElement()` and `getDelimiter()`.
+
+    /// Returns what you would expect from `It->element`.
+    ElementType *getElement() {
+      auto *ElementOrDelimiter = getElementOrDelimiter();
+      if (ElementOrDelimiter && isElement(ElementOrDelimiter))
+        return cast<ElementType>(ElementOrDelimiter);
+      return nullptr;
+    }
+
+    /// Returns what you would expect from `It->delimiter`.
+    Leaf *getDelimiter() {
+      auto *DelimiterOrElement = getDelimiterOrElement();
+      if (DelimiterOrElement && isDelimiter(DelimiterOrElement))
+        return cast<Leaf>(DelimiterOrElement);
+      return nullptr;
+    }
+
+    bool operator==(const ElementAndDelimiterIterator &Other) const {
+      return Opaque == Other.Opaque;
+    }
+
+    bool operator!=(const ElementAndDelimiterIterator &Other) const {
+      return !(*this == Other);
+    }
+
+  private:
+    enum class IteratorKind {
+      BeforeBegin,
+      End,
+      NotSentinel,
+      MissingLastInSeparated // Models ElementAndDelimiter = (nullptr, nullptr).
+    };
+
+    // The pointer inside `Opaque` changes semantics according to the
+    // `IteratorKind`. For `NotSentinel` it stores the `Element` or, if the
+    // element is missing, the following `Delimiter`. Otherwise it
+    // stores the `Parent`. Invariant: Opaque != nullptr.
+    llvm::PointerIntPair<Node *, 2, IteratorKind> Opaque;
+
+    void setSentinel(IteratorKind K) {
+      assert(K != IteratorKind::NotSentinel);
+      auto *Parent = getParent();
+      Opaque.setPointerAndInt(Parent, K);
+    }
+    void setElementOrDelimiter(Node *EOrD) {
+      Opaque.setPointerAndInt(EOrD, IteratorKind::NotSentinel);
+    }
+
+    /// For the current `ElementAndDelimiter` returns the element if it is
+    /// present, otherwise returns the delimiter.
+    ///
+    /// Examples:
+    ///  ElementAndDelimiter | ElementOrDelimiter
+    ///  (Element, Delimiter)| Element
+    ///  (Element, nullptr  )| Element
+    ///  (nullptr, Delimiter)| Delimiter
+    ///  (nullptr, nullptr  )| nullptr
+    Node *getElementOrDelimiter() {
+      switch (getKind()) {
+      case IteratorKind::NotSentinel: {
+        return Opaque.getPointer();
+      }
+      case IteratorKind::MissingLastInSeparated:
+        return nullptr;
+      case IteratorKind::BeforeBegin:
+      case IteratorKind::End:
+        llvm_unreachable("Dereferencing sentinel iterator");
+      }
+    }
+    /// For the current `ElementAndDelimiter` returns the delimiter if it is
+    /// present, otherwise returns the element.
+    Node *getDelimiterOrElement() {
+      auto *ElementOrDelimiter = getElementOrDelimiter();
+      if (!ElementOrDelimiter)
+        return nullptr;
+      auto *Next = ElementOrDelimiter->getNextSibling();
+      if (isElement(ElementOrDelimiter) && Next && isDelimiter(Next)) {
+        return Next;
+      }
+      return ElementOrDelimiter;
+    }
+    IteratorKind getKind() const { return Opaque.getInt(); }
+
+  public:
+    List *getParent() {
+      switch (getKind()) {
+      case IteratorKind::NotSentinel:
+        return cast<List>(getElementOrDelimiter()->getParent());
+      case IteratorKind::BeforeBegin:
+      case IteratorKind::MissingLastInSeparated:
+      case IteratorKind::End:
+        return cast<List>(Opaque.getPointer());
+      }
+    }
+    const List *getParent() const {
+      return const_cast<ElementAndDelimiterIterator *>(this)->getParent();
+    }
+
+  private:
+    ElementAndDelimiterIterator(Node *N, IteratorKind K) : Opaque(N, K) {}
+
+  public:
+    static ElementAndDelimiterIterator<ElementType> beforeBegin(List *Parent) {
+      assert(Parent);
+      return ElementAndDelimiterIterator<ElementType>(
+          Parent, IteratorKind::BeforeBegin);
+    }
+
+    static ElementAndDelimiterIterator<ElementType> begin(List *Parent) {
+      return ++beforeBegin(Parent);
+    }
+
+    static ElementAndDelimiterIterator<ElementType> end(List *Parent) {
+      assert(Parent);
+      return ElementAndDelimiterIterator<ElementType>(Parent,
+                                                      IteratorKind::End);
+    }
+  };
+
+protected:
+  // These function templates are specialized in syntax nodes inheriting from
+  // `List`, according to their `ElementType`s.
+
+  template <typename ElementType>
+  ElementAndDelimiterIterator<ElementType> getElementAndDelimiterBeforeBegin() {
+    return ElementAndDelimiterIterator<ElementType>::beforeBegin(this);
+  }
+  template <typename ElementType>
+  ElementAndDelimiterIterator<ElementType> getElementAndDelimiterBegin() {
+    return ElementAndDelimiterIterator<ElementType>::begin(this);
+  }
+  template <typename ElementType>
+  ElementAndDelimiterIterator<ElementType> getElementAndDelimiterEnd() {
+    return ElementAndDelimiterIterator<ElementType>::end(this);
+  }
+
+  /// Returns the elements of the list, and their corresponding delimiters.
+  /// Missing elements are represented as null pointers.
+  template <typename ElementType>
+  std::vector<ElementAndDelimiter<ElementType>> getElementsAndDelimiters() {
+    std::vector<ElementAndDelimiter<ElementType>> Result;
+    for (auto It = getElementAndDelimiterBegin<ElementType>(),
+              End = getElementAndDelimiterEnd<ElementType>();
+         It != End; ++It)
+      Result.push_back({It.getElement(), It.getDelimiter()});
+    return Result;
+  }
 
   /// Returns the elements of the list. Missing elements are represented
   /// as null pointers in the same way as in the return value of
-  /// `getElementsAsNodesAndDelimiters()`.
+  /// `getElementsAndDelimiters()`.
+  template <typename ElementType> std::vector<ElementType *> getElements() {
+    std::vector<ElementType *> Result;
+    for (auto It = getElementAndDelimiterBegin<ElementType>(),
+              End = getElementAndDelimiterEnd<ElementType>();
+         It != End; ++It)
+      Result.push_back(It.getElement());
+    return Result;
+  }
+
+public:
+  ElementAndDelimiterIterator<Node>
+  getElementsAsNodesAndDelimitersBeforeBegin();
+  ElementAndDelimiterIterator<Node> getElementsAsNodesAndDelimitersBegin();
+  ElementAndDelimiterIterator<Node> getElementsAsNodesAndDelimitersEnd();
+
+  std::vector<ElementAndDelimiter<Node>> getElementsAsNodesAndDelimiters();
   std::vector<Node *> getElementsAsNodes();
 
-  // These can't be implemented with the information we have!
+  enum class TerminationKind {
+    Terminated,
+    MaybeTerminated,
+    Separated,
+  };
+
+  TerminationKind getTerminationKind() const;
 
   /// Returns the appropriate delimiter for this list.
   ///
@@ -243,14 +460,16 @@
   /// elements to empty or one-element lists.
   clang::tok::TokenKind getDelimiterTokenKind() const;
 
-  TerminationKind getTerminationKind() const;
-
   /// Whether this list can be empty in syntactically and semantically correct
   /// code.
   ///
   /// This list may be empty when the source code has errors even if
   /// canBeEmpty() returns false.
   bool canBeEmpty() const;
+
+private:
+  static bool isElement(Node *N);
+  static bool isDelimiter(Node *N);
 };
 
 } // namespace syntax
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to