diff --git a/clang/include/clang/Tooling/Syntax/Mutations.h b/clang/include/clang/Tooling/Syntax/Mutations.h --- a/clang/include/clang/Tooling/Syntax/Mutations.h +++ b/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 diff --git a/clang/lib/Tooling/Syntax/Mutations.cpp b/clang/lib/Tooling/Syntax/Mutations.cpp --- a/clang/lib/Tooling/Syntax/Mutations.cpp +++ b/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> 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 BeforeBegin, + List::ElementAndDelimiterIterator 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 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}}); +}