//===- Tree.cpp -----------------------------------------------*- C++ -*-=====// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Tooling/Syntax/Tree.h" #include "clang/Basic/TokenKinds.h" #include "clang/Tooling/Syntax/Nodes.h" #include "llvm/ADT/BitVector.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Casting.h" #include using namespace clang; namespace { static void traverse(const syntax::Node *N, llvm::function_ref Visit) { if (auto *T = dyn_cast(N)) { for (const syntax::Node &C : T->getChildren()) traverse(&C, Visit); } Visit(N); } static void traverse(syntax::Node *N, llvm::function_ref Visit) { traverse(static_cast(N), [&](const syntax::Node *N) { Visit(const_cast(N)); }); } } // namespace syntax::Leaf::Leaf(syntax::TokenManager::Key K) : Node(NodeKind::Leaf), K(K) {} syntax::Node::Node(NodeKind Kind) : Parent(nullptr), NextSibling(nullptr), PreviousSibling(nullptr), Kind(static_cast(Kind)), Role(0), Original(false), CanModify(false) { this->setRole(NodeRole::Detached); } bool syntax::Node::isDetached() const { return getRole() == NodeRole::Detached; } void syntax::Node::setRole(NodeRole NR) { this->Role = static_cast(NR); } void syntax::Tree::appendChildLowLevel(Node *Child, NodeRole Role) { assert(Child->getRole() == NodeRole::Detached); assert(Role != NodeRole::Detached); Child->setRole(Role); appendChildLowLevel(Child); } void syntax::Tree::appendChildLowLevel(Node *Child) { assert(Child->Parent == nullptr); assert(Child->NextSibling == nullptr); assert(Child->PreviousSibling == nullptr); assert(Child->getRole() != NodeRole::Detached); Child->Parent = this; if (this->LastChild) { Child->PreviousSibling = this->LastChild; this->LastChild->NextSibling = Child; } else this->FirstChild = Child; this->LastChild = Child; } void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) { assert(Child->getRole() == NodeRole::Detached); assert(Role != NodeRole::Detached); Child->setRole(Role); prependChildLowLevel(Child); } void syntax::Tree::prependChildLowLevel(Node *Child) { assert(Child->Parent == nullptr); assert(Child->NextSibling == nullptr); assert(Child->PreviousSibling == nullptr); assert(Child->getRole() != NodeRole::Detached); Child->Parent = this; if (this->FirstChild) { Child->NextSibling = this->FirstChild; this->FirstChild->PreviousSibling = Child; } else this->LastChild = Child; this->FirstChild = Child; } void syntax::Tree::replaceChildRangeLowLevel(Node *Begin, Node *End, Node *New) { assert((!Begin || Begin->Parent == this) && "`Begin` is not a child of `this`."); assert((!End || End->Parent == this) && "`End` is not a child of `this`."); assert(canModify() && "Cannot modify `this`."); #ifndef NDEBUG for (auto *N = New; N; N = N->NextSibling) { assert(N->Parent == nullptr); assert(N->getRole() != NodeRole::Detached && "Roles must be set"); // FIXME: validate the role. } auto Reachable = [](Node *From, Node *N) { if (!N) return true; for (auto *It = From; It; It = It->NextSibling) if (It == N) return true; return false; }; assert(Reachable(FirstChild, Begin) && "`Begin` is not reachable."); assert(Reachable(Begin, End) && "`End` is not after `Begin`."); #endif if (!New && Begin == End) return; // Mark modification. for (auto *T = this; T && T->Original; T = T->Parent) T->Original = false; // Save the node before the range to be removed. Later we insert the `New` // range after this node. auto *BeforeBegin = Begin ? Begin->PreviousSibling : LastChild; // Detach old nodes. for (auto *N = Begin; N != End;) { auto *Next = N->NextSibling; N->setRole(NodeRole::Detached); N->Parent = nullptr; N->NextSibling = nullptr; N->PreviousSibling = nullptr; if (N->Original) traverse(N, [](Node *C) { C->Original = false; }); N = Next; } // Attach new range. auto *&NewFirst = BeforeBegin ? BeforeBegin->NextSibling : FirstChild; auto *&NewLast = End ? End->PreviousSibling : LastChild; if (!New) { NewFirst = End; NewLast = BeforeBegin; return; } New->PreviousSibling = BeforeBegin; NewFirst = New; Node *LastInNew; for (auto *N = New; N != nullptr; N = N->NextSibling) { LastInNew = N; N->Parent = this; } LastInNew->NextSibling = End; NewLast = LastInNew; } namespace { static void dumpNode(raw_ostream &OS, const syntax::Node *N, const syntax::TokenManager &TM, llvm::BitVector IndentMask) { auto DumpExtraInfo = [&OS](const syntax::Node *N) { if (N->getRole() != syntax::NodeRole::Unknown) OS << " " << N->getRole(); if (!N->isOriginal()) OS << " synthesized"; if (!N->canModify()) OS << " unmodifiable"; }; assert(N); if (const auto *L = dyn_cast(N)) { OS << "'"; OS << TM.getText(L->getTokenKey()); OS << "'"; DumpExtraInfo(N); OS << "\n"; return; } const auto *T = cast(N); OS << T->getKind(); DumpExtraInfo(N); OS << "\n"; for (const syntax::Node &It : T->getChildren()) { for (unsigned Idx = 0; Idx < IndentMask.size(); ++Idx) { if (IndentMask[Idx]) OS << "| "; else OS << " "; } if (!It.getNextSibling()) { OS << "`-"; IndentMask.push_back(false); } else { OS << "|-"; IndentMask.push_back(true); } dumpNode(OS, &It, TM, IndentMask); IndentMask.pop_back(); } } } // namespace std::string syntax::Node::dump(const TokenManager &TM) const { std::string Str; llvm::raw_string_ostream OS(Str); dumpNode(OS, this, TM, /*IndentMask=*/{}); return std::move(OS.str()); } std::string syntax::Node::dumpTokens(const TokenManager &TM) const { std::string Storage; llvm::raw_string_ostream OS(Storage); traverse(this, [&](const syntax::Node *N) { if (const auto *L = dyn_cast(N)) { OS << TM.getText(L->getTokenKey()); OS << " "; } }); return Storage; } void syntax::Node::assertInvariants() const { #ifndef NDEBUG if (isDetached()) assert(getParent() == nullptr); else assert(getParent() != nullptr); const auto *T = dyn_cast(this); if (!T) return; for (const Node &C : T->getChildren()) { if (T->isOriginal()) assert(C.isOriginal()); assert(!C.isDetached()); assert(C.getParent() == T); const auto *Next = C.getNextSibling(); assert(!Next || &C == Next->getPreviousSibling()); if (!C.getNextSibling()) assert(&C == T->getLastChild() && "Last child is reachable by advancing from the first child."); } const auto *L = dyn_cast(T); if (!L) return; for (const Node &C : T->getChildren()) { assert(C.getRole() == NodeRole::ListElement || C.getRole() == NodeRole::ListDelimiter); if (C.getRole() == NodeRole::ListDelimiter) { assert(isa(C)); // FIXME: re-enable it when there is way to retrieve token kind in Leaf. // assert(cast(C).getToken()->kind() == L->getDelimiterTokenKind()); } } #endif } void syntax::Node::assertInvariantsRecursive() const { #ifndef NDEBUG traverse(this, [&](const syntax::Node *N) { N->assertInvariants(); }); #endif } const syntax::Leaf *syntax::Tree::findFirstLeaf() const { for (const Node &C : getChildren()) { if (const auto *L = dyn_cast(&C)) return L; if (const auto *L = cast(C).findFirstLeaf()) return L; } return nullptr; } const syntax::Leaf *syntax::Tree::findLastLeaf() const { for (const auto *C = getLastChild(); C; C = C->getPreviousSibling()) { if (const auto *L = dyn_cast(C)) return L; if (const auto *L = cast(C)->findLastLeaf()) return L; } return nullptr; } const syntax::Node *syntax::Tree::findChild(NodeRole R) const { for (const Node &C : getChildren()) { if (C.getRole() == R) return &C; } return nullptr; } std::vector> syntax::List::getElementsAsNodesAndDelimiters() { if (!getFirstChild()) return {}; std::vector> Children; syntax::Node *ElementWithoutDelimiter = nullptr; for (Node &C : getChildren()) { 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(&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; } // Almost the same implementation of `getElementsAsNodesAndDelimiters` but // ignoring delimiters std::vector syntax::List::getElementsAsNodes() { if (!getFirstChild()) return {}; std::vector Children; syntax::Node *ElementWithoutDelimiter = nullptr; for (Node &C : getChildren()) { 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."); } } 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; } } return Children; } clang::tok::TokenKind syntax::List::getDelimiterTokenKind() const { switch (this->getKind()) { case NodeKind::NestedNameSpecifier: return clang::tok::coloncolon; case NodeKind::CallArguments: case NodeKind::ParameterDeclarationList: case NodeKind::DeclaratorList: return clang::tok::comma; default: llvm_unreachable("This is not a subclass of List, thus " "getDelimiterTokenKind() cannot be called"); } } syntax::List::TerminationKind syntax::List::getTerminationKind() const { switch (this->getKind()) { case NodeKind::NestedNameSpecifier: return TerminationKind::Terminated; case NodeKind::CallArguments: case NodeKind::ParameterDeclarationList: case NodeKind::DeclaratorList: return TerminationKind::Separated; default: llvm_unreachable("This is not a subclass of List, thus " "getTerminationKind() cannot be called"); } } bool syntax::List::canBeEmpty() const { switch (this->getKind()) { case NodeKind::NestedNameSpecifier: return false; case NodeKind::CallArguments: return true; case NodeKind::ParameterDeclarationList: return true; case NodeKind::DeclaratorList: return true; default: llvm_unreachable("This is not a subclass of List, thus canBeEmpty() " "cannot be called"); } }