@@ -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 ;
@@ -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); });
@@ -5836,8 +5869,8 @@ class AsyncConverter : private SourceEntityWalker {
5836
5869
5837
5870
// The completion handler that is called as part of the \p CE call.
5838
5871
// This will be called once the async function returns.
5839
- auto CompletionHandler = AsyncHandlerDesc::get (CallbackDecl,
5840
- /* ignoreName =*/ true );
5872
+ auto CompletionHandler =
5873
+ AsyncHandlerDesc::get (CallbackDecl, /* RequireAttributeOrName =*/ false );
5841
5874
if (CompletionHandler.isValid ()) {
5842
5875
if (auto CalledFunc = getUnderlyingFunc (CE->getFn ())) {
5843
5876
StringRef HandlerName = Lexer::getCharSourceRangeFromSourceRange (
@@ -6430,8 +6463,8 @@ bool RefactoringActionConvertCallToAsyncAlternative::isApplicable(
6430
6463
if (!CE)
6431
6464
return false ;
6432
6465
6433
- auto HandlerDesc = AsyncHandlerParamDesc::find (getUnderlyingFunc (CE-> getFn ()),
6434
- /* ignoreName =*/ true );
6466
+ auto HandlerDesc = AsyncHandlerParamDesc::find (
6467
+ getUnderlyingFunc (CE-> getFn ()), /* RequireAttributeOrName =*/ false );
6435
6468
return HandlerDesc.isValid ();
6436
6469
}
6437
6470
@@ -6488,7 +6521,8 @@ bool RefactoringActionConvertToAsync::performChange() {
6488
6521
assert (FD &&
6489
6522
" Should not run performChange when refactoring is not applicable" );
6490
6523
6491
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6524
+ auto HandlerDesc =
6525
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6492
6526
AsyncConverter Converter (TheFile, SM, DiagEngine, FD, HandlerDesc);
6493
6527
if (!Converter.convert ())
6494
6528
return true ;
@@ -6505,7 +6539,8 @@ bool RefactoringActionAddAsyncAlternative::isApplicable(
6505
6539
if (!FD)
6506
6540
return false ;
6507
6541
6508
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6542
+ auto HandlerDesc =
6543
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6509
6544
return HandlerDesc.isValid ();
6510
6545
}
6511
6546
@@ -6522,21 +6557,37 @@ bool RefactoringActionAddAsyncAlternative::performChange() {
6522
6557
assert (FD &&
6523
6558
" Should not run performChange when refactoring is not applicable" );
6524
6559
6525
- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6560
+ auto HandlerDesc =
6561
+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
6526
6562
assert (HandlerDesc.isValid () &&
6527
6563
" Should not run performChange when refactoring is not applicable" );
6528
6564
6529
6565
AsyncConverter Converter (TheFile, SM, DiagEngine, FD, HandlerDesc);
6530
6566
if (!Converter.convert ())
6531
6567
return true ;
6532
6568
6569
+ // Deprecate the synchronous function
6533
6570
EditConsumer.accept (SM, FD->getAttributeInsertionLoc (false ),
6534
6571
" @available(*, deprecated, message: \" Prefer async "
6535
6572
" alternative instead\" )\n " );
6573
+
6574
+ if (Ctx.LangOpts .EnableExperimentalConcurrency ) {
6575
+ // Add an attribute to describe its async alternative
6576
+ llvm::SmallString<0 > HandlerAttribute;
6577
+ llvm::raw_svector_ostream OS (HandlerAttribute);
6578
+ OS << " @completionHandlerAsync(\" " ;
6579
+ HandlerDesc.printAsyncFunctionName (OS);
6580
+ OS << " \" , completionHandlerIndex: " << HandlerDesc.Index << " )\n " ;
6581
+ EditConsumer.accept (SM, FD->getAttributeInsertionLoc (false ),
6582
+ HandlerAttribute);
6583
+ }
6584
+
6536
6585
AsyncConverter LegacyBodyCreator (TheFile, SM, DiagEngine, FD, HandlerDesc);
6537
6586
if (LegacyBodyCreator.createLegacyBody ()) {
6538
6587
LegacyBodyCreator.replace (FD->getBody (), EditConsumer);
6539
6588
}
6589
+
6590
+ // Add the async alternative
6540
6591
Converter.insertAfter (FD, EditConsumer);
6541
6592
6542
6593
return false ;
0 commit comments