diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-01-17 20:45:01 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-01-17 20:45:01 +0000 |
commit | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch) | |
tree | 4adf86a776049cbf7f69a1929c4babcbbef925eb /clang/lib/Tooling/Syntax/Tree.cpp | |
parent | 7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff) |
Notes
Diffstat (limited to 'clang/lib/Tooling/Syntax/Tree.cpp')
-rw-r--r-- | clang/lib/Tooling/Syntax/Tree.cpp | 142 |
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) |