Skip to content

Commit 8903190

Browse files
committed
[Refactoring] Merge LegacyAlternativeBodyCreator into AsyncConverter
This will later allow us to reuse parts of `LegacyAlternativeBodyCreator` from `AsyncConverter` when refactoring calls to an async alternative if they pass a variable as the completion handler.
1 parent 25ba22c commit 8903190

File tree

1 file changed

+142
-140
lines changed

1 file changed

+142
-140
lines changed

lib/IDE/Refactoring.cpp

Lines changed: 142 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -4828,8 +4828,7 @@ class AsyncConverter : private SourceEntityWalker {
48284828
}
48294829

48304830
bool convert() {
4831-
if (!Buffer.empty())
4832-
return !DiagEngine.hadAnyError();
4831+
assert(Buffer.empty() && "AsyncConverter can only be used once");
48334832

48344833
if (auto *FD = dyn_cast_or_null<FuncDecl>(StartNode.dyn_cast<Decl *>())) {
48354834
addFuncDecl(FD);
@@ -4842,6 +4841,60 @@ class AsyncConverter : private SourceEntityWalker {
48424841
return !DiagEngine.hadAnyError();
48434842
}
48444843

4844+
/// When adding an async alternative method for the function declaration \c
4845+
/// FD, this function tries to create a function body for the legacy function
4846+
/// (the one with a completion handler), which calls the newly converted async
4847+
/// function. There are certain situations in which we fail to create such a
4848+
/// body, e.g. if the completion handler has the signature `(String, Error?)
4849+
/// -> Void` in which case we can't synthesize the result of type \c String in
4850+
/// the error case.
4851+
bool createLegacyBody() {
4852+
assert(Buffer.empty() &&
4853+
"AsyncConverter can only be used once");
4854+
if (!canCreateLegacyBody()) {
4855+
return false;
4856+
}
4857+
FuncDecl *FD = cast<FuncDecl>(StartNode.get<Decl *>());
4858+
Identifier CompletionHandlerName = TopHandler.Handler->getParameterName();
4859+
4860+
OS << "{\n"; // start function body
4861+
OS << "async {\n";
4862+
if (TopHandler.HasError) {
4863+
OS << "do {\n";
4864+
if (!TopHandler.willAsyncReturnVoid()) {
4865+
OS << "let result";
4866+
addResultTypeAnnotationIfNecessary(FD, TopHandler);
4867+
OS << " = ";
4868+
}
4869+
OS << "try await ";
4870+
addCallToAsyncMethod(FD, TopHandler);
4871+
OS << "\n";
4872+
addCallToCompletionHandler(/*HasResult=*/true, CompletionHandlerName, FD,
4873+
TopHandler);
4874+
OS << "\n"
4875+
<< "} catch {\n";
4876+
addCallToCompletionHandler(/*HasResult=*/false, CompletionHandlerName, FD,
4877+
TopHandler);
4878+
OS << "\n"
4879+
<< "}\n"; // end catch
4880+
} else {
4881+
if (!TopHandler.willAsyncReturnVoid()) {
4882+
OS << "let result";
4883+
addResultTypeAnnotationIfNecessary(FD, TopHandler);
4884+
OS << " = ";
4885+
}
4886+
OS << "await ";
4887+
addCallToAsyncMethod(FD, TopHandler);
4888+
OS << "\n";
4889+
addCallToCompletionHandler(/*HasResult=*/true, CompletionHandlerName, FD,
4890+
TopHandler);
4891+
OS << "\n";
4892+
}
4893+
OS << "}\n"; // end 'async'
4894+
OS << "}\n"; // end function body
4895+
return true;
4896+
}
4897+
48454898
void replace(ASTNode Node, SourceEditConsumer &EditConsumer,
48464899
SourceLoc StartOverride = SourceLoc()) {
48474900
SourceRange Range = Node.getSourceRange();
@@ -4861,6 +4914,44 @@ class AsyncConverter : private SourceEntityWalker {
48614914
}
48624915

48634916
private:
4917+
bool canCreateLegacyBody() {
4918+
FuncDecl *FD = dyn_cast<FuncDecl>(StartNode.dyn_cast<Decl *>());
4919+
if (!FD) {
4920+
return false;
4921+
}
4922+
if (FD == nullptr || FD->getBody() == nullptr) {
4923+
return false;
4924+
}
4925+
if (FD->hasThrows()) {
4926+
assert(!TopHandler.isValid() && "We shouldn't have found a handler desc "
4927+
"if the original function throws");
4928+
return false;
4929+
}
4930+
switch (TopHandler.Type) {
4931+
case HandlerType::INVALID:
4932+
return false;
4933+
case HandlerType::PARAMS: {
4934+
if (TopHandler.HasError) {
4935+
// The non-error parameters must be optional so that we can set them to
4936+
// nil in the error case.
4937+
// The error parameter must be optional so we can set it to nil in the
4938+
// success case.
4939+
// Otherwise we can't synthesize the values to return for these
4940+
// parameters.
4941+
return llvm::all_of(TopHandler.params(),
4942+
[](AnyFunctionType::Param Param) -> bool {
4943+
return Param.getPlainType()->isOptional();
4944+
});
4945+
} else {
4946+
return true;
4947+
}
4948+
}
4949+
case HandlerType::RESULT:
4950+
return true;
4951+
}
4952+
}
4953+
4954+
48644955
void convertNodes(ArrayRef<ASTNode> Nodes) {
48654956
for (auto Node : Nodes) {
48664957
OS << "\n";
@@ -5413,30 +5504,12 @@ class AsyncConverter : private SourceEntityWalker {
54135504
Names.erase(Param);
54145505
}
54155506
}
5416-
};
5417-
5418-
/// When adding an async alternative method for the function declaration \c FD,
5419-
/// this class tries to create a function body for the legacy function (the one
5420-
/// with a completion handler), which calls the newly converted async function.
5421-
/// There are certain situations in which we fail to create such a body, e.g.
5422-
/// if the completion handler has the signature `(String, Error?) -> Void` in
5423-
/// which case we can't synthesize the result of type \c String in the error
5424-
/// case.
5425-
class LegacyAlternativeBodyCreator {
5426-
/// The old function declaration for which an async alternative has been added
5427-
/// and whose body shall be rewritten to call the newly added async
5428-
/// alternative.
5429-
FuncDecl *FD;
5430-
5431-
/// The description of the completion handler in the old function declaration.
5432-
AsyncHandlerDesc HandlerDesc;
54335507

5434-
std::string Buffer;
5435-
llvm::raw_string_ostream OS;
5436-
5437-
/// Adds the call to the refactored 'async' method without the 'await'
5438-
/// keyword to the output stream.
5439-
void addCallToAsyncMethod() {
5508+
/// Adds the call to an 'async' version of \p FD, where \p HanderDesc
5509+
/// describes the async completion handler of \p FD. This does not add an
5510+
/// 'await' keyword.
5511+
void addCallToAsyncMethod(const FuncDecl *FD,
5512+
const AsyncHandlerDesc &HandlerDesc) {
54405513
OS << FD->getBaseName() << "(";
54415514
bool FirstParam = true;
54425515
for (auto Param : *FD->getParameters()) {
@@ -5457,32 +5530,34 @@ class LegacyAlternativeBodyCreator {
54575530
OS << ")";
54585531
}
54595532

5460-
/// If the returned error type is more specialized than \c Error, adds an
5461-
/// 'as! CustomError' cast to the more specialized error type to the output
5462-
/// stream.
5463-
void addCastToCustomErrorTypeIfNecessary() {
5533+
/// If the error type of \p HandlerDesc is more specialized than \c Error,
5534+
/// adds an 'as! CustomError' cast to the more specialized error type to the
5535+
/// output stream.
5536+
void addCastToCustomErrorTypeIfNecessary(const AsyncHandlerDesc &HandlerDesc,
5537+
const ASTContext &Ctx) {
54645538
auto ErrorType = *HandlerDesc.getErrorType();
5465-
if (ErrorType->getCanonicalType() !=
5466-
FD->getASTContext().getExceptionType()) {
5539+
if (ErrorType->getCanonicalType() != Ctx.getExceptionType()) {
54675540
OS << " as! ";
54685541
ErrorType->lookThroughSingleOptionalType()->print(OS);
54695542
}
54705543
}
54715544

5472-
/// Adds the \c Index -th parameter to the completion handler.
5473-
/// If \p HasResult is \c true, it is assumed that a variable named 'result'
5474-
/// contains the result returned from the async alternative. If the callback
5475-
/// also takes an error parameter, \c nil passed to the completion handler for
5476-
/// the error.
5477-
/// If \p HasResult is \c false, it is a assumed that a variable named 'error'
5478-
/// contains the error thrown from the async method and 'nil' will be passed
5479-
/// to the completion handler for all result parameters.
5480-
void addCompletionHandlerArgument(size_t Index, bool HasResult) {
5545+
/// Adds the \c Index -th parameter to the completion handler of \p FD.
5546+
/// \p HanderDesc describes which of \p FD's parameters is the completion
5547+
/// handler. If \p HasResult is \c true, it is assumed that a variable named
5548+
/// 'result' contains the result returned from the async alternative. If the
5549+
/// callback also takes an error parameter, \c nil passed to the completion
5550+
/// handler for the error. If \p HasResult is \c false, it is a assumed that a
5551+
/// variable named 'error' contains the error thrown from the async method and
5552+
/// 'nil' will be passed to the completion handler for all result parameters.
5553+
void addCompletionHandlerArgument(size_t Index, bool HasResult,
5554+
const FuncDecl *FD,
5555+
const AsyncHandlerDesc &HandlerDesc) {
54815556
if (HandlerDesc.HasError && Index == HandlerDesc.params().size() - 1) {
54825557
// The error parameter is the last argument of the completion handler.
54835558
if (!HasResult) {
54845559
OS << "error";
5485-
addCastToCustomErrorTypeIfNecessary();
5560+
addCastToCustomErrorTypeIfNecessary(HandlerDesc, FD->getASTContext());
54865561
} else {
54875562
OS << "nil";
54885563
}
@@ -5517,11 +5592,16 @@ class LegacyAlternativeBodyCreator {
55175592
}
55185593
}
55195594

5520-
/// Adds the call to the completion handler. See \c
5521-
/// getCompletionHandlerArgument for how the arguments are synthesized if the
5522-
/// completion handler takes arguments, not a \c Result type.
5523-
void addCallToCompletionHandler(bool HasResult) {
5524-
OS << HandlerDesc.Handler->getParameterName() << "(";
5595+
/// If the completion handler of a call to \p FD is named \p HandlerName,
5596+
/// add a call to \p HandlerName passing all the required arguments. \p
5597+
/// HandlerDesc describes which of \p FD's parameters is the completion
5598+
/// handler hat is being called. See \c getCompletionHandlerArgument for how
5599+
/// the arguments are synthesized if the completion handler takes arguments,
5600+
/// not a \c Result type.
5601+
void addCallToCompletionHandler(bool HasResult, Identifier HandlerName,
5602+
const FuncDecl *FD,
5603+
const AsyncHandlerDesc &HandlerDesc) {
5604+
OS << HandlerName << "(";
55255605

55265606
// Construct arguments to pass to the completion handler
55275607
switch (HandlerDesc.Type) {
@@ -5533,7 +5613,7 @@ class LegacyAlternativeBodyCreator {
55335613
if (I > 0) {
55345614
OS << ", ";
55355615
}
5536-
addCompletionHandlerArgument(I, HasResult);
5616+
addCompletionHandlerArgument(I, HasResult, FD, HandlerDesc);
55375617
}
55385618
break;
55395619
}
@@ -5542,7 +5622,7 @@ class LegacyAlternativeBodyCreator {
55425622
OS << ".success(result)";
55435623
} else {
55445624
OS << ".failure(error";
5545-
addCastToCustomErrorTypeIfNecessary();
5625+
addCastToCustomErrorTypeIfNecessary(HandlerDesc, FD->getASTContext());
55465626
OS << ")";
55475627
}
55485628
break;
@@ -5551,8 +5631,9 @@ class LegacyAlternativeBodyCreator {
55515631
OS << ")"; // Close the call to the completion handler
55525632
}
55535633

5554-
/// Adds the result type of the converted async function.
5555-
void addAsyncFuncReturnType() {
5634+
/// Adds the result type of a refactored async function that previously
5635+
/// returned results via a completion handler described by \p HandlerDesc.
5636+
void addAsyncFuncReturnType(const AsyncHandlerDesc &HandlerDesc) {
55565637
SmallVector<Type, 2> Scratch;
55575638
auto ReturnTypes = HandlerDesc.getAsyncReturnTypes(Scratch);
55585639
if (ReturnTypes.size() > 1) {
@@ -5567,16 +5648,16 @@ class LegacyAlternativeBodyCreator {
55675648
}
55685649
}
55695650

5570-
/// If the async alternative function is generic, adds the type annotation
5571-
/// to the 'return' variable in the legacy function so that the generic
5572-
/// parameters of the legacy function are passed to the generic function.
5573-
/// For example for
5651+
/// If \p FD is generic, adds a type annotation with the return type of the
5652+
/// converted async function. This is used when creating a legacy function,
5653+
/// calling the converted 'async' function so that the generic parameters of
5654+
/// the legacy function are passed to the generic function. For example for
55745655
/// \code
55755656
/// func foo<GenericParam>() async -> GenericParam {}
55765657
/// \endcode
55775658
/// we generate
55785659
/// \code
5579-
/// func foo<GenericParam>(completion: (T) -> Void) {
5660+
/// func foo<GenericParam>(completion: (GenericParam) -> Void) {
55805661
/// async {
55815662
/// let result: GenericParam = await foo()
55825663
/// <------------>
@@ -5585,89 +5666,13 @@ class LegacyAlternativeBodyCreator {
55855666
/// }
55865667
/// \endcode
55875668
/// This function adds the range marked by \c <----->
5588-
void addResultTypeAnnotationIfNecessary() {
5669+
void addResultTypeAnnotationIfNecessary(const FuncDecl *FD,
5670+
const AsyncHandlerDesc &HandlerDesc) {
55895671
if (FD->isGeneric()) {
55905672
OS << ": ";
5591-
addAsyncFuncReturnType();
5673+
addAsyncFuncReturnType(HandlerDesc);
55925674
}
55935675
}
5594-
5595-
public:
5596-
LegacyAlternativeBodyCreator(FuncDecl *FD, AsyncHandlerDesc HandlerDesc)
5597-
: FD(FD), HandlerDesc(HandlerDesc), OS(Buffer) {}
5598-
5599-
bool canRewriteLegacyBody() {
5600-
if (FD == nullptr || FD->getBody() == nullptr) {
5601-
return false;
5602-
}
5603-
if (FD->hasThrows()) {
5604-
assert(!HandlerDesc.isValid() && "We shouldn't have found a handler desc "
5605-
"if the original function throws");
5606-
return false;
5607-
}
5608-
switch (HandlerDesc.Type) {
5609-
case HandlerType::INVALID:
5610-
return false;
5611-
case HandlerType::PARAMS: {
5612-
if (HandlerDesc.HasError) {
5613-
// The non-error parameters must be optional so that we can set them to
5614-
// nil in the error case.
5615-
// The error parameter must be optional so we can set it to nil in the
5616-
// success case.
5617-
// Otherwise we can't synthesize the values to return for these
5618-
// parameters.
5619-
return llvm::all_of(HandlerDesc.params(),
5620-
[](AnyFunctionType::Param Param) -> bool {
5621-
return Param.getPlainType()->isOptional();
5622-
});
5623-
} else {
5624-
return true;
5625-
}
5626-
}
5627-
case HandlerType::RESULT:
5628-
return true;
5629-
}
5630-
}
5631-
5632-
std::string create() {
5633-
assert(Buffer.empty() &&
5634-
"LegacyAlternativeBodyCreator can only be used once");
5635-
assert(canRewriteLegacyBody() &&
5636-
"Cannot create a legacy body if the body can't be rewritten");
5637-
OS << "{\n"; // start function body
5638-
OS << "async {\n";
5639-
if (HandlerDesc.HasError) {
5640-
OS << "do {\n";
5641-
if (!HandlerDesc.willAsyncReturnVoid()) {
5642-
OS << "let result";
5643-
addResultTypeAnnotationIfNecessary();
5644-
OS << " = ";
5645-
}
5646-
OS << "try await ";
5647-
addCallToAsyncMethod();
5648-
OS << "\n";
5649-
addCallToCompletionHandler(/*HasResult=*/true);
5650-
OS << "\n"
5651-
<< "} catch {\n";
5652-
addCallToCompletionHandler(/*HasResult=*/false);
5653-
OS << "\n"
5654-
<< "}\n"; // end catch
5655-
} else {
5656-
if (!HandlerDesc.willAsyncReturnVoid()) {
5657-
OS << "let result";
5658-
addResultTypeAnnotationIfNecessary();
5659-
OS << " = ";
5660-
}
5661-
OS << "await ";
5662-
addCallToAsyncMethod();
5663-
OS << "\n";
5664-
addCallToCompletionHandler(/*HasResult=*/true);
5665-
OS << "\n";
5666-
}
5667-
OS << "}\n"; // end 'async'
5668-
OS << "}\n"; // end function body
5669-
return Buffer;
5670-
}
56715676
};
56725677

56735678
} // namespace asyncrefactorings
@@ -5776,12 +5781,9 @@ bool RefactoringActionAddAsyncAlternative::performChange() {
57765781
EditConsumer.accept(SM, FD->getAttributeInsertionLoc(false),
57775782
"@available(*, deprecated, message: \"Prefer async "
57785783
"alternative instead\")\n");
5779-
LegacyAlternativeBodyCreator LegacyBody(FD, HandlerDesc);
5780-
if (LegacyBody.canRewriteLegacyBody()) {
5781-
EditConsumer.accept(SM,
5782-
Lexer::getCharSourceRangeFromSourceRange(
5783-
SM, FD->getBody()->getSourceRange()),
5784-
LegacyBody.create());
5784+
AsyncConverter LegacyBodyCreator(SM, DiagEngine, FD, HandlerDesc);
5785+
if (LegacyBodyCreator.createLegacyBody()) {
5786+
LegacyBodyCreator.replace(FD->getBody(), EditConsumer);
57855787
}
57865788
Converter.insertAfter(FD, EditConsumer);
57875789

0 commit comments

Comments
 (0)