@@ -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
48634916private:
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