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
, 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_CASE_P(TreeTests
, TreeTest
,
107 ::testing::ValuesIn(allTestClangConfigs()), );
109 TEST_P(TreeTest
, FirstLeaf
) {
110 buildTree("", GetParam());
111 std::vector
<const Node
*> Leafs
= {createLeaf(*Arena
, tok::l_paren
),
112 createLeaf(*Arena
, tok::r_paren
)};
113 for (const auto *Tree
: generateAllTreesWithShape(Leafs
, {3u})) {
114 ASSERT_TRUE(Tree
->findFirstLeaf() != nullptr);
115 EXPECT_EQ(Tree
->findFirstLeaf()->getToken()->kind(), tok::l_paren
);
119 TEST_P(TreeTest
, LastLeaf
) {
120 buildTree("", GetParam());
121 std::vector
<const Node
*> Leafs
= {createLeaf(*Arena
, tok::l_paren
),
122 createLeaf(*Arena
, tok::r_paren
)};
123 for (const auto *Tree
: generateAllTreesWithShape(Leafs
, {3u})) {
124 ASSERT_TRUE(Tree
->findLastLeaf() != nullptr);
125 EXPECT_EQ(Tree
->findLastLeaf()->getToken()->kind(), tok::r_paren
);
129 TEST_F(TreeTest
, Iterators
) {
130 buildTree("", allTestClangConfigs().front());
131 std::vector
<Node
*> Children
= {createLeaf(*Arena
, tok::identifier
, "a"),
132 createLeaf(*Arena
, tok::identifier
, "b"),
133 createLeaf(*Arena
, 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
<decltype(*It
), syntax::Node
&>::value
,
156 static_assert(std::is_same
<decltype(*CIt
), const syntax::Node
&>::value
,
159 for (unsigned I
= 0; I
< 3; ++I
) {
163 EXPECT_EQ(It
.asPointer(), Children
[I
]);
164 EXPECT_EQ(CIt
.asPointer(), Children
[I
]);
165 EXPECT_EQ(&*It
, Children
[I
]);
166 EXPECT_EQ(&*CIt
, Children
[I
]);
171 EXPECT_EQ(It
, Tree::ChildIterator());
172 EXPECT_EQ(CIt
, Tree::ConstChildIterator());
175 EXPECT_EQ(nullptr, It
.asPointer());
176 EXPECT_EQ(nullptr, CIt
.asPointer());
179 class ListTest
: public SyntaxTreeTest
{
181 std::string
dumpQuotedTokensOrNull(const Node
*N
) {
183 StringRef(N
->dumpTokens(Arena
->getSourceManager()))
192 dumpElementsAndDelimiters(ArrayRef
<List::ElementAndDelimiter
<Node
>> EDs
) {
194 llvm::raw_string_ostream
OS(Storage
);
198 llvm::interleaveComma(
199 EDs
, OS
, [&OS
, this](const List::ElementAndDelimiter
<Node
> &ED
) {
200 OS
<< "(" << dumpQuotedTokensOrNull(ED
.element
) << ", "
201 << dumpQuotedTokensOrNull(ED
.delimiter
) << ")";
209 std::string
dumpNodes(ArrayRef
<Node
*> Nodes
) {
211 llvm::raw_string_ostream
OS(Storage
);
215 llvm::interleaveComma(Nodes
, OS
, [&OS
, this](const Node
*N
) {
216 OS
<< dumpQuotedTokensOrNull(N
);
225 INSTANTIATE_TEST_CASE_P(TreeTests
, ListTest
,
226 ::testing::ValuesIn(allTestClangConfigs()), );
228 /// "a, b, c" <=> [("a", ","), ("b", ","), ("c", null)]
229 TEST_P(ListTest
, List_Separated_WellFormed
) {
230 buildTree("", GetParam());
233 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
236 {createLeaf(*Arena
, tok::identifier
, "a"), NodeRole::ListElement
},
237 {createLeaf(*Arena
, tok::comma
), NodeRole::ListDelimiter
},
238 {createLeaf(*Arena
, tok::identifier
, "b"), NodeRole::ListElement
},
239 {createLeaf(*Arena
, tok::comma
), NodeRole::ListDelimiter
},
240 {createLeaf(*Arena
, tok::identifier
, "c"), NodeRole::ListElement
},
242 NodeKind::CallArguments
));
244 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
245 "[('a', ','), ('b', ','), ('c', null)]");
246 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");
249 /// "a, , c" <=> [("a", ","), (null, ","), ("c", null)]
250 TEST_P(ListTest
, List_Separated_MissingElement
) {
251 buildTree("", GetParam());
254 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
257 {createLeaf(*Arena
, tok::identifier
, "a"), NodeRole::ListElement
},
258 {createLeaf(*Arena
, tok::comma
), NodeRole::ListDelimiter
},
259 {createLeaf(*Arena
, tok::comma
), NodeRole::ListDelimiter
},
260 {createLeaf(*Arena
, tok::identifier
, "c"), NodeRole::ListElement
},
262 NodeKind::CallArguments
));
264 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
265 "[('a', ','), (null, ','), ('c', null)]");
266 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', null, 'c']");
269 /// "a, b c" <=> [("a", ","), ("b", null), ("c", null)]
270 TEST_P(ListTest
, List_Separated_MissingDelimiter
) {
271 buildTree("", GetParam());
274 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
277 {createLeaf(*Arena
, tok::identifier
, "a"), NodeRole::ListElement
},
278 {createLeaf(*Arena
, tok::comma
), NodeRole::ListDelimiter
},
279 {createLeaf(*Arena
, tok::identifier
, "b"), NodeRole::ListElement
},
280 {createLeaf(*Arena
, tok::identifier
, "c"), NodeRole::ListElement
},
282 NodeKind::CallArguments
));
284 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
285 "[('a', ','), ('b', null), ('c', null)]");
286 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");
289 /// "a, b," <=> [("a", ","), ("b", ","), (null, null)]
290 TEST_P(ListTest
, List_Separated_MissingLastElement
) {
291 buildTree("", GetParam());
294 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
297 {createLeaf(*Arena
, tok::identifier
, "a"), NodeRole::ListElement
},
298 {createLeaf(*Arena
, tok::comma
), NodeRole::ListDelimiter
},
299 {createLeaf(*Arena
, tok::identifier
, "b"), NodeRole::ListElement
},
300 {createLeaf(*Arena
, tok::comma
), NodeRole::ListDelimiter
},
302 NodeKind::CallArguments
));
304 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
305 "[('a', ','), ('b', ','), (null, null)]");
306 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', null]");
309 /// "a:: b:: c::" <=> [("a", "::"), ("b", "::"), ("c", "::")]
310 TEST_P(ListTest
, List_Terminated_WellFormed
) {
311 if (!GetParam().isCXX()) {
314 buildTree("", GetParam());
317 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
320 {createLeaf(*Arena
, tok::identifier
, "a"), NodeRole::ListElement
},
321 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
322 {createLeaf(*Arena
, tok::identifier
, "b"), NodeRole::ListElement
},
323 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
324 {createLeaf(*Arena
, tok::identifier
, "c"), NodeRole::ListElement
},
325 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
327 NodeKind::NestedNameSpecifier
));
329 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
330 "[('a', '::'), ('b', '::'), ('c', '::')]");
331 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");
334 /// "a:: :: c::" <=> [("a", "::"), (null, "::"), ("c", "::")]
335 TEST_P(ListTest
, List_Terminated_MissingElement
) {
336 if (!GetParam().isCXX()) {
339 buildTree("", GetParam());
342 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
345 {createLeaf(*Arena
, tok::identifier
, "a"), NodeRole::ListElement
},
346 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
347 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
348 {createLeaf(*Arena
, tok::identifier
, "c"), NodeRole::ListElement
},
349 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
351 NodeKind::NestedNameSpecifier
));
353 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
354 "[('a', '::'), (null, '::'), ('c', '::')]");
355 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', null, 'c']");
358 /// "a:: b c::" <=> [("a", "::"), ("b", null), ("c", "::")]
359 TEST_P(ListTest
, List_Terminated_MissingDelimiter
) {
360 if (!GetParam().isCXX()) {
363 buildTree("", GetParam());
366 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
369 {createLeaf(*Arena
, tok::identifier
, "a"), NodeRole::ListElement
},
370 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
371 {createLeaf(*Arena
, tok::identifier
, "b"), NodeRole::ListElement
},
372 {createLeaf(*Arena
, tok::identifier
, "c"), NodeRole::ListElement
},
373 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
375 NodeKind::NestedNameSpecifier
));
377 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
378 "[('a', '::'), ('b', null), ('c', '::')]");
379 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");
382 /// "a:: b:: c" <=> [("a", "::"), ("b", "::"), ("c", null)]
383 TEST_P(ListTest
, List_Terminated_MissingLastDelimiter
) {
384 if (!GetParam().isCXX()) {
387 buildTree("", GetParam());
390 auto *List
= dyn_cast
<syntax::List
>(syntax::createTree(
393 {createLeaf(*Arena
, tok::identifier
, "a"), NodeRole::ListElement
},
394 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
395 {createLeaf(*Arena
, tok::identifier
, "b"), NodeRole::ListElement
},
396 {createLeaf(*Arena
, tok::coloncolon
), NodeRole::ListDelimiter
},
397 {createLeaf(*Arena
, tok::identifier
, "c"), NodeRole::ListElement
},
399 NodeKind::NestedNameSpecifier
));
401 EXPECT_EQ(dumpElementsAndDelimiters(List
->getElementsAsNodesAndDelimiters()),
402 "[('a', '::'), ('b', '::'), ('c', null)]");
403 EXPECT_EQ(dumpNodes(List
->getElementsAsNodes()), "['a', 'b', 'c']");