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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits