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