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 @@ -40,6 +40,13 @@ std::vector> Children, syntax::NodeKind K); +/// Deep copies `N`. +/// +/// The copy is detached, i.e. `Parent == NextSibling == nullptr` and +/// `Role == Detached`. +/// The copy is synthesized, i.e. `Original == false`. +syntax::Node *deepCopy(syntax::Arena &A, const syntax::Node *N); + // Synthesis of Syntax Nodes clang::syntax::EmptyStatement *createEmptyStatement(clang::syntax::Arena &A); 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 @@ -66,6 +66,27 @@ return T; } +static syntax::Leaf *copyLeaf(syntax::Arena &A, const syntax::Leaf *L) { + auto *Leaf = new (A.getAllocator()) syntax::Leaf(L->getToken()); + if (L->canModify()) + syntax::FactoryImpl::setCanModify(Leaf); + + return Leaf; +} + +syntax::Node *clang::syntax::deepCopy(syntax::Arena &A, const Node *N) { + if (const auto *L = dyn_cast(N)) { + return copyLeaf(A, L); + } + + const auto *T = cast(N); + auto Children = std::vector>(); + for (const auto *C = T->getFirstChild(); C; C = C->getNextSibling()) { + Children.push_back({deepCopy(A, C), C->getRole()}); + } + return clang::syntax::createTree(A, Children, N->getKind()); +} + syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena &A) { auto *S = new (A.getAllocator()) syntax::EmptyStatement; FactoryImpl::setCanModify(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 @@ -137,6 +137,50 @@ )txt")); } +TEST_P(SynthesisTest, Copy_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 = deepCopy(*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, Copy_Original) { + auto *OriginalTree = buildTree("int a;", GetParam()); + + auto *Copy = deepCopy(*Arena, OriginalTree); + EXPECT_TRUE(treeDumpEqual(Copy, R"txt( +TranslationUnit Detached synthesized +`-SimpleDeclaration synthesized + |-'int' synthesized + |-SimpleDeclarator Declarator synthesized + | `-'a' synthesized + `-';' synthesized + )txt")); +} + +TEST_P(SynthesisTest, Copy_Child) { + auto *OriginalTree = buildTree("int a;", GetParam()); + + auto *Copy = deepCopy(*Arena, OriginalTree->getFirstChild()); + EXPECT_TRUE(treeDumpEqual(Copy, R"txt( +SimpleDeclaration Detached synthesized +|-'int' synthesized +|-SimpleDeclarator Declarator synthesized +| `-'a' synthesized +`-';' synthesized + )txt")); +} + TEST_P(SynthesisTest, Statement_EmptyStatement) { buildTree("", GetParam());