[clang] af582c9 - [SyntaxTree] Test `findFirstLeaf` and `findLastLeaf`

Eduardo Caldas via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 21 23:47:50 PDT 2020


Author: Eduardo Caldas
Date: 2020-09-22T06:47:36Z
New Revision: af582c9b0f3a09b6a1b5101fd30dcbcef5c188b0

URL: https://github.com/llvm/llvm-project/commit/af582c9b0f3a09b6a1b5101fd30dcbcef5c188b0
DIFF: https://github.com/llvm/llvm-project/commit/af582c9b0f3a09b6a1b5101fd30dcbcef5c188b0.diff

LOG: [SyntaxTree] Test `findFirstLeaf` and `findLastLeaf`

* Introduce `TreeTest.cpp` to unit test `Tree.h`
* Add `generateAllTreesWithShape` to generating test cases
* Add tests for `findFirstLeaf` and `findLastLeaf`
* Fix implementations of `findFirstLeaf` and `findLastLeaf` that had
been broken when empty `Tree` were present.

Differential Revision: https://reviews.llvm.org/D87779

Added: 
    clang/unittests/Tooling/Syntax/TreeTest.cpp

Modified: 
    clang/lib/Tooling/Syntax/Tree.cpp
    clang/unittests/Tooling/Syntax/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/lib/Tooling/Syntax/Tree.cpp b/clang/lib/Tooling/Syntax/Tree.cpp
index 2c77e8f64944..b558e7ab9a1b 100644
--- a/clang/lib/Tooling/Syntax/Tree.cpp
+++ b/clang/lib/Tooling/Syntax/Tree.cpp
@@ -255,27 +255,24 @@ void syntax::Node::assertInvariantsRecursive() const {
 }
 
 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<syntax::Leaf>(C))
       return L;
-    T = cast<syntax::Tree>(C);
+    if (auto *L = cast<syntax::Tree>(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<syntax::Leaf>(C))
-      return L;
-    T = cast<syntax::Tree>(C);
+      Last = L;
+    else if (auto *L = cast<syntax::Tree>(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
index 34a480503def..174f3e7bf573 100644
--- a/clang/unittests/Tooling/Syntax/CMakeLists.txt
+++ b/clang/unittests/Tooling/Syntax/CMakeLists.txt
@@ -7,6 +7,7 @@ add_clang_unittest(SyntaxTests
   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
index 000000000000..2448db36a465
--- /dev/null
+++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -0,0 +1,125 @@
+//===- 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<const Node *> Children) {
+    std::vector<std::pair<Node *, NodeRole>> 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<std::vector<const Tree *>>
+  generateAllForests(ArrayRef<const Node *> 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<std::vector<const Tree *>> 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<const Tree *>
+  generateAllTreesWithShape(ArrayRef<const Node *> Base,
+                            ArrayRef<unsigned> NodeCountPerLayer) {
+    // We compute the solution per layer. A layer is a collection of bases,
+    // where each base has the same number of nodes, given by
+    // `NodeCountPerLayer`.
+    auto GenerateNextLayer = [this](ArrayRef<std::vector<const Node *>> Layer,
+                                    unsigned NextLayerNodeCount) {
+      std::vector<std::vector<const Node *>> NextLayer;
+      for (const auto &Base : Layer) {
+        for (const auto &NextBase :
+             generateAllForests(Base, NextLayerNodeCount)) {
+          NextLayer.push_back(
+              std::vector<const Node *>(NextBase.begin(), NextBase.end()));
+        }
+      }
+      return NextLayer;
+    };
+
+    std::vector<std::vector<const Node *>> Layer = {Base};
+    for (auto NodeCount : NodeCountPerLayer)
+      Layer = GenerateNextLayer(Layer, NodeCount);
+
+    std::vector<const Tree *> 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<const Node *> 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<const Node *> 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


        


More information about the cfe-commits mailing list