summaryrefslogtreecommitdiff
path: root/clang/lib/Tooling/Syntax/Tree.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
commit706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch)
tree4adf86a776049cbf7f69a1929c4babcbbef925eb /clang/lib/Tooling/Syntax/Tree.cpp
parent7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff)
Notes
Diffstat (limited to 'clang/lib/Tooling/Syntax/Tree.cpp')
-rw-r--r--clang/lib/Tooling/Syntax/Tree.cpp142
1 files changed, 127 insertions, 15 deletions
diff --git a/clang/lib/Tooling/Syntax/Tree.cpp b/clang/lib/Tooling/Syntax/Tree.cpp
index 1549b6724fa4..9a6270ec4cce 100644
--- a/clang/lib/Tooling/Syntax/Tree.cpp
+++ b/clang/lib/Tooling/Syntax/Tree.cpp
@@ -11,9 +11,27 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
+#include <cassert>
using namespace clang;
+namespace {
+static void traverse(const syntax::Node *N,
+ llvm::function_ref<void(const syntax::Node *)> Visit) {
+ if (auto *T = dyn_cast<syntax::Tree>(N)) {
+ for (auto *C = T->firstChild(); C; C = C->nextSibling())
+ traverse(C, Visit);
+ }
+ Visit(N);
+}
+static void traverse(syntax::Node *N,
+ llvm::function_ref<void(syntax::Node *)> Visit) {
+ traverse(static_cast<const syntax::Node *>(N), [&](const syntax::Node *N) {
+ Visit(const_cast<syntax::Node *>(N));
+ });
+}
+} // namespace
+
syntax::Arena::Arena(SourceManager &SourceMgr, const LangOptions &LangOpts,
TokenBuffer Tokens)
: SourceMgr(SourceMgr), LangOpts(LangOpts), Tokens(std::move(Tokens)) {}
@@ -40,7 +58,10 @@ bool syntax::Leaf::classof(const Node *N) {
syntax::Node::Node(NodeKind Kind)
: Parent(nullptr), NextSibling(nullptr), Kind(static_cast<unsigned>(Kind)),
- Role(static_cast<unsigned>(NodeRole::Detached)) {}
+ Role(static_cast<unsigned>(NodeRole::Detached)), Original(false),
+ CanModify(false) {}
+
+bool syntax::Node::isDetached() const { return role() == NodeRole::Detached; }
bool syntax::Tree::classof(const Node *N) { return N->kind() > NodeKind::Leaf; }
@@ -56,15 +77,53 @@ void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) {
this->FirstChild = Child;
}
-namespace {
-static void traverse(const syntax::Node *N,
- llvm::function_ref<void(const syntax::Node *)> Visit) {
- if (auto *T = dyn_cast<syntax::Tree>(N)) {
- for (auto *C = T->firstChild(); C; C = C->nextSibling())
- traverse(C, Visit);
+void syntax::Tree::replaceChildRangeLowLevel(Node *BeforeBegin, Node *End,
+ Node *New) {
+ assert(!BeforeBegin || BeforeBegin->Parent == this);
+
+#ifndef NDEBUG
+ for (auto *N = New; N; N = N->nextSibling()) {
+ assert(N->Parent == nullptr);
+ assert(N->role() != NodeRole::Detached && "Roles must be set");
+ // FIXME: sanity-check the role.
}
- Visit(N);
+#endif
+
+ // Detach old nodes.
+ for (auto *N = !BeforeBegin ? FirstChild : BeforeBegin->nextSibling();
+ N != End;) {
+ auto *Next = N->NextSibling;
+
+ N->Role = static_cast<unsigned>(NodeRole::Detached);
+ N->Parent = nullptr;
+ N->NextSibling = nullptr;
+ if (N->Original)
+ 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 (New) {
+ auto *Last = New;
+ for (auto *N = New; N != nullptr; N = N->nextSibling()) {
+ Last = N;
+ N->Parent = this;
+ }
+ Last->NextSibling = End;
+ }
+
+ // Mark the node as modified.
+ for (auto *T = this; T && T->Original; T = T->Parent)
+ T->Original = false;
}
+
+namespace {
static void dumpTokens(llvm::raw_ostream &OS, ArrayRef<syntax::Token> Tokens,
const SourceManager &SM) {
assert(!Tokens.empty());
@@ -85,13 +144,16 @@ static void dumpTokens(llvm::raw_ostream &OS, ArrayRef<syntax::Token> Tokens,
static void dumpTree(llvm::raw_ostream &OS, const syntax::Node *N,
const syntax::Arena &A, std::vector<bool> IndentMask) {
- if (N->role() != syntax::NodeRole::Unknown) {
- // FIXME: print the symbolic name of a role.
- if (N->role() == syntax::NodeRole::Detached)
- OS << "*: ";
- else
- OS << static_cast<int>(N->role()) << ": ";
- }
+ std::string Marks;
+ if (!N->isOriginal())
+ Marks += "M";
+ if (N->role() == syntax::NodeRole::Detached)
+ Marks += "*"; // FIXME: find a nice way to print other roles.
+ if (!N->canModify())
+ Marks += "I";
+ if (!Marks.empty())
+ OS << Marks << ": ";
+
if (auto *L = llvm::dyn_cast<syntax::Leaf>(N)) {
dumpTokens(OS, *L->token(), A.sourceManager());
OS << "\n";
@@ -136,10 +198,60 @@ std::string syntax::Node::dumpTokens(const Arena &A) const {
if (!L)
return;
::dumpTokens(OS, *L->token(), A.sourceManager());
+ OS << " ";
});
return OS.str();
}
+void syntax::Node::assertInvariants() const {
+#ifndef NDEBUG
+ if (isDetached())
+ assert(parent() == nullptr);
+ else
+ assert(parent() != nullptr);
+
+ auto *T = dyn_cast<Tree>(this);
+ if (!T)
+ return;
+ for (auto *C = T->firstChild(); C; C = C->nextSibling()) {
+ if (T->isOriginal())
+ assert(C->isOriginal());
+ assert(!C->isDetached());
+ assert(C->parent() == T);
+ }
+#endif
+}
+
+void syntax::Node::assertInvariantsRecursive() const {
+#ifndef NDEBUG
+ traverse(this, [&](const syntax::Node *N) { N->assertInvariants(); });
+#endif
+}
+
+syntax::Leaf *syntax::Tree::firstLeaf() {
+ auto *T = this;
+ while (auto *C = T->firstChild()) {
+ if (auto *L = dyn_cast<syntax::Leaf>(C))
+ return L;
+ T = cast<syntax::Tree>(C);
+ }
+ return nullptr;
+}
+
+syntax::Leaf *syntax::Tree::lastLeaf() {
+ auto *T = this;
+ while (auto *C = T->firstChild()) {
+ // Find the last child.
+ while (auto *Next = C->nextSibling())
+ C = Next;
+
+ if (auto *L = dyn_cast<syntax::Leaf>(C))
+ return L;
+ T = cast<syntax::Tree>(C);
+ }
+ return nullptr;
+}
+
syntax::Node *syntax::Tree::findChild(NodeRole R) {
for (auto *C = FirstChild; C; C = C->nextSibling()) {
if (C->role() == R)