| //===- SynthesisTest.cpp --------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file tests synthesis API for syntax trees. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "TreeTestBase.h" |
| #include "clang/Tooling/Syntax/BuildTree.h" |
| #include "clang/Tooling/Syntax/Nodes.h" |
| #include "gtest/gtest.h" |
| |
| using namespace clang; |
| using namespace clang::syntax; |
| |
| namespace { |
| |
| class SynthesisTest : public SyntaxTreeTest { |
| protected: |
| ::testing::AssertionResult treeDumpEqual(syntax::Node *Root, StringRef Dump) { |
| if (!Root) |
| return ::testing::AssertionFailure() |
| << "Root was not built successfully."; |
| |
| auto Actual = StringRef(Root->dump(Arena->getSourceManager())).trim().str(); |
| auto Expected = Dump.trim().str(); |
| // EXPECT_EQ shows the diff between the two strings if they are different. |
| EXPECT_EQ(Expected, Actual); |
| if (Actual != Expected) { |
| return ::testing::AssertionFailure(); |
| } |
| return ::testing::AssertionSuccess(); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(SynthesisTests, SynthesisTest, |
| ::testing::ValuesIn(allTestClangConfigs()), ); |
| |
| TEST_P(SynthesisTest, Leaf_Punctuation) { |
| buildTree("", GetParam()); |
| |
| auto *Leaf = createLeaf(*Arena, tok::comma); |
| |
| EXPECT_TRUE(treeDumpEqual(Leaf, R"txt( |
| ',' Detached synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Leaf_Punctuation_CXX) { |
| if (!GetParam().isCXX()) |
| return; |
| |
| buildTree("", GetParam()); |
| |
| auto *Leaf = createLeaf(*Arena, tok::coloncolon); |
| |
| EXPECT_TRUE(treeDumpEqual(Leaf, R"txt( |
| '::' Detached synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Leaf_Keyword) { |
| buildTree("", GetParam()); |
| |
| auto *Leaf = createLeaf(*Arena, tok::kw_if); |
| |
| EXPECT_TRUE(treeDumpEqual(Leaf, R"txt( |
| 'if' Detached synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Leaf_Keyword_CXX11) { |
| if (!GetParam().isCXX11OrLater()) |
| return; |
| |
| buildTree("", GetParam()); |
| |
| auto *Leaf = createLeaf(*Arena, tok::kw_nullptr); |
| |
| EXPECT_TRUE(treeDumpEqual(Leaf, R"txt( |
| 'nullptr' Detached synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Leaf_Identifier) { |
| buildTree("", GetParam()); |
| |
| auto *Leaf = createLeaf(*Arena, tok::identifier, "a"); |
| |
| EXPECT_TRUE(treeDumpEqual(Leaf, R"txt( |
| 'a' Detached synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Leaf_Number) { |
| buildTree("", GetParam()); |
| |
| auto *Leaf = createLeaf(*Arena, tok::numeric_constant, "1"); |
| |
| EXPECT_TRUE(treeDumpEqual(Leaf, R"txt( |
| '1' Detached synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Tree_Empty) { |
| buildTree("", GetParam()); |
| |
| auto *Tree = createTree(*Arena, {}, NodeKind::UnknownExpression); |
| |
| EXPECT_TRUE(treeDumpEqual(Tree, R"txt( |
| UnknownExpression Detached synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Tree_Flat) { |
| buildTree("", GetParam()); |
| |
| auto *LeafLParen = createLeaf(*Arena, tok::l_paren); |
| auto *LeafRParen = createLeaf(*Arena, tok::r_paren); |
| auto *TreeParen = createTree(*Arena, |
| {{LeafLParen, NodeRole::LeftHandSide}, |
| {LeafRParen, NodeRole::RightHandSide}}, |
| NodeKind::ParenExpression); |
| |
| EXPECT_TRUE(treeDumpEqual(TreeParen, R"txt( |
| ParenExpression Detached synthesized |
| |-'(' LeftHandSide synthesized |
| `-')' RightHandSide synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Tree_OfTree) { |
| buildTree("", GetParam()); |
| |
| auto *Leaf1 = createLeaf(*Arena, tok::numeric_constant, "1"); |
| auto *Int1 = createTree(*Arena, {{Leaf1, NodeRole::LiteralToken}}, |
| NodeKind::IntegerLiteralExpression); |
| |
| auto *LeafPlus = createLeaf(*Arena, tok::plus); |
| |
| auto *Leaf2 = createLeaf(*Arena, tok::numeric_constant, "2"); |
| auto *Int2 = createTree(*Arena, {{Leaf2, NodeRole::LiteralToken}}, |
| NodeKind::IntegerLiteralExpression); |
| |
| auto *TreeBinaryOperator = createTree(*Arena, |
| {{Int1, NodeRole::LeftHandSide}, |
| {LeafPlus, NodeRole::OperatorToken}, |
| {Int2, NodeRole::RightHandSide}}, |
| NodeKind::BinaryOperatorExpression); |
| |
| EXPECT_TRUE(treeDumpEqual(TreeBinaryOperator, R"txt( |
| BinaryOperatorExpression Detached synthesized |
| |-IntegerLiteralExpression LeftHandSide synthesized |
| | `-'1' LiteralToken synthesized |
| |-'+' OperatorToken synthesized |
| `-IntegerLiteralExpression RightHandSide synthesized |
| `-'2' LiteralToken synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, DeepCopy_Synthesized) { |
| buildTree("", GetParam()); |
| |
| auto *LeafContinue = createLeaf(*Arena, tok::kw_continue); |
| auto *LeafSemiColon = createLeaf(*Arena, tok::semi); |
| auto *StatementContinue = createTree(*Arena, |
| {{LeafContinue, NodeRole::LiteralToken}, |
| {LeafSemiColon, NodeRole::Unknown}}, |
| NodeKind::ContinueStatement); |
| |
| auto *Copy = deepCopyExpandingMacros(*Arena, StatementContinue); |
| EXPECT_TRUE( |
| treeDumpEqual(Copy, StatementContinue->dump(Arena->getSourceManager()))); |
| // FIXME: Test that copy is independent of original, once the Mutations API is |
| // more developed. |
| } |
| |
| TEST_P(SynthesisTest, DeepCopy_Original) { |
| auto *OriginalTree = buildTree("int a;", GetParam()); |
| |
| auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree); |
| EXPECT_TRUE(treeDumpEqual(Copy, R"txt( |
| TranslationUnit Detached synthesized |
| `-SimpleDeclaration synthesized |
| |-'int' synthesized |
| |-DeclaratorList Declarators synthesized |
| | `-SimpleDeclarator ListElement synthesized |
| | `-'a' synthesized |
| `-';' synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, DeepCopy_Child) { |
| auto *OriginalTree = buildTree("int a;", GetParam()); |
| |
| auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree->getFirstChild()); |
| EXPECT_TRUE(treeDumpEqual(Copy, R"txt( |
| SimpleDeclaration Detached synthesized |
| |-'int' synthesized |
| |-DeclaratorList Declarators synthesized |
| | `-SimpleDeclarator ListElement synthesized |
| | `-'a' synthesized |
| `-';' synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, DeepCopy_Macro) { |
| auto *OriginalTree = buildTree(R"cpp( |
| #define HALF_IF if (1+ |
| #define HALF_IF_2 1) {} |
| void test() { |
| HALF_IF HALF_IF_2 else {} |
| })cpp", |
| GetParam()); |
| |
| auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree); |
| |
| // The syntax tree stores already expanded Tokens, we can only see whether the |
| // macro was expanded when computing replacements. The dump does show that |
| // nodes in the copy are `modifiable`. |
| EXPECT_TRUE(treeDumpEqual(Copy, R"txt( |
| TranslationUnit Detached synthesized |
| `-SimpleDeclaration synthesized |
| |-'void' synthesized |
| |-DeclaratorList Declarators synthesized |
| | `-SimpleDeclarator ListElement synthesized |
| | |-'test' synthesized |
| | `-ParametersAndQualifiers synthesized |
| | |-'(' OpenParen synthesized |
| | `-')' CloseParen synthesized |
| `-CompoundStatement synthesized |
| |-'{' OpenParen synthesized |
| |-IfStatement Statement synthesized |
| | |-'if' IntroducerKeyword synthesized |
| | |-'(' synthesized |
| | |-ExpressionStatement Condition synthesized |
| | | `-BinaryOperatorExpression Expression synthesized |
| | | |-IntegerLiteralExpression LeftHandSide synthesized |
| | | | `-'1' LiteralToken synthesized |
| | | |-'+' OperatorToken synthesized |
| | | `-IntegerLiteralExpression RightHandSide synthesized |
| | | `-'1' LiteralToken synthesized |
| | |-')' synthesized |
| | |-CompoundStatement ThenStatement synthesized |
| | | |-'{' OpenParen synthesized |
| | | `-'}' CloseParen synthesized |
| | |-'else' ElseKeyword synthesized |
| | `-CompoundStatement ElseStatement synthesized |
| | |-'{' OpenParen synthesized |
| | `-'}' CloseParen synthesized |
| `-'}' CloseParen synthesized |
| )txt")); |
| } |
| |
| TEST_P(SynthesisTest, Statement_EmptyStatement) { |
| buildTree("", GetParam()); |
| |
| auto *S = createEmptyStatement(*Arena); |
| EXPECT_TRUE(treeDumpEqual(S, R"txt( |
| EmptyStatement Detached synthesized |
| `-';' synthesized |
| )txt")); |
| } |
| } // namespace |