Author: Eduardo Caldas Date: 2020-09-21T09:27:15Z New Revision: 4a5cc389c51d267f39286a9a8c58c32f758b9d4b
URL: https://github.com/llvm/llvm-project/commit/4a5cc389c51d267f39286a9a8c58c32f758b9d4b DIFF: https://github.com/llvm/llvm-project/commit/4a5cc389c51d267f39286a9a8c58c32f758b9d4b.diff LOG: [SyntaxTree][Synthesis] Implement `deepCopy` Differential Revision: https://reviews.llvm.org/D87749 Added: Modified: clang/include/clang/Tooling/Syntax/BuildTree.h clang/lib/Tooling/Syntax/Synthesis.cpp clang/unittests/Tooling/Syntax/SynthesisTest.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Tooling/Syntax/BuildTree.h b/clang/include/clang/Tooling/Syntax/BuildTree.h index 452edf580ae1..7b36dff123f6 100644 --- a/clang/include/clang/Tooling/Syntax/BuildTree.h +++ b/clang/include/clang/Tooling/Syntax/BuildTree.h @@ -45,6 +45,17 @@ createTree(syntax::Arena &A, // 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 index f171d26512d9..c8fcac27e0d5 100644 --- a/clang/lib/Tooling/Syntax/Synthesis.cpp +++ b/clang/lib/Tooling/Syntax/Synthesis.cpp @@ -28,10 +28,12 @@ class clang::syntax::FactoryImpl { } }; +// 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 && @@ -192,14 +194,52 @@ syntax::Tree *clang::syntax::createTree( 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<syntax::Leaf>(N)) + return L->canModify(); + + const auto *T = cast<syntax::Tree>(N); + + if (!T->canModify()) + return false; + for (const auto *Child = T->getFirstChild(); Child; + Child = Child->getNextSibling()) + if (!canModifyAllDescendants(Child)) + return false; + + return true; +} + +syntax::Node *deepCopyImpl(syntax::Arena &A, const syntax::Node *N) { + if (const auto *L = dyn_cast<syntax::Leaf>(N)) + return createLeaf(A, L->getToken()->kind(), + L->getToken()->text(A.getSourceManager())); + + const auto *T = cast<syntax::Tree>(N); + std::vector<std::pair<syntax::Node *, syntax::NodeRole>> Children; + for (const auto *Child = T->getFirstChild(); Child; + Child = Child->getNextSibling()) + Children.push_back({deepCopyImpl(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<EmptyStatement>( createTree(A, {{createLeaf(A, tok::semi), NodeRole::Unknown}}, diff --git a/clang/unittests/Tooling/Syntax/SynthesisTest.cpp b/clang/unittests/Tooling/Syntax/SynthesisTest.cpp index 8d9fb706eac3..2af1fcf8f317 100644 --- a/clang/unittests/Tooling/Syntax/SynthesisTest.cpp +++ b/clang/unittests/Tooling/Syntax/SynthesisTest.cpp @@ -163,6 +163,63 @@ BinaryOperatorExpression Detached 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 = 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, 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 = deepCopy(*Arena, OriginalTree); + EXPECT_TRUE(Copy == nullptr); +} + TEST_P(SynthesisTest, Statement_EmptyStatement) { buildTree("", GetParam()); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits