diff --git a/clang/lib/Tooling/Syntax/Tree.cpp b/clang/lib/Tooling/Syntax/Tree.cpp --- a/clang/lib/Tooling/Syntax/Tree.cpp +++ b/clang/lib/Tooling/Syntax/Tree.cpp @@ -255,27 +255,24 @@ } syntax::Leaf *syntax::Tree::findFirstLeaf() { - auto *T = this; - while (auto *C = T->getFirstChild()) { + for (auto *C = getFirstChild(); C; C = C->getNextSibling()) { if (auto *L = dyn_cast(C)) return L; - T = cast(C); + if (auto *L = cast(C)->findFirstLeaf()) + return L; } return nullptr; } syntax::Leaf *syntax::Tree::findLastLeaf() { - auto *T = this; - while (auto *C = T->getFirstChild()) { - // Find the last child. - while (auto *Next = C->getNextSibling()) - C = Next; - + syntax::Leaf *Last = nullptr; + for (auto *C = getFirstChild(); C; C = C->getNextSibling()) { if (auto *L = dyn_cast(C)) - return L; - T = cast(C); + Last = L; + else if (auto *L = cast(C)->findLastLeaf()) + Last = L; } - return nullptr; + return Last; } syntax::Node *syntax::Tree::findChild(NodeRole R) { diff --git a/clang/unittests/Tooling/Syntax/CMakeLists.txt b/clang/unittests/Tooling/Syntax/CMakeLists.txt --- a/clang/unittests/Tooling/Syntax/CMakeLists.txt +++ b/clang/unittests/Tooling/Syntax/CMakeLists.txt @@ -7,6 +7,7 @@ BuildTreeTest.cpp MutationsTest.cpp SynthesisTest.cpp + TreeTest.cpp TokensTest.cpp ) diff --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp @@ -0,0 +1,123 @@ +//===- TreeTest.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 "TreeTestBase.h" +#include "clang/Tooling/Syntax/BuildTree.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace clang::syntax; + +namespace { + +class TreeTest : public SyntaxTreeTest { +private: + Tree *createTree(ArrayRef Children) { + std::vector> ChildrenWithRoles; + ChildrenWithRoles.reserve(Children.size()); + for (const auto *Child : Children) { + ChildrenWithRoles.push_back( + std::make_pair(deepCopy(*Arena, Child), NodeRole::Unknown)); + } + return clang::syntax::createTree(*Arena, ChildrenWithRoles, + NodeKind::UnknownExpression); + } + + // Generate Forests by combining `Children` into `ParentCount` Trees. + // + // We do this recursively. + std::vector> + generateAllForests(ArrayRef Children, unsigned ParentCount) { + assert(ParentCount > 0); + // If there is only one Parent node, then combine `Children` under + // this Parent. + if (ParentCount == 1) + return {{createTree(Children)}}; + + // Otherwise, combine `ChildrenCount` children under the last parent and + // solve the smaller problem without these children and this parent. Do this + // for every `ChildrenCount` and combine the results. + std::vector> AllForests; + for (unsigned ChildrenCount = 0; ChildrenCount <= Children.size(); + ++ChildrenCount) { + auto *LastParent = createTree(Children.take_back(ChildrenCount)); + for (auto &Forest : generateAllForests(Children.drop_back(ChildrenCount), + ParentCount - 1)) { + Forest.push_back(LastParent); + AllForests.push_back(Forest); + } + } + return AllForests; + } + +protected: + // Generates all trees with a `Base` of `Node`s and `NodeCountPerLayer` + // `Node`s per layer. An example of Tree with `Base` = {`(`, `)`} and + // `NodeCountPerLayer` = {2, 2}: + // Tree + // |-Tree + // `-Tree + // |-Tree + // | `-'(' + // `-Tree + // `-')' + std::vector + generateAllTreesWithShape(ArrayRef Base, + ArrayRef NodeCountPerLayer) { + auto GenerateNextLayer = [this](ArrayRef> Layer, + unsigned NextLayerNodeCount) { + std::vector> NextLayer; + for (const auto &Base : Layer) { + for (const auto &NextBase : + generateAllForests(Base, NextLayerNodeCount)) { + NextLayer.push_back( + std::vector(NextBase.begin(), NextBase.end())); + } + } + return NextLayer; + }; + + std::vector> Layer = {Base}; + for (auto NodeCount : NodeCountPerLayer) { + Layer = GenerateNextLayer(Layer, NodeCount); + } + + std::vector AllTrees; + AllTrees.reserve(Layer.size()); + for (const auto &Base : Layer) { + AllTrees.push_back(createTree(Base)); + } + return AllTrees; + } +}; + +INSTANTIATE_TEST_CASE_P(TreeTests, TreeTest, + ::testing::ValuesIn(allTestClangConfigs()), ); + +TEST_P(TreeTest, FirstLeaf) { + buildTree("", GetParam()); + std::vector Leafs = {createLeaf(*Arena, tok::l_paren), + createLeaf(*Arena, tok::r_paren)}; + for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) { + ASSERT_TRUE(Tree->findFirstLeaf() != nullptr); + EXPECT_EQ(Tree->findFirstLeaf()->getToken()->kind(), tok::l_paren); + } +} + +TEST_P(TreeTest, LastLeaf) { + buildTree("", GetParam()); + std::vector Leafs = {createLeaf(*Arena, tok::l_paren), + createLeaf(*Arena, tok::r_paren)}; + for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) { + ASSERT_TRUE(Tree->findLastLeaf() != nullptr); + EXPECT_EQ(Tree->findLastLeaf()->getToken()->kind(), tok::r_paren); + } +} + +} // namespace