@@ -4828,8 +4828,7 @@ class AsyncConverter : private SourceEntityWalker {
4828
4828
}
4829
4829
4830
4830
bool convert () {
4831
- if (!Buffer.empty ())
4832
- return !DiagEngine.hadAnyError ();
4831
+ assert (Buffer.empty () && " AsyncConverter can only be used once" );
4833
4832
4834
4833
if (auto *FD = dyn_cast_or_null<FuncDecl>(StartNode.dyn_cast <Decl *>())) {
4835
4834
addFuncDecl (FD);
@@ -4842,6 +4841,60 @@ class AsyncConverter : private SourceEntityWalker {
4842
4841
return !DiagEngine.hadAnyError ();
4843
4842
}
4844
4843
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
+
4845
4898
void replace (ASTNode Node, SourceEditConsumer &EditConsumer,
4846
4899
SourceLoc StartOverride = SourceLoc()) {
4847
4900
SourceRange Range = Node.getSourceRange ();
@@ -4861,6 +4914,44 @@ class AsyncConverter : private SourceEntityWalker {
4861
4914
}
4862
4915
4863
4916
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
+
4864
4955
void convertNodes (ArrayRef<ASTNode> Nodes) {
4865
4956
for (auto Node : Nodes) {
4866
4957
OS << " \n " ;
@@ -5413,30 +5504,12 @@ class AsyncConverter : private SourceEntityWalker {
5413
5504
Names.erase (Param);
5414
5505
}
5415
5506
}
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;
5433
5507
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) {
5440
5513
OS << FD->getBaseName () << " (" ;
5441
5514
bool FirstParam = true ;
5442
5515
for (auto Param : *FD->getParameters ()) {
@@ -5457,32 +5530,34 @@ class LegacyAlternativeBodyCreator {
5457
5530
OS << " )" ;
5458
5531
}
5459
5532
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) {
5464
5538
auto ErrorType = *HandlerDesc.getErrorType ();
5465
- if (ErrorType->getCanonicalType () !=
5466
- FD->getASTContext ().getExceptionType ()) {
5539
+ if (ErrorType->getCanonicalType () != Ctx.getExceptionType ()) {
5467
5540
OS << " as! " ;
5468
5541
ErrorType->lookThroughSingleOptionalType ()->print (OS);
5469
5542
}
5470
5543
}
5471
5544
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) {
5481
5556
if (HandlerDesc.HasError && Index == HandlerDesc.params ().size () - 1 ) {
5482
5557
// The error parameter is the last argument of the completion handler.
5483
5558
if (!HasResult) {
5484
5559
OS << " error" ;
5485
- addCastToCustomErrorTypeIfNecessary ();
5560
+ addCastToCustomErrorTypeIfNecessary (HandlerDesc, FD-> getASTContext () );
5486
5561
} else {
5487
5562
OS << " nil" ;
5488
5563
}
@@ -5517,11 +5592,16 @@ class LegacyAlternativeBodyCreator {
5517
5592
}
5518
5593
}
5519
5594
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 << " (" ;
5525
5605
5526
5606
// Construct arguments to pass to the completion handler
5527
5607
switch (HandlerDesc.Type ) {
@@ -5533,7 +5613,7 @@ class LegacyAlternativeBodyCreator {
5533
5613
if (I > 0 ) {
5534
5614
OS << " , " ;
5535
5615
}
5536
- addCompletionHandlerArgument (I, HasResult);
5616
+ addCompletionHandlerArgument (I, HasResult, FD, HandlerDesc );
5537
5617
}
5538
5618
break ;
5539
5619
}
@@ -5542,7 +5622,7 @@ class LegacyAlternativeBodyCreator {
5542
5622
OS << " .success(result)" ;
5543
5623
} else {
5544
5624
OS << " .failure(error" ;
5545
- addCastToCustomErrorTypeIfNecessary ();
5625
+ addCastToCustomErrorTypeIfNecessary (HandlerDesc, FD-> getASTContext () );
5546
5626
OS << " )" ;
5547
5627
}
5548
5628
break ;
@@ -5551,8 +5631,9 @@ class LegacyAlternativeBodyCreator {
5551
5631
OS << " )" ; // Close the call to the completion handler
5552
5632
}
5553
5633
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) {
5556
5637
SmallVector<Type, 2 > Scratch;
5557
5638
auto ReturnTypes = HandlerDesc.getAsyncReturnTypes (Scratch);
5558
5639
if (ReturnTypes.size () > 1 ) {
@@ -5567,16 +5648,16 @@ class LegacyAlternativeBodyCreator {
5567
5648
}
5568
5649
}
5569
5650
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
5574
5655
// / \code
5575
5656
// / func foo<GenericParam>() async -> GenericParam {}
5576
5657
// / \endcode
5577
5658
// / we generate
5578
5659
// / \code
5579
- // / func foo<GenericParam>(completion: (T ) -> Void) {
5660
+ // / func foo<GenericParam>(completion: (GenericParam ) -> Void) {
5580
5661
// / async {
5581
5662
// / let result: GenericParam = await foo()
5582
5663
// / <------------>
@@ -5585,89 +5666,13 @@ class LegacyAlternativeBodyCreator {
5585
5666
// / }
5586
5667
// / \endcode
5587
5668
// / This function adds the range marked by \c <----->
5588
- void addResultTypeAnnotationIfNecessary () {
5669
+ void addResultTypeAnnotationIfNecessary (const FuncDecl *FD,
5670
+ const AsyncHandlerDesc &HandlerDesc) {
5589
5671
if (FD->isGeneric ()) {
5590
5672
OS << " : " ;
5591
- addAsyncFuncReturnType ();
5673
+ addAsyncFuncReturnType (HandlerDesc );
5592
5674
}
5593
5675
}
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
- }
5671
5676
};
5672
5677
5673
5678
} // namespace asyncrefactorings
@@ -5776,12 +5781,9 @@ bool RefactoringActionAddAsyncAlternative::performChange() {
5776
5781
EditConsumer.accept (SM, FD->getAttributeInsertionLoc (false ),
5777
5782
" @available(*, deprecated, message: \" Prefer async "
5778
5783
" 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);
5785
5787
}
5786
5788
Converter.insertAfter (FD, EditConsumer);
5787
5789
0 commit comments