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 @@ -45,6 +45,17 @@ // Synthesis of Syntax Nodes syntax::EmptyStatement *createEmptyStatement(syntax::Arena &A); +/// Creates a completely independent copy of `N` (a deep copy). +/// +/// The copy is: +/// * Detached, i.e. `Parent == NextSibling == nullptr` and +/// `Role == Detached`. +/// * Synthesized, i.e. `Original == false`. +/// +/// `N` might be backed by source code but if any descendants of `N` are +/// unmodifiable returns `nullptr`. +syntax::Node *deepCopy(syntax::Arena &A, const syntax::Node *N); + } // namespace syntax } // namespace clang #endif 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 @@ -28,10 +28,12 @@ } }; +// FIXME: `createLeaf` is based on `syntax::tokenize` internally, as such it +// doesn't support digraphs or line continuations. syntax::Leaf *clang::syntax::createLeaf(syntax::Arena &A, tok::TokenKind K, StringRef Spelling) { auto Tokens = - FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBuffer(Spelling)) + FactoryImpl::lexBuffer(A, llvm::MemoryBuffer::getMemBufferCopy(Spelling)) .second; assert(Tokens.size() == 1); assert(Tokens.front().kind() == K && @@ -184,6 +186,7 @@ } llvm_unreachable("unknown node kind"); } + } // namespace syntax::Tree *clang::syntax::createTree( @@ -192,14 +195,52 @@ syntax::NodeKind K) { auto *T = allocateTree(A, K); FactoryImpl::setCanModify(T); - for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend(); - std::advance(ChildIt, 1)) + for (auto ChildIt = Children.rbegin(); ChildIt != Children.rend(); ++ChildIt) FactoryImpl::prependChildLowLevel(T, ChildIt->first, ChildIt->second); T->assertInvariants(); return T; } +namespace { +bool canModifyAllDescendants(const syntax::Node *N) { + if (const auto *L = dyn_cast(N)) + return L->canModify(); + + const auto *T = cast(N); + + if (!T->canModify()) + return false; + for (const auto *Child = T->getFirstChild(); Child; + Child = Child->getNextSibling()) + if (!Child->canModify()) + return false; + + return true; +} + +syntax::Node *deepCopyImpl(syntax::Arena &A, const syntax::Node *N) { + if (const auto *L = dyn_cast(N)) + return createLeaf(A, L->getToken()->kind(), + L->getToken()->text(A.getSourceManager())); + + const auto *T = cast(N); + auto Children = std::vector>(); + for (const auto *Child = T->getFirstChild(); Child; + Child = Child->getNextSibling()) + Children.push_back({deepCopy(A, Child), Child->getRole()}); + + return createTree(A, Children, N->getKind()); +} +} // namespace + +syntax::Node *clang::syntax::deepCopy(syntax::Arena &A, const Node *N) { + if (!canModifyAllDescendants(N)) + return nullptr; + + return deepCopyImpl(A, N); +} + syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena &A) { return cast( createTree(A, {{createLeaf(A, tok::semi), NodeRole::Unknown}}, 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 @@ -163,6 +163,50 @@ )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 = 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, DeepCopy_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, DeepCopy_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());