eduucaldas created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
eduucaldas requested review of this revision.

Example of such refactoring:

  Customer* C;
  getCustumer(C);

  Customer C = getCustumer();


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D89794

Files:
  clang/include/clang/Tooling/Syntax/Mutations.h
  clang/lib/Tooling/Syntax/Mutations.cpp

Index: clang/lib/Tooling/Syntax/Mutations.cpp
===================================================================
--- clang/lib/Tooling/Syntax/Mutations.cpp
+++ clang/lib/Tooling/Syntax/Mutations.cpp
@@ -85,6 +85,92 @@
     P->assertInvariants();
     N->assertInvariants();
   }
+
+  static void
+  replaceChildRange(syntax::Tree *Parent, syntax::Node *BeforeBegin,
+                    syntax::Node *End,
+                    ArrayRef<std::pair<Node *, NodeRole>> NewRange) {
+    if (NewRange.empty())
+      return Parent->replaceChildRangeLowLevel(BeforeBegin, End, nullptr);
+
+    for (const auto &NodeAndRole : NewRange)
+      NodeAndRole.first->setRole(NodeAndRole.second);
+
+    for (auto It = NewRange.begin(), EndIt = std::prev(NewRange.end());
+         It != EndIt; ++It) {
+      auto *Node = It->first;
+      auto *Next = std::next(It)->first;
+      Node->NextSibling = Next;
+    }
+
+    Parent->replaceChildRangeLowLevel(BeforeBegin, End, NewRange.front().first);
+  }
+
+  /// Removes all the `Node`s between the iterators `BeforeBegin` and `End`. For
+  /// separated lists, if we remove the last element we may need to remove the
+  /// delimiter before it, in order to keep it a separated list.
+  static void removeElementAndDelimiterRange(
+      List::ElementAndDelimiterIterator<Node> BeforeBegin,
+      List::ElementAndDelimiterIterator<Node> End) {
+    auto *EndNode = [&End]() -> Node * {
+      if (End.isEnd())
+        return nullptr;
+      if ((*End).element)
+        return (*End).element;
+      return (*End).delimiter;
+    }();
+    auto *Parent = End.getParent();
+    Node *BeforeBeginNode;
+    if (End.isEnd() &&
+        Parent->getTerminationKind() == List::TerminationKind::Separated) {
+      // In this case we also remove the delimiter before the last element of
+      // the list, if it exists.
+      if (BeforeBegin.isBeforeBegin())
+        BeforeBeginNode = nullptr;
+      else if ((*BeforeBegin).element)
+        BeforeBeginNode = (*BeforeBegin).element;
+      else if ((*BeforeBegin).delimiter)
+        BeforeBeginNode = findPrevious((*BeforeBegin).delimiter);
+      else
+        // `BeforeBegin` = (null, null) and thus we are in the last element of
+        // the list. Nothing will be removed.
+        return;
+    } else
+      BeforeBeginNode = [&BeforeBegin]() -> Node * {
+        if (BeforeBegin.isBeforeBegin())
+          return nullptr;
+        if ((*BeforeBegin).delimiter)
+          return (*BeforeBegin).delimiter;
+        if ((*BeforeBegin).element)
+          return (*BeforeBegin).element;
+        llvm_unreachable("(null, null) can only appear in the end of a "
+                         "separated range, and this is not possible here");
+      }();
+
+    Parent->replaceChildRangeLowLevel(BeforeBeginNode, EndNode, nullptr);
+  }
+
+  static Node *extractLastElementAndDelimiter(List *Parent) {
+    auto Prev = [](List::ElementAndDelimiterIterator<Node> EDI,
+                   unsigned i = 1) {
+      auto Previous = EDI.getParent()->getElementAndDelimiterBeforeBegin();
+      auto It = Previous;
+      for (unsigned j = 0; j < i; ++j)
+        ++It;
+      for (; It != EDI; ++It)
+        ++Previous;
+
+      return Previous;
+    };
+    if (Parent->getElementAndDelimiterBegin() ==
+        Parent->getElementAndDelimiterEnd())
+      return nullptr;
+    auto BeforeLast = Prev(Parent->getElementAndDelimiterEnd(), 2);
+    auto Last = std::next(BeforeLast);
+    removeElementAndDelimiterRange(BeforeLast,
+                                   Parent->getElementAndDelimiterEnd());
+    return (*Last).element;
+  }
 };
 
 void syntax::removeStatement(syntax::Arena &A, syntax::Statement *S) {
@@ -102,3 +188,39 @@
 
   MutationsImpl::replace(S, createEmptyStatement(A));
 }
+
+void syntax::replaceOutputParameterWithReturnValue(syntax::Arena &A,
+                                                   syntax::CallExpression *CE) {
+  assert(CE);
+  assert(CE->canModify());
+  assert(CE->getParent());
+
+  // Extract Output Parameter
+  auto *OutParam =
+      MutationsImpl::extractLastElementAndDelimiter(CE->getArguments());
+
+  // Create pointer dereference with outParam
+  auto *DerefOutParam =
+      createTree(A,
+                 {{createLeaf(A, tok::star), NodeRole::OperatorToken},
+                  {OutParam, NodeRole::Operand}},
+                 NodeKind::PrefixUnaryOperatorExpression);
+
+  // f(); -> *outParam = f();
+  // TODO: How to improve this: Add parent to AddAfter. Or `NodeIterator`, with
+  // a pointer to Parent when it's a sentinel.
+  auto *Parent = CE->getParent();
+  auto *BeforeBegin = findPrevious(CE);
+  auto *End = CE->getNextSibling();
+  auto Role = CE->getRole();
+  MutationsImpl::remove(CE);
+  auto *Assignment =
+      createTree(A,
+                 {{DerefOutParam, NodeRole::LeftHandSide},
+                  {createLeaf(A, tok::equal), NodeRole::OperatorToken},
+                  {CE, NodeRole::RightHandSide}},
+                 NodeKind::BinaryOperatorExpression);
+
+  MutationsImpl::replaceChildRange(Parent, BeforeBegin, End,
+                                   {{Assignment, Role}});
+}
Index: clang/include/clang/Tooling/Syntax/Mutations.h
===================================================================
--- clang/include/clang/Tooling/Syntax/Mutations.h
+++ clang/include/clang/Tooling/Syntax/Mutations.h
@@ -30,6 +30,8 @@
 /// replace it with an empty statement.
 /// EXPECTS: S->canModify() == true
 void removeStatement(syntax::Arena &A, syntax::Statement *S);
+void replaceOutputParameterWithReturnValue(syntax::Arena &A,
+                                           syntax::CallExpression *CE);
 
 } // namespace syntax
 } // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to