@@ -4079,7 +4079,7 @@ struct AsyncHandlerDesc {
40794079 HandlerType Type = HandlerType::INVALID;
40804080 bool HasError = false ;
40814081
4082- static AsyncHandlerDesc get (const ValueDecl *Handler, bool ignoreName ) {
4082+ static AsyncHandlerDesc get (const ValueDecl *Handler, bool RequireName ) {
40834083 AsyncHandlerDesc HandlerDesc;
40844084 if (auto Var = dyn_cast<VarDecl>(Handler)) {
40854085 HandlerDesc.Handler = Var;
@@ -4090,8 +4090,8 @@ struct AsyncHandlerDesc {
40904090 return AsyncHandlerDesc ();
40914091 }
40924092
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 ()))
40954095 return AsyncHandlerDesc ();
40964096
40974097 // Callback must be a function type and return void. Doesn't need to have
@@ -4340,17 +4340,26 @@ struct AsyncHandlerDesc {
43404340// / information about that completion handler and its index within the function
43414341// / declaration.
43424342struct AsyncHandlerParamDesc : public AsyncHandlerDesc {
4343+ // / The function the completion handler is a parameter of.
4344+ const FuncDecl *Func = nullptr ;
43434345 // / The index of the completion handler in the function that declares it.
43444346 int Index = -1 ;
43454347
43464348 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) {}
43494352
4350- static AsyncHandlerParamDesc find (const FuncDecl *FD, bool ignoreName) {
4353+ static AsyncHandlerParamDesc find (const FuncDecl *FD,
4354+ bool RequireAttributeOrName) {
43514355 if (!FD || FD->hasAsync () || FD->hasThrows ())
43524356 return AsyncHandlerParamDesc ();
43534357
4358+ bool RequireName = RequireAttributeOrName;
4359+ if (RequireAttributeOrName &&
4360+ FD->getAttrs ().hasAttribute <CompletionHandlerAsyncAttr>())
4361+ RequireName = false ;
4362+
43544363 // Require at least one parameter and void return type
43554364 auto *Params = FD->getParameters ();
43564365 if (Params->size () == 0 || !FD->getResultInterfaceType ()->isVoid ())
@@ -4364,10 +4373,30 @@ struct AsyncHandlerParamDesc : public AsyncHandlerDesc {
43644373 if (Param->isAutoClosure ())
43654374 return AsyncHandlerParamDesc ();
43664375
4367- return AsyncHandlerParamDesc (AsyncHandlerDesc::get (Param, ignoreName) ,
4376+ return AsyncHandlerParamDesc (AsyncHandlerDesc::get (Param, RequireName), FD ,
43684377 Index);
43694378 }
43704379
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+
43714400 bool operator ==(const AsyncHandlerParamDesc &Other) const {
43724401 return Handler == Other.Handler && Type == Other.Type &&
43734402 HasError == Other.HasError && Index == Other.Index ;
@@ -5554,8 +5583,12 @@ class AsyncConverter : private SourceEntityWalker {
55545583 return addCustom (CE->getSourceRange (), [&]() { addHandlerCall (CE); });
55555584
55565585 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.
55575589 auto HandlerDesc = AsyncHandlerParamDesc::find (
5558- getUnderlyingFunc (CE->getFn ()), StartNode.dyn_cast <Expr *>() == CE);
5590+ getUnderlyingFunc (CE->getFn ()),
5591+ /* RequireAttributeOrName=*/ StartNode.dyn_cast <Expr *>() != CE);
55595592 if (HandlerDesc.isValid ())
55605593 return addCustom (CE->getSourceRange (),
55615594 [&]() { addHoistedCallback (CE, HandlerDesc); });
@@ -5836,8 +5869,8 @@ class AsyncConverter : private SourceEntityWalker {
58365869
58375870 // The completion handler that is called as part of the \p CE call.
58385871 // 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 );
58415874 if (CompletionHandler.isValid ()) {
58425875 if (auto CalledFunc = getUnderlyingFunc (CE->getFn ())) {
58435876 StringRef HandlerName = Lexer::getCharSourceRangeFromSourceRange (
@@ -6430,8 +6463,8 @@ bool RefactoringActionConvertCallToAsyncAlternative::isApplicable(
64306463 if (!CE)
64316464 return false ;
64326465
6433- auto HandlerDesc = AsyncHandlerParamDesc::find (getUnderlyingFunc (CE-> getFn ()),
6434- /* ignoreName =*/ true );
6466+ auto HandlerDesc = AsyncHandlerParamDesc::find (
6467+ getUnderlyingFunc (CE-> getFn ()), /* RequireAttributeOrName =*/ false );
64356468 return HandlerDesc.isValid ();
64366469}
64376470
@@ -6488,7 +6521,8 @@ bool RefactoringActionConvertToAsync::performChange() {
64886521 assert (FD &&
64896522 " Should not run performChange when refactoring is not applicable" );
64906523
6491- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6524+ auto HandlerDesc =
6525+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
64926526 AsyncConverter Converter (TheFile, SM, DiagEngine, FD, HandlerDesc);
64936527 if (!Converter.convert ())
64946528 return true ;
@@ -6505,7 +6539,8 @@ bool RefactoringActionAddAsyncAlternative::isApplicable(
65056539 if (!FD)
65066540 return false ;
65076541
6508- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6542+ auto HandlerDesc =
6543+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
65096544 return HandlerDesc.isValid ();
65106545}
65116546
@@ -6522,21 +6557,37 @@ bool RefactoringActionAddAsyncAlternative::performChange() {
65226557 assert (FD &&
65236558 " Should not run performChange when refactoring is not applicable" );
65246559
6525- auto HandlerDesc = AsyncHandlerParamDesc::find (FD, /* ignoreName=*/ true );
6560+ auto HandlerDesc =
6561+ AsyncHandlerParamDesc::find (FD, /* RequireAttributeOrName=*/ false );
65266562 assert (HandlerDesc.isValid () &&
65276563 " Should not run performChange when refactoring is not applicable" );
65286564
65296565 AsyncConverter Converter (TheFile, SM, DiagEngine, FD, HandlerDesc);
65306566 if (!Converter.convert ())
65316567 return true ;
65326568
6569+ // Deprecate the synchronous function
65336570 EditConsumer.accept (SM, FD->getAttributeInsertionLoc (false ),
65346571 " @available(*, deprecated, message: \" Prefer async "
65356572 " 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+
65366585 AsyncConverter LegacyBodyCreator (TheFile, SM, DiagEngine, FD, HandlerDesc);
65376586 if (LegacyBodyCreator.createLegacyBody ()) {
65386587 LegacyBodyCreator.replace (FD->getBody (), EditConsumer);
65396588 }
6589+
6590+ // Add the async alternative
65406591 Converter.insertAfter (FD, EditConsumer);
65416592
65426593 return false ;
0 commit comments