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