eduucaldas updated this revision to Diff 293085.
eduucaldas marked 3 inline comments as done.
eduucaldas added a comment.

- `deepCopy` returns `nullptr` if copy code with Macro expansions.
- `deepCopy` also deep copies the Tokens.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87749/new/

https://reviews.llvm.org/D87749

Files:
  clang/include/clang/Tooling/Syntax/BuildTree.h
  clang/lib/Tooling/Syntax/Synthesis.cpp
  clang/unittests/Tooling/Syntax/SynthesisTest.cpp

Index: clang/unittests/Tooling/Syntax/SynthesisTest.cpp
===================================================================
--- clang/unittests/Tooling/Syntax/SynthesisTest.cpp
+++ 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());
 
Index: clang/lib/Tooling/Syntax/Synthesis.cpp
===================================================================
--- clang/lib/Tooling/Syntax/Synthesis.cpp
+++ 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<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 (!Child->canModify())
+      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);
+  auto Children = std::vector<std::pair<syntax::Node *, syntax::NodeRole>>();
+  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<EmptyStatement>(
       createTree(A, {{createLeaf(A, tok::semi), NodeRole::Unknown}},
Index: clang/include/clang/Tooling/Syntax/BuildTree.h
===================================================================
--- clang/include/clang/Tooling/Syntax/BuildTree.h
+++ 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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to