`module.map` as a module map name has been discouraged since 2014, and
Clang will soon warn on its usage. This patch renames all instances of
`module.map` in the Swift tests to `module.modulemap` in preparation
for this change to Clang.
rdar://106123303
Generalize the logic to handle different BlockKinds,
and add binding logic that lets us assign `true` or
`false` to the given bool success param in the
fallback case.
If we have a known bool flag parameter, drop it
from the success parameters being bound in a
refactored await call, as the async variant drops
it.
rdar://81896460
The async refactorings should not try to unique '_' when it's used as
the parameter name. Also skip adding a let binding to the `catch` if the
error parameter is '_'.
Resolves rdar://82158389
Convert Function to Async is available on an async function. It could be
useful to run this refactoring still, as it would attempt to convert any
completion-handler functions to their async alternatives. Keep allowing
this, but make sure not to re-add "async" to the function declaration.
Resolves rdar://82156720
`%build-clang-importer-objc-overlays` builds the
overlays for the current target rather than the
host, so update the `%refactor` invocations to
take the target as an argument.
rdar://81128571
Update the async refactoring tests in `convert_function.swift` to
compile the refactored code (where possible) to check for errors. This
would have prevented the issue with the refactored `Result<Void, ...`>
handler producing erroneous code.
When adding an async alternative for a function with a
`(Result<Void, ...>) -> Void` completion handler, the legacy body was
adding a call to the handler with `.success(result)`. We don't assign to
`result` for `Void` handlers, so pass `()` instead.
Resolves rdar://81829392
Update the async refactorings to use the name from the async alternative
if one is known, rather than always assuming the name matches the
synchronous function.
Note that this could also be used to determine whether results remain
optional or not, but that has been left for a future patch.
Resolves rdar://80612521
Instead of a new attribute `@completionHandlerAsync`, allow the use of
the existing `renamed` parameter of `@available` to specify the
asynchronous alternative of a synchronous function.
No errors will be output from invalid names as `@completionHandlerAsync`
had, but if a function is correctly matched then it will be used to
output warnings when using the synchronous function in an asynchronous
context (as before).
Resolves rdar://80612731
Update the trailing closure handling logic to
handle multiple trailing closures, and adjust the
refactoring output to surround the call in
parentheses rather than adding '.self'. This allows
the parser to deal with the multiple trailing
closures and also silences a warning that would
previously occur.
rdar://81230908
The async refactorings ignore whether a completion handler had
`@escaping` or not. In preparation of fixing this, fix up all functions
to have `@escaping` for their completion handler parameter.
Also some small miscellaneous fixes in order to reduce the number of
warnings output on test failures and also the addition of `REQUIRES:
concurrency` on all tests.
When passing a forwarded error to a CustomError?
completion handler parameter, wrap the cast in a
set of parentheses to silence a warning in the
refactored code.
rdar://80409905
In the previous `fatalError` message `Expected non-nil result 'result' for nil error`, it wasn’t exactly clear wht the `error` referred to. In some cases, when the error was ignored, there wasn’t even an `error` variable in the refactored code.
The new `Expected non-nil result '...' in the non-error case` is a little more generic and also fits if an error is ignored.
The 'success param/argument' is a terminology we use internally in the refactoring and not one we want to present to the user. Just name it 'result' instead.
Since we aren’t checking that all result arguments are `nil` if there was an `error` in all the other refactorings, also remove the assertions in the async wrapper.
Previously, when a completion handler call had arguments for both the success and error parameters, we were always interpreting it as an error call. In case the error argument was an `Optional`, this could cause us to generate code that didn't compile, because we `throw`ed the `Error?` or passed it to `continuation.resume(throwing)`, both of which didn't work.
We now generate an `if let` statement that checks if the error is `nil`. If it is, the error is thrown. Otherwise, we interpret the call as a success call and return the result.
- Example 1 (convert to continuation)
-- Base Code
```swift
func test(completionHandler: (Int?, Error?) -> Void) {
withoutAsyncAlternativeThrowing { (theValue, theError) in
completionHandler(theValue, theError)
}
}
```
-- Old Refactoring Result
```swift
func test() async throws -> Int {
return try await withCheckedThrowingContinuation { continuation in
withoutAsyncAlternativeThrowing { (theValue, theError) in
continuation.resume(throwing: theError) // error: Argument type 'Error?' does not conform to expected type 'Error'
}
}
}
-- New Refactoring Result
```swift
func testThrowingContinuationRelayingErrorAndResult() async throws -> Int {
return try await withCheckedThrowingContinuation { continuation in
withoutAsyncAlternativeThrowing { (theValue, theError) in
if let error = theError {
continuation.resume(throwing: error)
} else {
guard let theValue = theValue else {
fatalError("Expected non-nil success argument 'theValue' for nil error")
}
continuation.resume(returning: theValue)
}
}
}
}
```
- Example 2 (convert to async/await)
-- Base Code
```swift
func test(completion: (String?, Error?) -> Void) {
simpleErr() { (res, err) in
completion(res, err)
}
}
```
-- Old Refactoring Result
```swift
func test() async throws -> String {
let res = try await simpleErr()
throw <#err#>
}
```
-- New Refactoring Result
```swift
func test() async throws -> String {
let res = try await simpleErr()
return res
}
```
Two mostly-cosmetic change to the "Add Async Wrapper" refactoring
- Rename the continuation from `cont` to `continuation` and error from `err` to `error` to better match Swifts naming guidelines
- Add assertions that all result parameters are `nil` if an error is passed to the completion handler.
Resolves rdar://80172152
If we are requested to convert a function to async, but the call in the function’s body that eventually calls the completion handler doesn’t have an async alternative, we are currently copying the call as-is, replacing any calls to the completion handler by placeholders.
For example,
```swift
func testDispatch(completionHandler: @escaping (Int) -> Void) {
DispatchQueue.global.async {
completionHandler(longSyncFunc())
}
}
```
becomes
```swift
func testDispatch() async -> Int {
DispatchQueue.global.async {
<#completionHandler#>(longSyncFunc())
}
}
```
and
```swift
func testUrlSession(completionHandler: @escaping (Data) -> Void) {
let task = URLSession.shared.dataTask(with: request) { data, response, error in
completion(data!)
}
task.resume()
}
```
becomes
```swift
func testUrlSession() async -> Data {
let task = URLSession.shared.dataTask(with: request) { data, response, error in
<#completion#>(data!)
}
task.resume()
}
```
Both of these are better modelled using continuations. Thus, if we find an expression that contains a call to the completion handler and can’t be hoisted to an await statement, we are wrapping the rest of the current scope in a `withChecked(Throwing)Continuation`, producing the following results:
```swift
func testDispatch() async -> Int {
return await withCheckedContinuation { (continuation: CheckedContinuation<Int, Never>) in
DispatchQueue.global.async {
continuation.resume(returning: syncComputation())
}
}
}
```
and
```swift
func testDataTask() async -> Int?
return await withCheckedContinuation { (continuation: CheckedContinuation<Data, Never>) in
let task = URLSession.shared.dataTask { data, response, error in
continuation.resume(returning: data!)
}
task.resume()
}
}
```
I think both are much closer to what the developer is actually expecting.
Resolves rdar://79304583
Previously, in the following case we were failing to add a `return` keyword inside `withImplicitReturn`.
```
func withImplicitReturn(completionHandler: (String) -> Void) {
simple {
completionHandler($0)
}
}
```
This is because the call of `completionHandler($0)` is wrapped by an implicit `ReturnStmt` and thus we assumed that there was already a `return` keyword present.
Fix this issue by checking if the wrapping `ReturnStmt` is implicit and if it is, add the `return` keyword.
Fixes rdar://80009760
If a completion handler specifies internal parameter labels, we can use those to label the elements of the tuple returned by the async alternative.
For example
```swift
func foo(completion: (_ first: String, _ second: String) -> Void) { }
```
gets refactored to
```swift
func foo() async -> (first: String, second: String) { }
```
Resolves rdar://77268040
Previously we only supported `case` patterns that bound with a `let` inside the associated value like `case .success(let value)`. With this change, we also support `case let .success(value)`.
Fixes rdar://79279846 [SR-14772]