diff --git a/clang/include/clang/Tooling/Syntax/BuildTree.h b/clang/include/clang/Tooling/Syntax/BuildTree.h --- a/clang/include/clang/Tooling/Syntax/BuildTree.h +++ b/clang/include/clang/Tooling/Syntax/BuildTree.h @@ -24,8 +24,13 @@ // Create syntax trees from subtrees not backed by the source code. -clang::syntax::Leaf *createPunctuation(clang::syntax::Arena &A, - clang::tok::TokenKind K); +// Synthesis of Leafs +syntax::Leaf *createPunctuation(Arena &A, tok::TokenKind K); +syntax::Leaf *createKeyword(Arena &A, tok::TokenKind K); +syntax::Leaf *createLeaf(syntax::Arena &A, tok::TokenKind K, + StringRef Spelling); + +// Synthesis of Syntax Nodes clang::syntax::EmptyStatement *createEmptyStatement(clang::syntax::Arena &A); } // namespace syntax diff --git a/clang/lib/Tooling/Syntax/Synthesis.cpp b/clang/lib/Tooling/Syntax/Synthesis.cpp --- a/clang/lib/Tooling/Syntax/Synthesis.cpp +++ b/clang/lib/Tooling/Syntax/Synthesis.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +#include "clang/Basic/TokenKinds.h" #include "clang/Tooling/Syntax/BuildTree.h" using namespace clang; @@ -21,24 +22,38 @@ } }; -clang::syntax::Leaf *syntax::createPunctuation(clang::syntax::Arena &A, - clang::tok::TokenKind K) { - auto Tokens = A.lexBuffer(llvm::MemoryBuffer::getMemBuffer( - clang::tok::getPunctuatorSpelling(K))) - .second; +namespace { +syntax::Leaf *createLeafLowLevel(syntax::Arena &A, StringRef Spelling) { + auto Tokens = A.lexBuffer(llvm::MemoryBuffer::getMemBuffer(Spelling)).second; assert(Tokens.size() == 1); - assert(Tokens.front().kind() == K); - auto *L = new (A.allocator()) clang::syntax::Leaf(Tokens.begin()); - FactoryImpl::setCanModify(L); + auto *L = new (A.allocator()) syntax::Leaf(Tokens.begin()); + clang::syntax::FactoryImpl::setCanModify(L); L->assertInvariants(); return L; } +} // namespace -clang::syntax::EmptyStatement * -syntax::createEmptyStatement(clang::syntax::Arena &A) { - auto *S = new (A.allocator()) clang::syntax::EmptyStatement; +syntax::Leaf *clang::syntax::createLeaf(syntax::Arena &A, tok::TokenKind K, + StringRef Spelling) { + auto *Leaf = createLeafLowLevel(A, Spelling); + assert(Leaf->token()->kind() == K && + "spelling is not lexed into the expected kind of token"); + return Leaf; +} + +syntax::Leaf *clang::syntax::createPunctuation(syntax::Arena &A, + tok::TokenKind K) { + return createLeaf(A, K, tok::getPunctuatorSpelling(K)); +} + +syntax::Leaf *clang::syntax::createKeyword(syntax::Arena &A, tok::TokenKind K) { + return createLeaf(A, K, tok::getKeywordSpelling(K)); +} + +syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena &A) { + auto *S = new (A.allocator()) syntax::EmptyStatement; FactoryImpl::setCanModify(S); - FactoryImpl::prependChildLowLevel(S, createPunctuation(A, clang::tok::semi), + FactoryImpl::prependChildLowLevel(S, createPunctuation(A, tok::semi), NodeRole::Unknown); S->assertInvariants(); return S; diff --git a/clang/unittests/Tooling/Syntax/SynthesisTest.cpp b/clang/unittests/Tooling/Syntax/SynthesisTest.cpp --- a/clang/unittests/Tooling/Syntax/SynthesisTest.cpp +++ b/clang/unittests/Tooling/Syntax/SynthesisTest.cpp @@ -12,33 +12,81 @@ #include "TreeTestBase.h" #include "clang/Tooling/Syntax/BuildTree.h" +#include "gtest/gtest.h" using namespace clang; using namespace clang::syntax; namespace { -INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, SyntaxTreeTest, +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->sourceManager())).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(SyntaxTreeTest, Leaf_Punctuation) { +TEST_P(SynthesisTest, Leaf_Punctuation) { + buildTree("", GetParam()); + + auto *C = createPunctuation(*Arena, tok::comma); + + EXPECT_TRUE(treeDumpEqual(C, R"txt( +',' Detached synthesized + )txt")); +} + +TEST_P(SynthesisTest, Leaf_Keyword) { + buildTree("", GetParam()); + + auto *C = createKeyword(*Arena, tok::kw_if); + + EXPECT_TRUE(treeDumpEqual(C, R"txt( +'if' Detached synthesized + )txt")); +} + +TEST_P(SynthesisTest, Leaf_Identifier) { buildTree("", GetParam()); - auto *C = syntax::createPunctuation(*Arena, tok::comma); - ASSERT_NE(C, nullptr); - EXPECT_EQ(C->token()->kind(), tok::comma); - EXPECT_TRUE(C->canModify()); - EXPECT_FALSE(C->isOriginal()); - EXPECT_TRUE(C->isDetached()); + auto *C = createLeaf(*Arena, tok::identifier, "a"); + + EXPECT_TRUE(treeDumpEqual(C, R"txt( +'a' Detached synthesized + )txt")); +} + +TEST_P(SynthesisTest, Leaf_Number) { + buildTree("", GetParam()); + + auto *C = createLeaf(*Arena, tok::numeric_constant, "1"); + + EXPECT_TRUE(treeDumpEqual(C, R"txt( +'1' Detached synthesized + )txt")); } -TEST_P(SyntaxTreeTest, Statement_Empty) { +TEST_P(SynthesisTest, Statement_EmptyStatement) { buildTree("", GetParam()); - auto *S = syntax::createEmptyStatement(*Arena); - ASSERT_NE(S, nullptr); - EXPECT_TRUE(S->canModify()); - EXPECT_FALSE(S->isOriginal()); - EXPECT_TRUE(S->isDetached()); + auto *S = createEmptyStatement(*Arena); + EXPECT_TRUE(treeDumpEqual(S, R"txt( +EmptyStatement Detached synthesized +`-';' synthesized + )txt")); } } // namespace