xref: /llvm-project/clang/unittests/Tooling/Syntax/SynthesisTest.cpp (revision 7dfdca1961aadc75ca397818bfb9bd32f1879248)
1 //===- SynthesisTest.cpp --------------------------------------------------===//
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 //===----------------------------------------------------------------------===//
8 //
9 // This file tests synthesis API for syntax trees.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "TreeTestBase.h"
14 #include "clang/Tooling/Syntax/BuildTree.h"
15 #include "clang/Tooling/Syntax/Nodes.h"
16 #include "gtest/gtest.h"
17 
18 using namespace clang;
19 using namespace clang::syntax;
20 
21 namespace {
22 
23 class SynthesisTest : public SyntaxTreeTest {
24 protected:
25   ::testing::AssertionResult treeDumpEqual(syntax::Node *Root, StringRef Dump) {
26     if (!Root)
27       return ::testing::AssertionFailure()
28              << "Root was not built successfully.";
29 
30     auto Actual = StringRef(Root->dump(*TM)).trim().str();
31     auto Expected = Dump.trim().str();
32     // EXPECT_EQ shows the diff between the two strings if they are different.
33     EXPECT_EQ(Expected, Actual);
34     if (Actual != Expected) {
35       return ::testing::AssertionFailure();
36     }
37     return ::testing::AssertionSuccess();
38   }
39 };
40 
41 INSTANTIATE_TEST_SUITE_P(
42     SynthesisTests, SynthesisTest, ::testing::ValuesIn(allTestClangConfigs()),
43     [](const testing::TestParamInfo<TestClangConfig> &Info) {
44       return Info.param.toShortString();
45     });
46 
47 TEST_P(SynthesisTest, Leaf_Punctuation) {
48   buildTree("", GetParam());
49 
50   auto *Leaf = createLeaf(*Arena, *TM, tok::comma);
51 
52   EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
53 ',' Detached synthesized
54   )txt"));
55 }
56 
57 TEST_P(SynthesisTest, Leaf_Punctuation_CXX) {
58   if (!GetParam().isCXX())
59     return;
60 
61   buildTree("", GetParam());
62 
63   auto *Leaf = createLeaf(*Arena, *TM, tok::coloncolon);
64 
65   EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
66 '::' Detached synthesized
67   )txt"));
68 }
69 
70 TEST_P(SynthesisTest, Leaf_Keyword) {
71   buildTree("", GetParam());
72 
73   auto *Leaf = createLeaf(*Arena, *TM, tok::kw_if);
74 
75   EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
76 'if' Detached synthesized
77   )txt"));
78 }
79 
80 TEST_P(SynthesisTest, Leaf_Keyword_CXX11) {
81   if (!GetParam().isCXX11OrLater())
82     return;
83 
84   buildTree("", GetParam());
85 
86   auto *Leaf = createLeaf(*Arena, *TM, tok::kw_nullptr);
87 
88   EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
89 'nullptr' Detached synthesized
90   )txt"));
91 }
92 
93 TEST_P(SynthesisTest, Leaf_Identifier) {
94   buildTree("", GetParam());
95 
96   auto *Leaf = createLeaf(*Arena, *TM, tok::identifier, "a");
97 
98   EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
99 'a' Detached synthesized
100   )txt"));
101 }
102 
103 TEST_P(SynthesisTest, Leaf_Number) {
104   buildTree("", GetParam());
105 
106   auto *Leaf = createLeaf(*Arena, *TM, tok::numeric_constant, "1");
107 
108   EXPECT_TRUE(treeDumpEqual(Leaf, R"txt(
109 '1' Detached synthesized
110   )txt"));
111 }
112 
113 TEST_P(SynthesisTest, Tree_Empty) {
114   buildTree("", GetParam());
115 
116   auto *Tree = createTree(*Arena, {}, NodeKind::UnknownExpression);
117 
118   EXPECT_TRUE(treeDumpEqual(Tree, R"txt(
119 UnknownExpression Detached synthesized
120   )txt"));
121 }
122 
123 TEST_P(SynthesisTest, Tree_Flat) {
124   buildTree("", GetParam());
125 
126   auto *LeafLParen = createLeaf(*Arena, *TM, tok::l_paren);
127   auto *LeafRParen = createLeaf(*Arena, *TM, tok::r_paren);
128   auto *TreeParen = createTree(*Arena,
129                                {{LeafLParen, NodeRole::LeftHandSide},
130                                 {LeafRParen, NodeRole::RightHandSide}},
131                                NodeKind::ParenExpression);
132 
133   EXPECT_TRUE(treeDumpEqual(TreeParen, R"txt(
134 ParenExpression Detached synthesized
135 |-'(' LeftHandSide synthesized
136 `-')' RightHandSide synthesized
137   )txt"));
138 }
139 
140 TEST_P(SynthesisTest, Tree_OfTree) {
141   buildTree("", GetParam());
142 
143   auto *Leaf1 = createLeaf(*Arena, *TM, tok::numeric_constant, "1");
144   auto *Int1 = createTree(*Arena, {{Leaf1, NodeRole::LiteralToken}},
145                           NodeKind::IntegerLiteralExpression);
146 
147   auto *LeafPlus = createLeaf(*Arena, *TM, tok::plus);
148 
149   auto *Leaf2 = createLeaf(*Arena, *TM, tok::numeric_constant, "2");
150   auto *Int2 = createTree(*Arena, {{Leaf2, NodeRole::LiteralToken}},
151                           NodeKind::IntegerLiteralExpression);
152 
153   auto *TreeBinaryOperator = createTree(*Arena,
154                                         {{Int1, NodeRole::LeftHandSide},
155                                          {LeafPlus, NodeRole::OperatorToken},
156                                          {Int2, NodeRole::RightHandSide}},
157                                         NodeKind::BinaryOperatorExpression);
158 
159   EXPECT_TRUE(treeDumpEqual(TreeBinaryOperator, R"txt(
160 BinaryOperatorExpression Detached synthesized
161 |-IntegerLiteralExpression LeftHandSide synthesized
162 | `-'1' LiteralToken synthesized
163 |-'+' OperatorToken synthesized
164 `-IntegerLiteralExpression RightHandSide synthesized
165   `-'2' LiteralToken synthesized
166   )txt"));
167 }
168 
169 TEST_P(SynthesisTest, DeepCopy_Synthesized) {
170   buildTree("", GetParam());
171 
172   auto *LeafContinue = createLeaf(*Arena, *TM, tok::kw_continue);
173   auto *LeafSemiColon = createLeaf(*Arena, *TM, tok::semi);
174   auto *StatementContinue = createTree(*Arena,
175                                        {{LeafContinue, NodeRole::LiteralToken},
176                                         {LeafSemiColon, NodeRole::Unknown}},
177                                        NodeKind::ContinueStatement);
178 
179   auto *Copy = deepCopyExpandingMacros(*Arena, *TM, StatementContinue);
180   EXPECT_TRUE(treeDumpEqual(Copy, StatementContinue->dump(*TM)));
181   // FIXME: Test that copy is independent of original, once the Mutations API is
182   // more developed.
183 }
184 
185 TEST_P(SynthesisTest, DeepCopy_Original) {
186   auto *OriginalTree = buildTree("int a;", GetParam());
187 
188   auto *Copy = deepCopyExpandingMacros(*Arena, *TM, OriginalTree);
189   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
190 TranslationUnit Detached synthesized
191 `-SimpleDeclaration synthesized
192   |-'int' synthesized
193   |-DeclaratorList Declarators synthesized
194   | `-SimpleDeclarator ListElement synthesized
195   |   `-'a' synthesized
196   `-';' synthesized
197   )txt"));
198 }
199 
200 TEST_P(SynthesisTest, DeepCopy_Child) {
201   auto *OriginalTree = buildTree("int a;", GetParam());
202 
203   auto *Copy =
204       deepCopyExpandingMacros(*Arena, *TM, OriginalTree->getFirstChild());
205   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
206 SimpleDeclaration Detached synthesized
207 |-'int' synthesized
208 |-DeclaratorList Declarators synthesized
209 | `-SimpleDeclarator ListElement synthesized
210 |   `-'a' synthesized
211 `-';' synthesized
212   )txt"));
213 }
214 
215 TEST_P(SynthesisTest, DeepCopy_Macro) {
216   auto *OriginalTree = buildTree(R"cpp(
217 #define HALF_IF if (1+
218 #define HALF_IF_2 1) {}
219 void test() {
220   HALF_IF HALF_IF_2 else {}
221 })cpp",
222                                  GetParam());
223 
224   auto *Copy = deepCopyExpandingMacros(*Arena, *TM, OriginalTree);
225 
226   // The syntax tree stores already expanded Tokens, we can only see whether the
227   // macro was expanded when computing replacements. The dump does show that
228   // nodes in the copy are `modifiable`.
229   EXPECT_TRUE(treeDumpEqual(Copy, R"txt(
230 TranslationUnit Detached synthesized
231 `-SimpleDeclaration synthesized
232   |-'void' synthesized
233   |-DeclaratorList Declarators synthesized
234   | `-SimpleDeclarator ListElement synthesized
235   |   |-'test' synthesized
236   |   `-ParametersAndQualifiers synthesized
237   |     |-'(' OpenParen synthesized
238   |     `-')' CloseParen synthesized
239   `-CompoundStatement synthesized
240     |-'{' OpenParen synthesized
241     |-IfStatement Statement synthesized
242     | |-'if' IntroducerKeyword synthesized
243     | |-'(' synthesized
244     | |-ExpressionStatement Condition synthesized
245     | | `-BinaryOperatorExpression Expression synthesized
246     | |   |-IntegerLiteralExpression LeftHandSide synthesized
247     | |   | `-'1' LiteralToken synthesized
248     | |   |-'+' OperatorToken synthesized
249     | |   `-IntegerLiteralExpression RightHandSide synthesized
250     | |     `-'1' LiteralToken synthesized
251     | |-')' synthesized
252     | |-CompoundStatement ThenStatement synthesized
253     | | |-'{' OpenParen synthesized
254     | | `-'}' CloseParen synthesized
255     | |-'else' ElseKeyword synthesized
256     | `-CompoundStatement ElseStatement synthesized
257     |   |-'{' OpenParen synthesized
258     |   `-'}' CloseParen synthesized
259     `-'}' CloseParen synthesized
260   )txt"));
261 }
262 
263 TEST_P(SynthesisTest, Statement_EmptyStatement) {
264   buildTree("", GetParam());
265 
266   auto *S = createEmptyStatement(*Arena, *TM);
267   EXPECT_TRUE(treeDumpEqual(S, R"txt(
268 EmptyStatement Detached synthesized
269 `-';' synthesized
270   )txt"));
271 }
272 } // namespace
273