eduucaldas created this revision.
Herald added subscribers: cfe-commits, mgorny.
Herald added a project: clang.
eduucaldas requested review of this revision.

- 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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D87779

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

Index: clang/unittests/Tooling/Syntax/TreeTest.cpp
===================================================================
--- /dev/null
+++ clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -0,0 +1,141 @@
+//===- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file tests the Syntax Tree base API.
+//
+//===----------------------------------------------------------------------===//
+
+#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 {
+protected:
+  ::testing::AssertionResult treeDumpEqual(const syntax::Node *Root,
+                                           StringRef Dump) {
+    if (!Root)
+      return ::testing::AssertionFailure()
+             << "Root was not built successfully.";
+
+    auto Actual = StringRef(Root->dump(Arena->getSourceManager())).trim().str();
+    auto Expected = Dump.trim().str();
+    // EXPECT_EQ shows the diff between the two strings if they are different.
+    EXPECT_EQ(Expected, Actual);
+    if (Actual != Expected) {
+      return ::testing::AssertionFailure();
+    }
+    return ::testing::AssertionSuccess();
+  }
+
+  Tree *createTree(ArrayRef<const Node *> Children) {
+    auto ChildrenWithRoles = std::vector<std::pair<Node *, NodeRole>>();
+    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` under `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, we need to combine `Children` under
+    // this Parent.
+    if (ParentCount == 1)
+      return {{createTree(Children)}};
+
+    // Otherwise, we combine `ChildrenCount` children under the last parent and
+    // solve the smaller problem without these children and this parent. Finally
+    // we combine the results for every possible `ChildrenCount`.
+    auto AllForests = std::vector<std::vector<const Tree *>>();
+    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;
+  }
+
+  // Generates all trees with a `Base` layer 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) {
+    auto GenerateNextLayers = [this](ArrayRef<std::vector<const Node *>> Bases,
+                                     unsigned NodeCount) {
+      auto NextLayers = std::vector<std::vector<const Node *>>();
+      for (const auto &Base : Bases) {
+        for (const auto &NextLayer : generateAllForests(Base, NodeCount)) {
+          NextLayers.push_back(
+              std::vector<const Node *>(NextLayer.begin(), NextLayer.end()));
+        }
+      }
+      return NextLayers;
+    };
+
+    auto Layers = std::vector<std::vector<const Node *>>({Base});
+    for (auto NodeCount : NodeCountPerLayer) {
+      Layers = GenerateNextLayers(Layers, NodeCount);
+    }
+
+    auto AllTrees = std::vector<const Tree *>();
+    AllTrees.reserve(Layers.size());
+    for (const auto &Layer : Layers) {
+      AllTrees.push_back(createTree(Layer));
+    }
+    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
Index: clang/unittests/Tooling/Syntax/CMakeLists.txt
===================================================================
--- clang/unittests/Tooling/Syntax/CMakeLists.txt
+++ clang/unittests/Tooling/Syntax/CMakeLists.txt
@@ -7,6 +7,7 @@
   BuildTreeTest.cpp
   MutationsTest.cpp
   SynthesisTest.cpp
+  TreeTest.cpp
   TokensTest.cpp
 )
 
Index: clang/lib/Tooling/Syntax/Tree.cpp
===================================================================
--- clang/lib/Tooling/Syntax/Tree.cpp
+++ 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<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) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to