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<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,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::List>(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 &First = *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::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.");
-    }
-  }
+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::Node>
+syntax::List::getElementAndDelimiterBeforeBegin() {
+  return ElementAndDelimiterIterator<Node>::makeBeforeBegin(this);
 }
 
-// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
-// ignoring delimiters
-std::vector<syntax::Node *> syntax::List::getElementsAsNodes() {
-  if (!getFirstChild())
-    return {};
+syntax::List::ElementAndDelimiterIterator<syntax::Node>
+syntax::List::getElementAndDelimiterBegin() {
+  return std::next(ElementAndDelimiterIterator<Node>::makeBeforeBegin(this));
+}
 
-  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.");
-    }
-  }
+syntax::List::ElementAndDelimiterIterator<syntax::Node>
+syntax::List::getElementAndDelimiterEnd() {
+  return ElementAndDelimiterIterator<Node>::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::ElementAndDelimiter<syntax::Node>>
+syntax::List::getElementsAsNodesAndDelimiters() {
+  return std::vector<syntax::List::ElementAndDelimiter<syntax::Node>>(
+      getElementAndDelimiterBegin(), getElementAndDelimiterEnd());
+}
 
+std::vector<syntax::Node *> syntax::List::getElementsAsNodes() {
+  std::vector<syntax::Node *> Children;
+  std::transform(
+      getElementAndDelimiterBegin(), getElementAndDelimiterEnd(),
+      std::back_inserter(Children),
+      [](ElementAndDelimiter<syntax::Node> 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::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));
-  }
+  for (auto C : getNodeRange())
+    Children.push_back(cast<syntax::NameSpecifier>(C.element));
+
   return Children;
 }
 
 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});
-  }
+  for (auto C : getNodeRange())
+    Children.push_back({cast<syntax::NameSpecifier>(C.element), C.delimiter});
+
   return Children;
 }
 
 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));
-  }
+  for (auto C : getNodeRange())
+    Children.push_back(cast<syntax::Expression>(C.element));
+
   return Children;
 }
 
 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});
-  }
+  for (auto C : getNodeRange())
+    Children.push_back({cast<syntax::Expression>(C.element), C.delimiter});
+
   return Children;
 }
 
 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));
-  }
+  for (auto C : getNodeRange())
+    Children.push_back(cast<syntax::SimpleDeclaration>(C.element));
+
   return Children;
 }
 
 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) {
+  for (auto C : getNodeRange())
     Children.push_back(
-        {llvm::cast<syntax::SimpleDeclaration>(ParameterAsNodeAndComma.element),
-         ParameterAsNodeAndComma.delimiter});
-  }
+        {cast<syntax::SimpleDeclaration>(C.element), C.delimiter});
+
   return Children;
 }
 
 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));
-  }
+  for (auto C : getNodeRange())
+    Children.push_back(cast<syntax::SimpleDeclarator>(C.element));
+
   return Children;
 }
 
 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) {
+  for (auto C : getNodeRange())
     Children.push_back(
-        {llvm::cast<syntax::SimpleDeclarator>(DeclaratorAsNodeAndComma.element),
-         DeclaratorAsNodeAndComma.delimiter});
-  }
+        {cast<syntax::SimpleDeclarator>(C.element), C.delimiter});
+
   return Children;
 }
 
Index: clang/include/clang/Tooling/Syntax/Tree.h
===================================================================
--- clang/include/clang/Tooling/Syntax/Tree.h
+++ clang/include/clang/Tooling/Syntax/Tree.h
@@ -28,8 +28,10 @@
 #include "clang/Tooling/Syntax/Tokens.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/iterator_range.h"
 #include "llvm/Support/Allocator.h"
 #include <cstdint>
