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, const char *spelling, + tok::TokenKind K); + +// 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, const char *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, const char *spelling, + tok::TokenKind K) { + 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, tok::getPunctuatorSpelling(K), K); +} + +syntax::Leaf *clang::syntax::createKeyword(syntax::Arena &A, tok::TokenKind K) { + return createLeaf(A, tok::getKeywordSpelling(K), 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 @@ -18,27 +18,75 @@ namespace { -INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, SyntaxTreeTest, +std::string trim(std::string s) { return StringRef(s).trim().str(); } + +INSTANTIATE_TEST_CASE_P(SynthesisTest, SyntaxTreeTest, ::testing::ValuesIn(allTestClangConfigs()), ); TEST_P(SyntaxTreeTest, Leaf_Punctuation) { buildTree("", GetParam()); - auto *C = syntax::createPunctuation(*Arena, tok::comma); + auto *C = createPunctuation(*Arena, tok::comma); + ASSERT_NE(C, nullptr); + + auto Dump = trim(C->dump(Arena->sourceManager())); + auto Expected = trim(R"txt( +',' Detached synthesized + )txt"); + + EXPECT_EQ(Expected, Dump); +} + +TEST_P(SyntaxTreeTest, Leaf_Keyword) { + buildTree("", GetParam()); + + auto *C = createKeyword(*Arena, tok::kw_if); + ASSERT_NE(C, nullptr); + auto Dump = trim(C->dump(Arena->sourceManager())); + auto Expected = trim(R"txt( +'if' Detached synthesized + )txt"); + + EXPECT_EQ(Expected, Dump); +} + +TEST_P(SyntaxTreeTest, Leaf_Identifier) { + buildTree("", GetParam()); + + auto *C = createLeaf(*Arena, "a", tok::identifier); ASSERT_NE(C, nullptr); - EXPECT_EQ(C->token()->kind(), tok::comma); - EXPECT_TRUE(C->canModify()); - EXPECT_FALSE(C->isOriginal()); - EXPECT_TRUE(C->isDetached()); + auto Dump = trim(C->dump(Arena->sourceManager())); + auto Expected = trim(R"txt( +'a' Detached synthesized + )txt"); + + EXPECT_EQ(Expected, Dump); } -TEST_P(SyntaxTreeTest, Statement_Empty) { +TEST_P(SyntaxTreeTest, Leaf_Number) { buildTree("", GetParam()); - auto *S = syntax::createEmptyStatement(*Arena); + auto *C = createLeaf(*Arena, "1", tok::numeric_constant); + ASSERT_NE(C, nullptr); + auto Dump = trim(C->dump(Arena->sourceManager())); + auto Expected = trim(R"txt( +'1' Detached synthesized + )txt"); + + EXPECT_EQ(Expected, Dump); +} + +TEST_P(SyntaxTreeTest, Statement_EmptyStatement) { + buildTree("", GetParam()); + + auto *S = createEmptyStatement(*Arena); ASSERT_NE(S, nullptr); - EXPECT_TRUE(S->canModify()); - EXPECT_FALSE(S->isOriginal()); - EXPECT_TRUE(S->isDetached()); + auto Dump = trim(S->dump(Arena->sourceManager())); + auto Expected = trim(R"txt( +EmptyStatement Detached synthesized +`-';' synthesized + )txt"); + + EXPECT_EQ(Expected, Dump); } } // namespace