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