+#include <iterator>
 
 namespace clang {
 namespace syntax {
@@ -204,6 +206,12 @@
   template <typename Element> struct ElementAndDelimiter {
     Element *element;
     Leaf *delimiter;
+    bool operator==(const ElementAndDelimiter &Other) const {
+      return element == Other.element && delimiter == Other.delimiter;
+    }
+    bool operator!=(const ElementAndDelimiter &Other) const {
+      return !(*this == Other);
+    }
   };
 
   enum class TerminationKind {
@@ -214,10 +222,12 @@
 
   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 would 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,6 +238,183 @@
   /// "a;  ; c;" <=> [("a" , ";"), (null, ";" ), ("c" , ";" )]
   /// "a; b  c;" <=> [("a" , ";"), ("b" , null), ("c" , ";" )]
   /// "a; b; c"  <=> [("a" , ";"), ("b" , ";" ), ("c" , null)]
+  ///
+  /// NOTE: This iterator *should* be a forward 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 std::iterator<std::input_iterator_tag,
+                             ElementAndDelimiter<ElementType>, ptrdiff_t,
+                             const ElementAndDelimiter<ElementType> *,
+                             ElementAndDelimiter<ElementType>> {
+  public:
+    ElementAndDelimiterIterator &operator++() {
+      assert(Kind != IteratorKind::End && "Incrementing end iterator.");
+      if (Kind == IteratorKind::BeforeBegin) {
+        if (auto *Begin = Parent->getFirstChild()) {
+          Kind = IteratorKind::NotSentinel;
+          if (isElement(Begin))
+            Current = getWithDelimiter(cast<ElementType>(Begin));
+          else
+            Current = {nullptr, cast<Leaf>(Begin)};
+        } else
+          Kind = IteratorKind::End;
+      } else {
+        auto Next = [](ElementAndDelimiter<ElementType> ED)
+            -> llvm::Optional<ElementAndDelimiter<ElementType>> {
+          auto *N = ED.element ? ED.element : ED.delimiter;
+          if (!N)
+            return None;
+          if (isElement(N))
+            return getElementAndDelimiterAfterElement(cast<ElementType>(N));
+          return getElementAndDelimiterAfterDelimiter(cast<Leaf>(N));
+        }(Current);
+        if (Next.hasValue())
+          Current = Next.getValue();
+        else
+          Kind = IteratorKind::End;
+      }
+      return *this;
+    }
+    ElementAndDelimiterIterator operator++(int) {
+      auto tmp = *this;
+      ++*this;
+      return tmp;
+    }
+
+    ElementAndDelimiter<ElementType> operator*() const {
+      assert(Kind == IteratorKind::NotSentinel &&
+             "Dereferencing sentinel iterator.");
+      return Current;
+    }
+
+    bool operator==(const ElementAndDelimiterIterator &Other) const {
+      return (Parent == Other.Parent && Kind == Other.Kind &&
+              (Kind != IteratorKind::NotSentinel || Current == Other.Current));
+    }
+
+    bool operator!=(const ElementAndDelimiterIterator &Other) const {
+      return !(*this == Other);
+    }
+
+  private:
+    enum class IteratorKind { BeforeBegin, End, NotSentinel };
+
+    List *Parent;
+    IteratorKind Kind;
+    // If `Kind != NotSentinel`, then `Current` has trash.
+    ElementAndDelimiter<ElementType> Current;
+
+  public:
+    List *getParent() { return Parent; }
+    const List *getParent() const { return Parent; }
+
+    bool isEnd() const { return Kind == IteratorKind::End; };
+    bool isBeforeBegin() const { return Kind == IteratorKind::BeforeBegin; };
+    bool isSentinel() const { return Kind != IteratorKind::NotSentinel; };
+
+    static ElementAndDelimiterIterator<ElementType> makeEnd(List *Parent) {
+      assert(Parent);
+      ElementAndDelimiterIterator<ElementType> It;
+      It.Parent = Parent;
+      It.Kind = IteratorKind::End;
+      return It;
+    }
+
+    static ElementAndDelimiterIterator<ElementType>
+    makeBeforeBegin(List *Parent) {
+      assert(Parent);
+      ElementAndDelimiterIterator<ElementType> It;
+      It.Parent = Parent;
+      It.Kind = IteratorKind::BeforeBegin;
+      return It;
+    }
+
+  private:
+    static ElementAndDelimiter<ElementType>
+    getWithDelimiter(ElementType *Element) {
+      assert(Element && isElement(Element));
+      auto *Next = Element->getNextSibling();
+      if (!Next)
+        return {Element, nullptr};
+
+      if (isElement(Next))
+        return {Element, nullptr};
+      if (isDelimiter(Next))
+        return {Element, cast<Leaf>(Next)};
+
+      llvm_unreachable(
+          "A list can have only elements and delimiters as children.");
+    }
+
+    static llvm::Optional<ElementAndDelimiter<ElementType>>
+    getElementAndDelimiterAfterDelimiter(Leaf *Delimiter) {
+      assert(Delimiter && isDelimiter(Delimiter));
+      auto *L = cast<List>(Delimiter->getParent());
+      auto *Next = Delimiter->getNextSibling();
+      if (!Next) {
+        switch (L->getTerminationKind()) {
+        // List is separated and ends with delimiter
+        // => missing last ElementAndDelimiter.
+        case TerminationKind::Separated:
+          return llvm::Optional<ElementAndDelimiter<ElementType>>(
+              {nullptr, nullptr});
+        case TerminationKind::Terminated:
+        case TerminationKind::MaybeTerminated:
+          return None;
+        }
+      }
+      if (isElement(Next))
+        return getWithDelimiter(cast<ElementType>(Next));
+
+      // Consecutive Delimiters => missing Element
+      if (isDelimiter(Next))
+        return llvm::Optional<ElementAndDelimiter<ElementType>>(
+            {nullptr, cast<Leaf>(Next)});
+      llvm_unreachable(
+          "A list can have only elements and delimiters as children.");
+    }
+
+    static llvm::Optional<ElementAndDelimiter<ElementType>>
+    getElementAndDelimiterAfterElement(ElementType *Element) {
+      assert(Element && isElement(Element));
+      auto *Next = Element->getNextSibling();
+      if (!Next)
+        // This was the last element of the list.
+        return None;
+
+      if (isElement(Next))
+        // We have something of the form "a b ..." => 'b' starts the next
+        // `ElementAndDelimiter`.
+        return getWithDelimiter(cast<ElementType>(Next));
+      if (isDelimiter(Next))
+        // We have something of the form "a , ..." => whatever comes after the
+        // comma starts the next `ElementAndDelimiter`.
+        return getElementAndDelimiterAfterDelimiter(cast<Leaf>(Next));
+
+      llvm_unreachable(
+          "A list can have only elements and delimiters as children.");
+    }
+  };
+
+  ElementAndDelimiterIterator<Node> getElementAndDelimiterBeforeBegin();
+  ElementAndDelimiterIterator<Node> getElementAndDelimiterBegin();
+  ElementAndDelimiterIterator<Node> getElementAndDelimiterEnd();
+
+  template <typename ElementType>
+  using ElementsAndDelimitersRange =
+      llvm::iterator_range<ElementAndDelimiterIterator<ElementType>>;
+
+  ElementsAndDelimitersRange<Node> getNodeRange() {
+    return ElementsAndDelimitersRange<Node>(getElementAndDelimiterBegin(),
+                                            getElementAndDelimiterEnd());
+  }
+
+  // Returns the elements of the list, and their corresponding delimiters.
+  // Missing elements are represented as null pointers. Iterating through
+  // `getElementsAsNodesAndDelimiters()` is equivalent to iterating through
+  // `getRange()`
   std::vector<ElementAndDelimiter<Node>> getElementsAsNodesAndDelimiters();
 
   /// Returns the elements of the list. Missing elements are represented
@@ -251,6 +438,10 @@
   /// 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