If the completion handler parameter has a default
argument, mark the resulting async function as
`@discardableResult`, as the caller may not care
about the result.
rdar://79190170
If we're going to be classifying nodes in either
an `else if` block or after a `guard` statement
with a known condition path, be more lenient with
unhandled conditions, as we already know what path
they should be classified along.
rdar://78564388
If we're in a case stmt body, any DeclRefExprs
to vars bound by a pattern will actually be to an
implicit var decl that's declared for the body. We
therefore need to walk to its "parent" to get to
the var referenced by the pattern, which will have
the correct entries in the naming and placeholder
maps.
Keep track of patterns that bind multiple vars and
print them out when converting an async call. If
the parameter being bound isn't referenced elsewhere,
we'll print the pattern inline as e.g:
```
let ((x, y), z) = await foo()
```
Otherwise, if the parameter is referenced elsewhere
in the block we'll print the pattern out of line,
such as:
```
let (res, z) = await foo()
let (x, y) = res
```
In addition, make sure to print var bindings out
of line if there's also a let binding, e.g:
```
let x = await foo()
var y = x
```
This ensures any mutations to y doesn't affect x.
If there's only a single var binding, we'll print
it inline.
rdar://77802560
`ClangFileRewriterHelper` only outputs the rewritten buffer when it's
destroyed, but the consumers were never being freed. Cleanup the
lifetime management of the stream and consumers so that they're properly
destroyed.
Rename `DuplicatingSourceEditConsumer` to
`BroadcastingSourceEditConsumer` to better describe its purpose.
The async refactoring may perform recursive AST
walks in cases such as transforming the body of
a hoisted closure. Make sure this can be handled
by the logic tracking the ASTWalker in the
SourceEntityWalker, such that we don't crash when
later converting the call to a completion callback.
rdar://78693050
We were trying to retrieve the name of all function calls in the body using `getBaseIdentifier`. But calls to `init` don’t have a base identifier, just a `DeclBaseName` (which is special). Work with the `DeclBaseName` internally to prevent the crash.
Fixes rdar://78024731 [SR-14637]
Add an additional case to `CallbackCondition` to
support boolean checks, and add the necessary
classification logic to determine whether a bool
flag check is for a success or failure path.
The logic currently first checks to see if the
async alternative has a convention that specifies
which parameter the flag is, and whether it's a
success or failure flag. If so, it classifies the
flag check accordingly. If there's no async
convention specified, we use a heuristic to see if
the error parameter is force unwrapped in the
failure block. If so, we treat that as the error
path. If there's no force unwrap of the error, we
leave the condition unhandled.
rdar://74063899
This allows an async alternative function to be
created that forwards onto the user's completion
handler function through the use of
`withCheckedContinuation`/`withCheckedThrowingContinuation`.
rdar://77802486
When adding an async alternative, add the @completionHandlerAsync
attribute to the sync function. Check for this attribute in addition to
the name check, ie. convert a call if the callee has either
@completionHandlerAsync or a name that is completion-handler-like name.
The addition of the attribute is currently gated behind the experimental
concurrency flag.
Resolves rdar://77486504
Previously we were unconditionally dropping a
return statement if it was the last node, which
could cause us to inadvertently drop the result
expression as well. Instead, only drop bare
'return' statements.
rdar://77789360
It's possible the user has already written an
explicit return for the call to the completion
handler. In that case, avoid adding another return.
rdar://77789360
Previously we would drop comments between nodes in
a BraceStmt, as we printed each node out individually.
To remedy this, always make sure we scan backwards
to find any preceding comments attached to a node,
and also keep track of any SourceLocs which we
don't print, but may have comments attached which
we want to preserve.
rdar://77401810
When converting a call or function, rename declarations such that
redeclaration errors and shadowing are avoided. In some cases this will
be overly conservative, but since any renamed variable can be fixed with
edit all in scope, this is preferred over causing redeclaration errors
or possible shadowing.
Resolves rdar://73973517
When a function’s completion handler is being passed to another function and the function that the completion handler is being declared in is converted to async, replace the call to the completion handler by a `return` statement.
For example:
```swift
func foo(completion: (String) -> Void) {
bar(completion)
}
```
becomes
```swift
func foo() async -> String {
return await bar()
}
```
Previously, we were calling the completion handler, which no longer exists in the newly created `async` function.
Previously, we only supported refactoring a function to call the async alternative if a closure was used for the callback parameter. With this change, we also support calling a arbitrary function (or variable with function type) that is passed to the completion handler argument.
The implementation basically re-uses the code we already have to create the legacy function’s body (which calls the newly created async version and then forwards the arguments to the legacy completion handler).
To describe the completion handler that the result is being forwarded to, I’m also using `AsyncHandlerDesc`, but since the completion handler may be a variable, it doesn’t necessarily have an `Index` within a function decl that declares it. Because of this, I split the `AsyncHandlerDesc` up into a context-free `AsyncHandlerDesc` (without an `Index`) and `AsyncHandlerParamDesc` (which includes the `Index`). It turns out that `AsyncHandlerDesc` is sufficient in most places.
Resolves rdar://77460524
If we're lifting them outside of the control flow
structure they're dealing with, turn them into
placeholders, as they will no longer perform the
control flow the user is expecting.
This handles:
- Return statements at the top-level of the callback.
- Break statements in switches that we re-write.
Resolves rdar://74014897.
Instead of leaving two copies of the same implementation, rewrite the old method with the completion handler to call the newly added `async` method.
Resolves rdar://74464833
Remove an optional chain of a success parameter,
as it will no longer be optional, similar to how
we remove a force unwrap.
Note that while this is a locally valid transform
within the optional chain, e.g `foo?.x` -> `foo.x`,
it may change the type of the overall chain, which
could cause errors elsewhere in the code. However
this is generally more useful to the user than
just leaving `foo` as a placeholder. Note this is
only the case when no other optionals are involved
in the chain, e.g `foo?.x?.y` -> `foo.x?.y` is
completely valid.
Resolves rdar://74014826.
Convert function to async currently only adds "async" to the function and runs the convert call refactoring on the body.
This was intentional, but it turns out to be somewhat confusing. Instead, run the same refactoring as the add async alternative refactoring but just replace rather than add.
Resolves rdar://77103049
When converting a function with a completion handler
that has a Void success parameter, e.g
`(Void?, Error?) -> Void`, or more likely a
`Result<Void, Error>` parameter, make sure to omit
the `-> Void` from the resulting async function
conversion.
In addition, strip any Void bindings from an async
function call, and any explicit Void return values
from inside the async function.
Resolves rdar://75189289
We already have special logic to extrac the closure for closures with capture lists, add the same kind of logic for closures that are marked `@convention(block)` etc.
Resolves rdar://75301524 [SR-14328]
When a function declaration has no body (e.g. because it’s a protocol requirement), we construct the range to replace by the `async` keyword as follows:
- Start: One character after the closing `)` (or potentially the `throws` keyword if it exists)
- End: Last token in the function declaration
Since the last token in the function declaration is the `)`, we end up with a range that has `End < Start`, which crashes when trying to print the range.
If the function has no body, we should just use the range’s start location as the end location to construct an empty range.
Fixes rdar://76677035
Default arguments were still being visited when converting the call,
adding extra commas to the converted call. Skip over them.
Resolves rdar://74248990
Still convert the call if it was requested directly - only check the name
when converting a whole function. Once we have an attribute, we should
use that instead.
The replacement range was `FunctionDecl::getRange`, which does not
include attributes. This would cause attributes to be duplicated when
converting a function to async. Override the start with the attribute
start instead so that they are replaced as well.
Resolves rdar://74063741