@@ -4088,7 +4088,7 @@ struct AsyncHandlerDesc {
4088
4088
HandlerType Type = HandlerType::INVALID;
4089
4089
bool HasError = false ;
4090
4090
4091
- static AsyncHandlerDesc get (const ValueDecl *Handler, bool ignoreName ) {
4091
+ static AsyncHandlerDesc get (const ValueDecl *Handler, bool RequireName ) {
4092
4092
AsyncHandlerDesc HandlerDesc;
4093
4093
if (auto Var = dyn_cast<VarDecl>(Handler)) {
4094
4094
HandlerDesc.Handler = Var;
@@ -4099,8 +4099,8 @@ struct AsyncHandlerDesc {
4099
4099
return AsyncHandlerDesc ();
4100
4100
}
4101
4101
4102
- // Callback must have a completion-like name (if we're not ignoring it)
4103
- if (!ignoreName && !isCompletionHandlerParamName (HandlerDesc.getNameStr ()))
4102
+ // Callback must have a completion-like name
4103
+ if (RequireName && !isCompletionHandlerParamName (HandlerDesc.getNameStr ()))
4104
4104
return AsyncHandlerDesc ();
4105
4105
4106
4106
// Callback must be a function type and return void. Doesn't need to have
@@ -4349,17 +4349,26 @@ struct AsyncHandlerDesc {
4349
4349
// / information about that completion handler and its index within the function
4350
4350
// / declaration.
4351
4351
struct AsyncHandlerParamDesc : public AsyncHandlerDesc {
4352
+ // / The function the completion handler is a parameter of.
4353
+ const FuncDecl *Func = nullptr ;
4352
4354
// / The index of the completion handler in the function that declares it.
4353
4355
int Index = -1 ;
4354
4356
4355
4357
AsyncHandlerParamDesc () : AsyncHandlerDesc() {}
4356
- AsyncHandlerParamDesc (const AsyncHandlerDesc &Handler, int Index)
4357
- : AsyncHandlerDesc(Handler), Index(Index) {}
4358
+ AsyncHandlerParamDesc (const AsyncHandlerDesc &Handler, const FuncDecl *Func,
4359
+ int Index)
4360
+ : AsyncHandlerDesc(Handler), Func(Func), Index(Index) {}
4358
4361
4359
- static AsyncHandlerParamDesc find (const FuncDecl *FD, bool ignoreName) {
4362
+ static AsyncHandlerParamDesc find (const FuncDecl *FD,
4363
+ bool RequireAttributeOrName) {
4360
4364
if (!FD || FD->hasAsync () || FD->hasThrows ())
4361
4365
return AsyncHandlerParamDesc ();
4362
4366
4367
+ bool RequireName = RequireAttributeOrName;
4368
+ if (RequireAttributeOrName &&
4369
+ FD->getAttrs ().hasAttribute <CompletionHandlerAsyncAttr>())
4370
+ RequireName = false ;
4371
+
4363
4372
// Require at least one parameter and void return type
4364
4373
auto *Params = FD->getParameters ();
4365
4374
if (Params->size () == 0 || !FD->getResultInterfaceType ()->isVoid ())
@@ -4373,10 +4382,30 @@ struct AsyncHandlerParamDesc : public AsyncHandlerDesc {
4373
4382
if (Param->isAutoClosure ())
4374
4383
return AsyncHandlerParamDesc ();
4375
4384
4376
- return AsyncHandlerParamDesc (AsyncHandlerDesc::get (Param, ignoreName) ,
4385
+ return AsyncHandlerParamDesc (AsyncHandlerDesc::get (Param, RequireName), FD ,
4377
4386
Index);
4378
4387
}
4379
4388
4389
+ // / Print the name of the function with the completion handler, without
4390
+ // / the completion handler parameter, to \p OS. That is, the name of the
4391
+ // / async alternative function.
4392
+ void printAsyncFunctionName (llvm::raw_ostream &OS) const {
4393
+ if (!Func || Index < 0 )
4394
+ return ;
4395
+
4396
+ DeclName Name = Func->getName ();
4397
+ OS << Name.getBaseName ();
4398
+
4399
+ OS << tok::l_paren;
4400
+ ArrayRef<Identifier> ArgNames = Name.getArgumentNames ();
4401
+ for (size_t I = 0 ; I < ArgNames.size (); ++I) {
4402
+ if (I != (size_t )Index) {
4403
+ OS << ArgNames[I] << tok::colon;
4404
+ }
4405
+ }
4406
+ OS << tok::r_paren;
4407
+ }
4408
+
4380
4409
bool operator ==(const AsyncHandlerParamDesc &Other) const {
4381
4410
return Handler == Other.Handler && Type == Other.Type &&
4382
4411
HasError == Other.HasError && Index == Other.Index ;
@@ -5554,8 +5583,12 @@ class AsyncConverter : private SourceEntityWalker {
5554
5583
return addCustom (CE->getSourceRange (), [&]() { addHandlerCall (CE); });
5555
5584
5556
5585
if (auto *CE = dyn_cast<CallExpr>(E)) {
5586
+ // If the refactoring is on the call itself, do not require the callee
5587
+ // to have the @completionHandlerAsync attribute or a completion-like
5588
+ // name.
5557
5589
auto HandlerDesc = AsyncHandlerParamDesc::find (
5558
- getUnderlyingFunc (CE->getFn ()), StartNode.dyn_cast <Expr *>() == CE);
5590
+ getUnderlyingFunc (CE->getFn ()),
5591
+ /* RequireAttributeOrName=*/ StartNode.dyn_cast <Expr *>() != CE);
5559
5592
if (HandlerDesc.isValid ())
5560
5593
return addCustom (CE->getSourceRange (),
5561
5594
[&]() { addHoistedCallback (CE, HandlerDesc); });
@@ -5826,8 +5859,8 @@ class AsyncConverter : private SourceEntityWalker {
5826
5859
5827
5860
// The completion handler that is called as part of the \p CE call.
5828
5861
// This will be called once the async function returns.
5829
- auto CompletionHandler = AsyncHandlerDesc::get (CallbackDecl,
5830
- /* ignoreName =*/ true );
5862
+ auto CompletionHandler =
5863
+ AsyncHandlerDesc::get (CallbackDecl, /* RequireAttributeOrName =*/ false );
5831
5864
if (CompletionHandler.isValid ()) {
5832
5865
if (auto CalledFunc = getUnderlyingFunc (CE->getFn ())) {
5833
5866
StringRef HandlerName = Lexer::getCharSourceRangeFromSourceRange (
@@ -6420,8 +6453,8 @@ bool RefactoringActionConvertCallToAsyncAlternative::isApplicable(
6420
6453
if (!CE)
6421
6454
return false ;
6422
6455
6423
- auto HandlerDesc = AsyncHandlerParamDesc::find (getUnderlyingFunc (CE-> getFn ()),
6424
- /* ignoreName =*/ true );
6456
+ auto HandlerDesc = AsyncHandlerParamDesc::find (
6457
+ getUnderlyingFunc (CE-> getFn ()), /* RequireAttributeOrName =*/ false );
6425
6458
return HandlerDesc.isValid ();
6426
6459
}
6427
6460
@@ -6478,7 +6511,8 @@ bool RefactoringActionConvertToAsync::performChange() {
6478
6511
assert (FD &&
6479
6512
" Should not run performChange when refactoring is not applicable" );
6480
6513
6481
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6514
+ auto HandlerDesc =
6515
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6482
6516
AsyncConverter Converter (TheFile, SM, DiagEngine, FD, HandlerDesc);
6483
6517
if (!Converter.convert ())
6484
6518
return true ;
@@ -6495,7 +6529,8 @@ bool RefactoringActionAddAsyncAlternative::isApplicable(
6495
6529
if (!FD)
6496
6530
return false ;
6497
6531
6498
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6532
+ auto HandlerDesc =
6533
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6499
6534
return HandlerDesc.isValid ();
6500
6535
}
6501
6536
@@ -6512,21 +6547,37 @@ bool RefactoringActionAddAsyncAlternative::performChange() {
6512
6547
assert (FD &&
6513
6548
" Should not run performChange when refactoring is not applicable" );
6514
6549
6515
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6550
+ auto HandlerDesc =
6551
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6516
6552
assert (HandlerDesc.isValid () &&
6517
6553
" Should not run performChange when refactoring is not applicable" );
6518
6554
6519
6555
AsyncConverter Converter (TheFile, SM, DiagEngine, FD, HandlerDesc);
6520
6556
if (!Converter.convert ())
6521
6557
return true ;
6522
6558
6559
+ // Deprecate the synchronous function
6523
6560
EditConsumer.accept (SM, FD->getAttributeInsertionLoc (false ),
6524
6561
" @available(*, deprecated, message: \" Prefer async "
6525
6562
" alternative instead\" )\n " );
6563
+
6564
+ if (Ctx.LangOpts .EnableExperimentalConcurrency ) {
6565
+ // Add an attribute to describe its async alternative
6566
+ llvm::SmallString<0 > HandlerAttribute;
6567
+ llvm::raw_svector_ostream OS (HandlerAttribute);
6568
+ OS << " @completionHandlerAsync(\" " ;
6569
+ HandlerDesc.printAsyncFunctionName (OS);
6570
+ OS << " \" , completionHandlerIndex: " << HandlerDesc.Index << " )\n " ;
6571
+ EditConsumer.accept (SM, FD->getAttributeInsertionLoc (false ),
6572
+ HandlerAttribute);
6573
+ }
6574
+
6526
6575
AsyncConverter LegacyBodyCreator (TheFile, SM, DiagEngine, FD, HandlerDesc);
6527
6576
if (LegacyBodyCreator.createLegacyBody ()) {
6528
6577
LegacyBodyCreator.replace (FD->getBody (), EditConsumer);
6529
6578
}
6579
+
6580
+ // Add the async alternative
6530
6581
Converter.insertAfter (FD, EditConsumer);
6531
6582
6532
6583
return false ;
0 commit comments