[OptTable] Fix typo VALUE => VALUES (NFCI) (#121523)
[llvm-project.git] / clang / unittests / Tooling / Syntax / TreeTest.cpp
blobb553f704ae00cd2159bfce9ac7035ecb23e46f5f
1 //===- TreeTest.cpp ---------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "clang/Tooling/Syntax/Tree.h"
10 #include "TreeTestBase.h"
11 #include "clang/Basic/SourceManager.h"
12 #include "clang/Tooling/Syntax/BuildTree.h"
13 #include "clang/Tooling/Syntax/Nodes.h"
14 #include "llvm/ADT/STLExtras.h"
15 #include "gtest/gtest.h"
17 using namespace clang;
18 using namespace clang::syntax;
20 namespace {
21 using testing::ElementsAre;
23 class TreeTest : public SyntaxTreeTest {
24 private:
25 Tree *createTree(ArrayRef<const Node *> Children) {
26 std::vector<std::pair<Node *, NodeRole>> ChildrenWithRoles;
27 ChildrenWithRoles.reserve(Children.size());
28 for (const auto *Child : Children) {
29 ChildrenWithRoles.push_back(std::make_pair(
30 deepCopyExpandingMacros(*Arena, *TM, Child), NodeRole::Unknown));
32 return clang::syntax::createTree(*Arena, ChildrenWithRoles,
33 NodeKind::UnknownExpression);
36 // Generate Forests by combining `Children` into `ParentCount` Trees.
38 // We do this recursively.
39 std::vector<std::vector<const Tree *>>
40 generateAllForests(ArrayRef<const Node *> Children, unsigned ParentCount) {
41 assert(ParentCount > 0);
42 // If there is only one Parent node, then combine `Children` under
43 // this Parent.
44 if (ParentCount == 1)
45 return {{createTree(Children)}};
47 // Otherwise, combine `ChildrenCount` children under the last parent and
48 // solve the smaller problem without these children and this parent. Do this
49 // for every `ChildrenCount` and combine the results.
50 std::vector<std::vector<const Tree *>> AllForests;
51 for (unsigned ChildrenCount = 0; ChildrenCount <= Children.size();
52 ++ChildrenCount) {
53 auto *LastParent = createTree(Children.take_back(ChildrenCount));
54 for (auto &Forest : generateAllForests(Children.drop_back(ChildrenCount),
55 ParentCount - 1)) {
56 Forest.push_back(LastParent);
57 AllForests.push_back(Forest);
60 return AllForests;
63 protected:
64 // Generates all trees with a `Base` of `Node`s and `NodeCountPerLayer`
65 // `Node`s per layer. An example of Tree with `Base` = {`(`, `)`} and
66 // `NodeCountPerLayer` = {2, 2}:
67 // Tree
68 // |-Tree
69 // `-Tree
70 // |-Tree
71 // | `-'('
72 // `-Tree
73 // `-')'
74 std::vector<const Tree *>
75 generateAllTreesWithShape(ArrayRef<const Node *> Base,
76 ArrayRef<unsigned> NodeCountPerLayer) {
77 // We compute the solution per layer. A layer is a collection of bases,
78 // where each base has the same number of nodes, given by
79 // `NodeCountPerLayer`.
80 auto GenerateNextLayer = [this](ArrayRef<std::vector<const Node *>> Layer,
81 unsigned NextLayerNodeCount) {
82 std::vector<std::vector<const Node *>> NextLayer;
83 for (const auto &Base : Layer) {
84 for (const auto &NextBase :
85 generateAllForests(Base, NextLayerNodeCount)) {
86 NextLayer.push_back(
87 std::vector<const Node *>(NextBase.begin(), NextBase.end()));
90 return NextLayer;
93 std::vector<std::vector<const Node *>> Layer = {Base};
94 for (auto NodeCount : NodeCountPerLayer)
95 Layer = GenerateNextLayer(Layer, NodeCount);
97 std::vector<const Tree *> AllTrees;
98 AllTrees.reserve(Layer.size());
99 for (const auto &Base : Layer)
100 AllTrees.push_back(createTree(Base));
102 return AllTrees;
106 INSTANTIATE_TEST_SUITE_P(
107 TreeTests, TreeTest, ::testing::ValuesIn(allTestClangConfigs()),
108 [](const testing::TestParamInfo<TestClangConfig> &Info) {
109 return Info.param.toShortString();
112 TEST_P(TreeTest, FirstLeaf) {
113 buildTree("", GetParam());
114 std::vector<const Node *> Leafs = {createLeaf(*Arena, *TM, tok::l_paren),
115 createLeaf(*Arena, *TM, tok::r_paren)};
116 for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) {
117 ASSERT_TRUE(Tree->findFirstLeaf() != nullptr);
118 EXPECT_EQ(TM->getToken(Tree->findFirstLeaf()->getTokenKey())->kind(), tok::l_paren);
122 TEST_P(TreeTest, LastLeaf) {
123 buildTree("", GetParam());
124 std::vector<const Node *> Leafs = {createLeaf(*Arena, *TM, tok::l_paren),
125 createLeaf(*Arena, *TM, tok::r_paren)};
126 for (const auto *Tree : generateAllTreesWithShape(Leafs, {3u})) {
127 ASSERT_TRUE(Tree->findLastLeaf() != nullptr);
128 EXPECT_EQ(TM->getToken(Tree->findLastLeaf()->getTokenKey())->kind(), tok::r_paren);
132 TEST_F(TreeTest, Iterators) {
133 buildTree("", allTestClangConfigs().front());
134 std::vector<Node *> Children = {createLeaf(*Arena, *TM, tok::identifier, "a"),
135 createLeaf(*Arena, *TM, tok::identifier, "b"),
136 createLeaf(*Arena, *TM, tok::identifier, "c")};
137 auto *Tree = syntax::createTree(*Arena,
138 {{Children[0], NodeRole::LeftHandSide},
139 {Children[1], NodeRole::OperatorToken},
140 {Children[2], NodeRole::RightHandSide}},
141 NodeKind::TranslationUnit);
142 const auto *ConstTree = Tree;
144 auto Range = Tree->getChildren();
145 EXPECT_THAT(Range, ElementsAre(role(NodeRole::LeftHandSide),
146 role(NodeRole::OperatorToken),
147 role(NodeRole::RightHandSide)));
149 auto ConstRange = ConstTree->getChildren();
150 EXPECT_THAT(ConstRange, ElementsAre(role(NodeRole::LeftHandSide),
151 role(NodeRole::OperatorToken),
152 role(NodeRole::RightHandSide)));
154 // FIXME: mutate and observe no invalidation. Mutations are private for now...
155 auto It = Range.begin();
156 auto CIt = ConstRange.begin();
157 static_assert(std::is_same_v<decltype(*It), syntax::Node &>, "mutable range");
158 static_assert(std::is_same_v<decltype(*CIt), const syntax::Node &>,
159 "const range");
161 for (unsigned I = 0; I < 3; ++I) {
162 EXPECT_EQ(It, CIt);
163 EXPECT_TRUE(It);
164 EXPECT_TRUE(CIt);
165 EXPECT_EQ(It.asPointer(), Children[I]);
166 EXPECT_EQ(CIt.asPointer(), Children[I]);
167 EXPECT_EQ(&*It, Children[I]);
168 EXPECT_EQ(&*CIt, Children[I]);
169 ++It;
170 ++CIt;
172 EXPECT_EQ(It, CIt);
173 EXPECT_EQ(It, Tree::ChildIterator());
174 EXPECT_EQ(CIt, Tree::ConstChildIterator());
175 EXPECT_FALSE(It);
176 EXPECT_FALSE(CIt);
177 EXPECT_EQ(nullptr, It.asPointer());
178 EXPECT_EQ(nullptr, CIt.asPointer());
181 class ListTest : public SyntaxTreeTest {
182 private:
183 std::string dumpQuotedTokensOrNull(const Node *N) {
184 return N ? "'" +
185 StringRef(N->dumpTokens(*TM))
186 .trim()
187 .str() +
189 : "null";
192 protected:
193 std::string
194 dumpElementsAndDelimiters(ArrayRef<List::ElementAndDelimiter<Node>> EDs) {
195 std::string Storage;
196 llvm::raw_string_ostream OS(Storage);
198 OS << "[";
200 llvm::interleaveComma(
201 EDs, OS, [&OS, this](const List::ElementAndDelimiter<Node> &ED) {
202 OS << "(" << dumpQuotedTokensOrNull(ED.element) << ", "
203 << dumpQuotedTokensOrNull(ED.delimiter) << ")";
206 OS << "]";
208 return Storage;
211 std::string dumpNodes(ArrayRef<Node *> Nodes) {
212 std::string Storage;
213 llvm::raw_string_ostream OS(Storage);
215 OS << "[";
217 llvm::interleaveComma(Nodes, OS, [&OS, this](const Node *N) {
218 OS << dumpQuotedTokensOrNull(N);
221 OS << "]";
223 return Storage;
227 INSTANTIATE_TEST_SUITE_P(
228 TreeTests, ListTest, ::testing::ValuesIn(allTestClangConfigs()),
229 [](const testing::TestParamInfo<TestClangConfig> &Info) {
230 return Info.param.toShortString();
233 /// "a, b, c" <=> [("a", ","), ("b", ","), ("c", null)]
234 TEST_P(ListTest, List_Separated_WellFormed) {
235 buildTree("", GetParam());
237 // "a, b, c"
238 auto *List = dyn_cast<syntax::List>(syntax::createTree(
239 *Arena,
241 {createLeaf(*Arena, *TM, tok::identifier, "a"), NodeRole::ListElement},
242 {createLeaf(*Arena, *TM, tok::comma), NodeRole::ListDelimiter},
243 {createLeaf(*Arena, *TM, tok::identifier, "b"), NodeRole::ListElement},
244 {createLeaf(*Arena, *TM, tok::comma), NodeRole::ListDelimiter},
245 {createLeaf(*Arena, *TM, tok::identifier, "c"), NodeRole::ListElement},
247 NodeKind::CallArguments));
249 EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
250 "[('a', ','), ('b', ','), ('c', null)]");
251 EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
254 /// "a, , c" <=> [("a", ","), (null, ","), ("c", null)]
255 TEST_P(ListTest, List_Separated_MissingElement) {
256 buildTree("", GetParam());
258 // "a, , c"
259 auto *List = dyn_cast<syntax::List>(syntax::createTree(
260 *Arena,
262 {createLeaf(*Arena, *TM, tok::identifier, "a"), NodeRole::ListElement},
263 {createLeaf(*Arena, *TM, tok::comma), NodeRole::ListDelimiter},
264 {createLeaf(*Arena, *TM, tok::comma), NodeRole::ListDelimiter},
265 {createLeaf(*Arena, *TM, tok::identifier, "c"), NodeRole::ListElement},
267 NodeKind::CallArguments));
269 EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
270 "[('a', ','), (null, ','), ('c', null)]");
271 EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', null, 'c']");
274 /// "a, b c" <=> [("a", ","), ("b", null), ("c", null)]
275 TEST_P(ListTest, List_Separated_MissingDelimiter) {
276 buildTree("", GetParam());
278 // "a, b c"
279 auto *List = dyn_cast<syntax::List>(syntax::createTree(
280 *Arena,
282 {createLeaf(*Arena, *TM, tok::identifier, "a"), NodeRole::ListElement},
283 {createLeaf(*Arena, *TM, tok::comma), NodeRole::ListDelimiter},
284 {createLeaf(*Arena, *TM, tok::identifier, "b"), NodeRole::ListElement},
285 {createLeaf(*Arena, *TM, tok::identifier, "c"), NodeRole::ListElement},
287 NodeKind::CallArguments));
289 EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
290 "[('a', ','), ('b', null), ('c', null)]");
291 EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
294 /// "a, b," <=> [("a", ","), ("b", ","), (null, null)]
295 TEST_P(ListTest, List_Separated_MissingLastElement) {
296 buildTree("", GetParam());
298 // "a, b, c"
299 auto *List = dyn_cast<syntax::List>(syntax::createTree(
300 *Arena,
302 {createLeaf(*Arena, *TM, tok::identifier, "a"), NodeRole::ListElement},
303 {createLeaf(*Arena, *TM, tok::comma), NodeRole::ListDelimiter},
304 {createLeaf(*Arena, *TM, tok::identifier, "b"), NodeRole::ListElement},
305 {createLeaf(*Arena, *TM, tok::comma), NodeRole::ListDelimiter},
307 NodeKind::CallArguments));
309 EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
310 "[('a', ','), ('b', ','), (null, null)]");
311 EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', null]");
314 /// "a:: b:: c::" <=> [("a", "::"), ("b", "::"), ("c", "::")]
315 TEST_P(ListTest, List_Terminated_WellFormed) {
316 if (!GetParam().isCXX()) {
317 return;
319 buildTree("", GetParam());
321 // "a:: b:: c::"
322 auto *List = dyn_cast<syntax::List>(syntax::createTree(
323 *Arena,
325 {createLeaf(*Arena, *TM, tok::identifier, "a"), NodeRole::ListElement},
326 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
327 {createLeaf(*Arena, *TM, tok::identifier, "b"), NodeRole::ListElement},
328 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
329 {createLeaf(*Arena, *TM, tok::identifier, "c"), NodeRole::ListElement},
330 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
332 NodeKind::NestedNameSpecifier));
334 EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
335 "[('a', '::'), ('b', '::'), ('c', '::')]");
336 EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
339 /// "a:: :: c::" <=> [("a", "::"), (null, "::"), ("c", "::")]
340 TEST_P(ListTest, List_Terminated_MissingElement) {
341 if (!GetParam().isCXX()) {
342 return;
344 buildTree("", GetParam());
346 // "a:: b:: c::"
347 auto *List = dyn_cast<syntax::List>(syntax::createTree(
348 *Arena,
350 {createLeaf(*Arena, *TM, tok::identifier, "a"), NodeRole::ListElement},
351 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
352 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
353 {createLeaf(*Arena, *TM, tok::identifier, "c"), NodeRole::ListElement},
354 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
356 NodeKind::NestedNameSpecifier));
358 EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
359 "[('a', '::'), (null, '::'), ('c', '::')]");
360 EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', null, 'c']");
363 /// "a:: b c::" <=> [("a", "::"), ("b", null), ("c", "::")]
364 TEST_P(ListTest, List_Terminated_MissingDelimiter) {
365 if (!GetParam().isCXX()) {
366 return;
368 buildTree("", GetParam());
370 // "a:: b c::"
371 auto *List = dyn_cast<syntax::List>(syntax::createTree(
372 *Arena,
374 {createLeaf(*Arena, *TM, tok::identifier, "a"), NodeRole::ListElement},
375 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
376 {createLeaf(*Arena, *TM, tok::identifier, "b"), NodeRole::ListElement},
377 {createLeaf(*Arena, *TM, tok::identifier, "c"), NodeRole::ListElement},
378 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
380 NodeKind::NestedNameSpecifier));
382 EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
383 "[('a', '::'), ('b', null), ('c', '::')]");
384 EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
387 /// "a:: b:: c" <=> [("a", "::"), ("b", "::"), ("c", null)]
388 TEST_P(ListTest, List_Terminated_MissingLastDelimiter) {
389 if (!GetParam().isCXX()) {
390 return;
392 buildTree("", GetParam());
394 // "a:: b:: c"
395 auto *List = dyn_cast<syntax::List>(syntax::createTree(
396 *Arena,
398 {createLeaf(*Arena, *TM, tok::identifier, "a"), NodeRole::ListElement},
399 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
400 {createLeaf(*Arena, *TM, tok::identifier, "b"), NodeRole::ListElement},
401 {createLeaf(*Arena, *TM, tok::coloncolon), NodeRole::ListDelimiter},
402 {createLeaf(*Arena, *TM, tok::identifier, "c"), NodeRole::ListElement},
404 NodeKind::NestedNameSpecifier));
406 EXPECT_EQ(dumpElementsAndDelimiters(List->getElementsAsNodesAndDelimiters()),
407 "[('a', '::'), ('b', '::'), ('c', null)]");
408 EXPECT_EQ(dumpNodes(List->getElementsAsNodes()), "['a', 'b', 'c']");
411 } // namespace