1 //===- TreeTest.cpp ---------------------------------------------*- C++ -*-===//
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
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
;
21 using testing::ElementsAre
;
23 class TreeTest
: public SyntaxTreeTest
{
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
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();
53 auto *LastParent
= createTree(Children
.take_back(ChildrenCount
));
54 for (auto &Forest
: generateAllForests(Children
.drop_back(ChildrenCount
),
56 Forest
.push_back(LastParent
);
57 AllForests
.push_back(Forest
);
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}:
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
)) {
87 std::vector
<const Node
*>(NextBase
.begin(), NextBase
.end()));
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
));
106 INSTANTIATE_TEST_SUITE_P(TreeTests
, TreeTest
,
107 ::testing::ValuesIn(allTestClangConfigs()) );
109 TEST_P(TreeTest
, FirstLeaf
) {
110 buildTree("", GetParam());
111 std::vector
<const Node
*> Leafs
= {createLeaf(*Arena
, *TM
, tok::l_paren
),
112 createLeaf(*Arena
, *TM
, tok::r_paren
)};
113 for (const auto *Tree
: generateAllTreesWithShape(Leafs
, {3u})) {
114 ASSERT_TRUE(Tree
->findFirstLeaf() != nullptr);
115 EXPECT_EQ(TM
->getToken(Tree
->findFirstLeaf()->getTokenKey())->kind(), tok::l_paren
);
119 TEST_P(TreeTest
, LastLeaf
) {
120 buildTree("", GetParam());
121 std::vector
<const Node
*> Leafs
= {createLeaf(*Arena
, *TM
, tok::l_paren
),
122 createLeaf(*Arena
, *TM
, tok::r_paren
)};
123 for (const auto *Tree
: generateAllTreesWithShape(Leafs
, {3u})) {
124 ASSERT_TRUE(Tree
->findLastLeaf() != nullptr);
125 EXPECT_EQ(TM
->getToken(Tree
->findLastLeaf()->getTokenKey())->kind(), tok::r_paren
);
129 TEST_F(TreeTest
, Iterators
) {
130 buildTree("", allTestClangConfigs().front());
131 std::vector
<Node
*> Children
= {createLeaf(*Arena
, *TM
, tok::identifier
, "a"),
132 createLeaf(*Arena
, *TM
, tok::identifier
, "b"),
133 createLeaf(*Arena
, *TM
, tok::identifier
, "c")};
134 auto *Tree
= syntax::createTree(*Arena
,
135 {{Children
[0], NodeRole::LeftHandSide
},
136 {Children
[1], NodeRole::OperatorToken
},
137 {Children
[2], NodeRole::RightHandSide
}},
138 NodeKind::TranslationUnit
);
139 const auto *ConstTree
= Tree
;
141 auto Range
= Tree
->getChildren();
142 EXPECT_THAT(Range
, ElementsAre(role(NodeRole::LeftHandSide
),
143 role(NodeRole::OperatorToken
),
144 role(NodeRole::RightHandSide
)));
146 auto ConstRange
= ConstTree
->getChildren();
147 EXPECT_THAT(ConstRange
, ElementsAre(role(NodeRole::LeftHandSide
),
148 role(NodeRole::OperatorToken
),
149 role(NodeRole::RightHandSide
)));
151 // FIXME: mutate and observe no invalidation. Mutations are private for now...
152 auto It
= Range
.begin();
153 auto CIt
= ConstRange
.begin();
154 static_assert(std::is_same_v
<decltype(*It
), syntax::Node
&>, "mutable range");
155 static_assert(std::is_same_v
<decltype(*CIt
), const syntax::Node
&>,
158 for (unsigned I
= 0; I
< 3; ++I
) {
162 EXPECT_EQ(It
.asPointer(), Children
[I
]);
163 EXPECT_EQ(CIt
.asPointer(), Children
[I
]);
164 EXPECT_EQ(&*It
, Children
[I
]);
165 EXPECT_EQ(&*CIt
, Children
[I
]);
170 EXPECT_EQ(It
, Tree::ChildIterator());
171 EXPECT_EQ(CIt
, Tree::ConstChildIterator());
174 EXPECT_EQ(nullptr, It
.asPointer());
175 EXPECT_EQ(nullptr, CIt
.asPointer());
178 class ListTest
: public SyntaxTreeTest
{
180 std::string
dumpQuotedTokensOrNull(const Node
*N
) {
182 StringRef(N
->dumpTokens(*TM
))
191 dumpElementsAndDelimiters(ArrayRef
<List::ElementAndDelimiter
<Node
>> EDs
) {
193 llvm::raw_string_ostream
OS(Storage
);
197 llvm::interleaveComma(
198 EDs
, OS
, [&OS
, this](const List::ElementAndDelimiter
<Node
> &ED
) {
199 OS
<< "(" << dumpQuotedTokensOrNull(ED
.element
) << ", "
200 << dumpQuotedTokensOrNull(ED
.delimiter
) << ")";
208 std::string
dumpNodes(ArrayRef
<Node
*> Nodes
) {
210 llvm::raw_string_ostream
OS(Storage
);
214 llvm::interleaveComma(Nodes
, OS
, [&OS
, this](const Node
*N
) {
215 OS
<< dumpQuotedTokensOrNull(N
);
224 INSTANTIATE_TEST_SUITE_P(TreeTests
, ListTest
,
225 ::testing::ValuesIn(allTestClangConfigs()) );
227 /// "a, b, c" <=> [("a", ","), ("b", ","), ("c", null)]
228 TEST_P(ListTest
, List_Separated_WellFormed
) {
229 buildTree("", GetParam());
232 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
235 {createLeaf(*Arena
, *TM
, tok::identifier
, "a"), NodeRole::ListElement
},
236 {createLeaf(*Arena
, *TM
, tok::comma
), NodeRole::ListDelimiter
},
237 {createLeaf(*Arena
, *TM
, tok::identifier
, "b"), NodeRole::ListElement
},
238 {createLeaf(*Arena
, *TM
, tok::comma
), NodeRole::ListDelimiter
},
239 {createLeaf(*Arena
, *TM
, tok::identifier
, "c"), NodeRole::ListElement
},
241 NodeKind::CallArguments
));
243 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
244 "[('a', ','), ('b', ','), ('c', null)]");
245 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");
248 /// "a, , c" <=> [("a", ","), (null, ","), ("c", null)]
249 TEST_P(ListTest
, List_Separated_MissingElement
) {
250 buildTree("", GetParam());
253 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
256 {createLeaf(*Arena
, *TM
, tok::identifier
, "a"), NodeRole::ListElement
},
257 {createLeaf(*Arena
, *TM
, tok::comma
), NodeRole::ListDelimiter
},
258 {createLeaf(*Arena
, *TM
, tok::comma
), NodeRole::ListDelimiter
},
259 {createLeaf(*Arena
, *TM
, tok::identifier
, "c"), NodeRole::ListElement
},
261 NodeKind::CallArguments
));
263 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
264 "[('a', ','), (null, ','), ('c', null)]");
265 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', null, 'c']");
268 /// "a, b c" <=> [("a", ","), ("b", null), ("c", null)]
269 TEST_P(ListTest
, List_Separated_MissingDelimiter
) {
270 buildTree("", GetParam());
273 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
276 {createLeaf(*Arena
, *TM
, tok::identifier
, "a"), NodeRole::ListElement
},
277 {createLeaf(*Arena
, *TM
, tok::comma
), NodeRole::ListDelimiter
},
278 {createLeaf(*Arena
, *TM
, tok::identifier
, "b"), NodeRole::ListElement
},
279 {createLeaf(*Arena
, *TM
, tok::identifier
, "c"), NodeRole::ListElement
},
281 NodeKind::CallArguments
));
283 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
284 "[('a', ','), ('b', null), ('c', null)]");
285 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");
288 /// "a, b," <=> [("a", ","), ("b", ","), (null, null)]
289 TEST_P(ListTest
, List_Separated_MissingLastElement
) {
290 buildTree("", GetParam());
293 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
296 {createLeaf(*Arena
, *TM
, tok::identifier
, "a"), NodeRole::ListElement
},
297 {createLeaf(*Arena
, *TM
, tok::comma
), NodeRole::ListDelimiter
},
298 {createLeaf(*Arena
, *TM
, tok::identifier
, "b"), NodeRole::ListElement
},
299 {createLeaf(*Arena
, *TM
, tok::comma
), NodeRole::ListDelimiter
},
301 NodeKind::CallArguments
));
303 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
304 "[('a', ','), ('b', ','), (null, null)]");
305 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', null]");
308 /// "a:: b:: c::" <=> [("a", "::"), ("b", "::"), ("c", "::")]
309 TEST_P(ListTest
, List_Terminated_WellFormed
) {
310 if (!GetParam().isCXX()) {
313 buildTree("", GetParam());
316 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
319 {createLeaf(*Arena
, *TM
, tok::identifier
, "a"), NodeRole::ListElement
},
320 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
321 {createLeaf(*Arena
, *TM
, tok::identifier
, "b"), NodeRole::ListElement
},
322 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
323 {createLeaf(*Arena
, *TM
, tok::identifier
, "c"), NodeRole::ListElement
},
324 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
326 NodeKind::NestedNameSpecifier
));
328 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
329 "[('a', '::'), ('b', '::'), ('c', '::')]");
330 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");
333 /// "a:: :: c::" <=> [("a", "::"), (null, "::"), ("c", "::")]
334 TEST_P(ListTest
, List_Terminated_MissingElement
) {
335 if (!GetParam().isCXX()) {
338 buildTree("", GetParam());
341 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
344 {createLeaf(*Arena
, *TM
, tok::identifier
, "a"), NodeRole::ListElement
},
345 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
346 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
347 {createLeaf(*Arena
, *TM
, tok::identifier
, "c"), NodeRole::ListElement
},
348 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
350 NodeKind::NestedNameSpecifier
));
352 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
353 "[('a', '::'), (null, '::'), ('c', '::')]");
354 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', null, 'c']");
357 /// "a:: b c::" <=> [("a", "::"), ("b", null), ("c", "::")]
358 TEST_P(ListTest
, List_Terminated_MissingDelimiter
) {
359 if (!GetParam().isCXX()) {
362 buildTree("", GetParam());
365 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
368 {createLeaf(*Arena
, *TM
, tok::identifier
, "a"), NodeRole::ListElement
},
369 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
370 {createLeaf(*Arena
, *TM
, tok::identifier
, "b"), NodeRole::ListElement
},
371 {createLeaf(*Arena
, *TM
, tok::identifier
, "c"), NodeRole::ListElement
},
372 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
374 NodeKind::NestedNameSpecifier
));
376 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
377 "[('a', '::'), ('b', null), ('c', '::')]");
378 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");
381 /// "a:: b:: c" <=> [("a", "::"), ("b", "::"), ("c", null)]
382 TEST_P(ListTest
, List_Terminated_MissingLastDelimiter
) {
383 if (!GetParam().isCXX()) {
386 buildTree("", GetParam());
389 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
392 {createLeaf(*Arena
, *TM
, tok::identifier
, "a"), NodeRole::ListElement
},
393 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
394 {createLeaf(*Arena
, *TM
, tok::identifier
, "b"), NodeRole::ListElement
},
395 {createLeaf(*Arena
, *TM
, tok::coloncolon
), NodeRole::ListDelimiter
},
396 {createLeaf(*Arena
, *TM
, tok::identifier
, "c"), NodeRole::ListElement
},
398 NodeKind::NestedNameSpecifier
));
400 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
401 "[('a', '::'), ('b', '::'), ('c', null)]");
402 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");