Disclaimer: this is feedback from the person who implemented such a functionality using sed, of all tools (before macros were even an option), so take it for what it's worth, and not as coming from a particularly authoritative place in terms of the swiftiness of the solution…
On the contribution: this is undoubtedly valuable, in particular this seems to use Swift Syntax as straightforwardly as possible, such that this ought not break when unrelated additions are made to the Swift language syntax; and yet even then it requires quite a bit more code than my one-line sed command (with 3 substitution expressions), showing this task to be anything but trivial. Hence, this body of code is probably best handled by the community for when it inevitably needs to evolve with the times.
On the concept: as a reminder to this audience, the whole concept of implementing "reasync" as a macro is a stopgap at best, because of intrinsic limitations of what a macro can do: operating at the level of the AST, such a macro is unable to itself fully perform the kind of semantic validation that is to be expected of such a feature. For instance, no @Reasync macro that operates on the AST will ever catch that there is actually no way to generate a valid sync version of the combiner function here (sort of reimplementing significant compiler frontend functionality itself):
func b() async throws -> UInt
{
try await Task.sleep(nanoseconds: 1000);
return 3;
}
@Reasync func combiner(_ a: () async ->UInt) async throws -> UInt
{
return try await a() + b();
}
cf the discussion concluding at A case study for `reasync` - #36 by Zollerboy1
On the diagnostics: the whole concept of "reasync", modeled on rethrows, is that the marked function has the async effect only as a result of at least one of its nominal parameters being itself a function having the async effect (if its async effect is unrelated to its parameters, then it is not possible to call any version of it in a sync way, unless there is no await or equivalent in there, in which case the function does not need to be async itself in the first place!); and this is indeed the case with the usages of @Reasync in SwiftTestKit, where that parameter is named either body or property. Therefore, it would be better for the macro itself to catch situations early where it is meant to remove an await (or the async part of an async let, etc.), but no such parameter is found in the expression covered by the await; doing so would catch common oversights where async calls were added in that function in places unrelated to its parameter(s), and nothing complained so far since that was legal, up to now.
(As it stands, this diagnostic will be raised when the compiler will process the generated AST, at which points its context will be 100% generated code, which the user will need to reverse engineer as a prerequisite to figuring out where they went wrong; when macros were introduced I was under the impression they are responsible for performing this kind of diagnostic rather than let their generated code fail downstream, but I may have missed some developments in the meantime).
On the @ReasyncMembers macro: I myself have had no need for it, as none of the relevant functions in my code are members functions, but I can definitely picture the need for it.
On the commitment that this represents: blessing this macro would not commit the Swift community (besides the commitment to maintain this macro) any more than the existing practice of spelling out two versions of the relevant functions, one with async and await removed from it; and there is evidence this practice was already spreading as far back as 2022: The latest information on `reasync`? . That ship has sailed.
On the possibility of a more general macro that could remove any subset of the syntax: this would be really damaging to the possiblity to improve diagnostics by catching errors earlier as part of macro expansion. If the macro does not even know the meaning of what it is removing, it would not be able to help diagnose cases where the removal turns out to be ill-advised.
On the name proper: "reasync", while modeled on rethrows, is not as grammatically sound as its model if only to the extent async is not a verb. Which is the reason why I don't mind a stopgap functionality keeping that name, so the better eventual name can be reserved for future evolution such as the hoped for built-in language feature.