@@ -4079,7 +4079,7 @@ struct AsyncHandlerDesc {
4079
4079
HandlerType Type = HandlerType::INVALID;
4080
4080
bool HasError = false ;
4081
4081
4082
- static AsyncHandlerDesc get (const ValueDecl *Handler, bool ignoreName ) {
4082
+ static AsyncHandlerDesc get (const ValueDecl *Handler, bool RequireName ) {
4083
4083
AsyncHandlerDesc HandlerDesc;
4084
4084
if (auto Var = dyn_cast<VarDecl>(Handler)) {
4085
4085
HandlerDesc.Handler = Var;
@@ -4090,8 +4090,8 @@ struct AsyncHandlerDesc {
4090
4090
return AsyncHandlerDesc ();
4091
4091
}
4092
4092
4093
- // Callback must have a completion-like name (if we're not ignoring it)
4094
- if (!ignoreName && !isCompletionHandlerParamName (HandlerDesc.getNameStr ()))
4093
+ // Callback must have a completion-like name
4094
+ if (RequireName && !isCompletionHandlerParamName (HandlerDesc.getNameStr ()))
4095
4095
return AsyncHandlerDesc ();
4096
4096
4097
4097
// Callback must be a function type and return void. Doesn't need to have
@@ -4340,17 +4340,26 @@ struct AsyncHandlerDesc {
4340
4340
// / information about that completion handler and its index within the function
4341
4341
// / declaration.
4342
4342
struct AsyncHandlerParamDesc : public AsyncHandlerDesc {
4343
+ // / The function the completion handler is a parameter of.
4344
+ const FuncDecl *Func = nullptr ;
4343
4345
// / The index of the completion handler in the function that declares it.
4344
4346
int Index = -1 ;
4345
4347
4346
4348
AsyncHandlerParamDesc () : AsyncHandlerDesc() {}
4347
- AsyncHandlerParamDesc (const AsyncHandlerDesc &Handler, int Index)
4348
- : AsyncHandlerDesc(Handler), Index(Index) {}
4349
+ AsyncHandlerParamDesc (const AsyncHandlerDesc &Handler, const FuncDecl *Func,
4350
+ int Index)
4351
+ : AsyncHandlerDesc(Handler), Func(Func), Index(Index) {}
4349
4352
4350
- static AsyncHandlerParamDesc find (const FuncDecl *FD, bool ignoreName) {
4353
+ static AsyncHandlerParamDesc find (const FuncDecl *FD,
4354
+ bool RequireAttributeOrName) {
4351
4355
if (!FD || FD->hasAsync () || FD->hasThrows ())
4352
4356
return AsyncHandlerParamDesc ();
4353
4357
4358
+ bool RequireName = RequireAttributeOrName;
4359
+ if (RequireAttributeOrName &&
4360
+ FD->getAttrs ().hasAttribute <CompletionHandlerAsyncAttr>())
4361
+ RequireName = false ;
4362
+
4354
4363
// Require at least one parameter and void return type
4355
4364
auto *Params = FD->getParameters ();
4356
4365
if (Params->size () == 0 || !FD->getResultInterfaceType ()->isVoid ())
@@ -4364,10 +4373,30 @@ struct AsyncHandlerParamDesc : public AsyncHandlerDesc {
4364
4373
if (Param->isAutoClosure ())
4365
4374
return AsyncHandlerParamDesc ();
4366
4375
4367
- return AsyncHandlerParamDesc (AsyncHandlerDesc::get (Param, ignoreName) ,
4376
+ return AsyncHandlerParamDesc (AsyncHandlerDesc::get (Param, RequireName), FD ,
4368
4377
Index);
4369
4378
}
4370
4379
4380
+ // / Print the name of the function with the completion handler, without
4381
+ // / the completion handler parameter, to \p OS. That is, the name of the
4382
+ // / async alternative function.
4383
+ void printAsyncFunctionName (llvm::raw_ostream &OS) const {
4384
+ if (!Func || Index < 0 )
4385
+ return ;
4386
+
4387
+ DeclName Name = Func->getName ();
4388
+ OS << Name.getBaseName ();
4389
+
4390
+ OS << tok::l_paren;
4391
+ ArrayRef<Identifier> ArgNames = Name.getArgumentNames ();
4392
+ for (size_t I = 0 ; I < ArgNames.size (); ++I) {
4393
+ if (I != (size_t )Index) {
4394
+ OS << ArgNames[I] << tok::colon;
4395
+ }
4396
+ }
4397
+ OS << tok::r_paren;
4398
+ }
4399
+
4371
4400
bool operator ==(const AsyncHandlerParamDesc &Other) const {
4372
4401
return Handler == Other.Handler && Type == Other.Type &&
4373
4402
HasError == Other.HasError && Index == Other.Index ;
@@ -5546,8 +5575,12 @@ class AsyncConverter : private SourceEntityWalker {
5546
5575
return addCustom (CE->getSourceRange (), [&]() { addHandlerCall (CE); });
5547
5576
5548
5577
if (auto *CE = dyn_cast<CallExpr>(E)) {
5578
+ // If the refactoring is on the call itself, do not require the callee
5579
+ // to have the @completionHandlerAsync attribute or a completion-like
5580
+ // name.
5549
5581
auto HandlerDesc = AsyncHandlerParamDesc::find (
5550
- getUnderlyingFunc (CE->getFn ()), StartNode.dyn_cast <Expr *>() == CE);
5582
+ getUnderlyingFunc (CE->getFn ()),
5583
+ /* RequireAttributeOrName=*/ StartNode.dyn_cast <Expr *>() != CE);
5551
5584
if (HandlerDesc.isValid ())
5552
5585
return addCustom (CE->getSourceRange (),
5553
5586
[&]() { addHoistedCallback (CE, HandlerDesc); });
@@ -5818,8 +5851,8 @@ class AsyncConverter : private SourceEntityWalker {
5818
5851
5819
5852
// The completion handler that is called as part of the \p CE call.
5820
5853
// This will be called once the async function returns.
5821
- auto CompletionHandler = AsyncHandlerDesc::get (CallbackDecl,
5822
- /* ignoreName =*/ true );
5854
+ auto CompletionHandler =
5855
+ AsyncHandlerDesc::get (CallbackDecl, /* RequireAttributeOrName =*/ false );
5823
5856
if (CompletionHandler.isValid ()) {
5824
5857
if (auto CalledFunc = getUnderlyingFunc (CE->getFn ())) {
5825
5858
StringRef HandlerName = Lexer::getCharSourceRangeFromSourceRange (
@@ -6412,8 +6445,8 @@ bool RefactoringActionConvertCallToAsyncAlternative::isApplicable(
6412
6445
if (!CE)
6413
6446
return false ;
6414
6447
6415
- auto HandlerDesc = AsyncHandlerParamDesc::find (getUnderlyingFunc (CE-> getFn ()),
6416
- /* ignoreName =*/ true );
6448
+ auto HandlerDesc = AsyncHandlerParamDesc::find (
6449
+ getUnderlyingFunc (CE-> getFn ()), /* RequireAttributeOrName =*/ false );
6417
6450
return HandlerDesc.isValid ();
6418
6451
}
6419
6452
@@ -6470,7 +6503,8 @@ bool RefactoringActionConvertToAsync::performChange() {
6470
6503
assert (FD &&
6471
6504
" Should not run performChange when refactoring is not applicable" );
6472
6505
6473
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6506
+ auto HandlerDesc =
6507
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6474
6508
AsyncConverter Converter (TheFile, SM, DiagEngine, FD, HandlerDesc);
6475
6509
if (!Converter.convert ())
6476
6510
return true ;
@@ -6487,7 +6521,8 @@ bool RefactoringActionAddAsyncAlternative::isApplicable(
6487
6521
if (!FD)
6488
6522
return false ;
6489
6523
6490
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6524
+ auto HandlerDesc =
6525
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6491
6526
return HandlerDesc.isValid ();
6492
6527
}
6493
6528
@@ -6504,21 +6539,37 @@ bool RefactoringActionAddAsyncAlternative::performChange() {
6504
6539
assert (FD &&
6505
6540
" Should not run performChange when refactoring is not applicable" );
6506
6541
6507
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6542
+ auto HandlerDesc =
6543
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6508
6544
assert (HandlerDesc.isValid () &&
6509
6545
" Should not run performChange when refactoring is not applicable" );
6510
6546
6511
6547
AsyncConverter Converter (TheFile, SM, DiagEngine, FD, HandlerDesc);
6512
6548
if (!Converter.convert ())
6513
6549
return true ;
6514
6550
6551
+ // Deprecate the synchronous function
6515
6552
EditConsumer.accept (SM, FD->getAttributeInsertionLoc (false ),
6516
6553
" @available(*, deprecated, message: \" Prefer async "
6517
6554
" alternative instead\" )\n " );
6555
+
6556
+ if (Ctx.LangOpts .EnableExperimentalConcurrency ) {
6557
+ // Add an attribute to describe its async alternative
6558
+ llvm::SmallString<0 > HandlerAttribute;
6559
+ llvm::raw_svector_ostream OS (HandlerAttribute);
6560
+ OS << " @completionHandlerAsync(\" " ;
6561
+ HandlerDesc.printAsyncFunctionName (OS);
6562
+ OS << " \" , completionHandlerIndex: " << HandlerDesc.Index << " )\n " ;
6563
+ EditConsumer.accept (SM, FD->getAttributeInsertionLoc (false ),
6564
+ HandlerAttribute);
6565
+ }
6566
+
6518
6567
AsyncConverter LegacyBodyCreator (TheFile, SM, DiagEngine, FD, HandlerDesc);
6519
6568
if (LegacyBodyCreator.createLegacyBody ()) {
6520
6569
LegacyBodyCreator.replace (FD->getBody (), EditConsumer);
6521
6570
}
6571
+
6572
+ // Add the async alternative
6522
6573
Converter.insertAfter (FD, EditConsumer);
6523
6574
6524
6575
return false ;
0 commit comments