mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Async Refactoring] Wrap code in a continuation if conversion doesn't yield reasonable results
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
This commit is contained in:
@@ -129,8 +129,8 @@ func asyncResNewErr(arg: String, _ completion: (Result<String, Error>) -> Void)
|
||||
// ASYNC-ERR-NEXT: }
|
||||
// ASYNC-ERR-NEXT: }
|
||||
|
||||
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-UNHANDLED %s
|
||||
func asyncUnhandledCompletion(_ completion: (String) -> Void) {
|
||||
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CALL-NON-ASYNC-IN-ASYNC %s
|
||||
func callNonAsyncInAsync(_ completion: (String) -> Void) {
|
||||
simple { str in
|
||||
let success = run {
|
||||
completion(str)
|
||||
@@ -141,19 +141,21 @@ func asyncUnhandledCompletion(_ completion: (String) -> Void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ASYNC-UNHANDLED: func asyncUnhandledCompletion() async -> String {
|
||||
// ASYNC-UNHANDLED-NEXT: let str = await simple()
|
||||
// ASYNC-UNHANDLED-NEXT: let success = run {
|
||||
// ASYNC-UNHANDLED-NEXT: <#completion#>(str)
|
||||
// ASYNC-UNHANDLED-NEXT: {{^}} return true{{$}}
|
||||
// ASYNC-UNHANDLED-NEXT: }
|
||||
// ASYNC-UNHANDLED-NEXT: if !success {
|
||||
// ASYNC-UNHANDLED-NEXT: {{^}} return "bad"{{$}}
|
||||
// ASYNC-UNHANDLED-NEXT: }
|
||||
// ASYNC-UNHANDLED-NEXT: }
|
||||
// CALL-NON-ASYNC-IN-ASYNC: func callNonAsyncInAsync() async -> String {
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: let str = await simple()
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: return await withCheckedContinuation { continuation in
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: let success = run {
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: continuation.resume(returning: str)
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: {{^}} return true{{$}}
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: }
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: if !success {
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: continuation.resume(returning: "bad")
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: }
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: }
|
||||
// CALL-NON-ASYNC-IN-ASYNC-NEXT: }
|
||||
|
||||
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-UNHANDLED-COMMENT %s
|
||||
func asyncUnhandledCommentedCompletion(_ completion: (String) -> Void) {
|
||||
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CALL-NON-ASYNC-IN-ASYNC-COMMENT %s
|
||||
func callNonAsyncInAsyncComment(_ completion: (String) -> Void) {
|
||||
// a
|
||||
simple { str in // b
|
||||
// c
|
||||
@@ -174,28 +176,30 @@ func asyncUnhandledCommentedCompletion(_ completion: (String) -> Void) {
|
||||
}
|
||||
// k
|
||||
}
|
||||
// ASYNC-UNHANDLED-COMMENT: func asyncUnhandledCommentedCompletion() async -> String {
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // a
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: let str = await simple()
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // b
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // c
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: let success = run {
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // d
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: <#completion#>(str)
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // e
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: {{^}} return true{{$}}
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // f
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: }
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // g
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: if !success {
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // h
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: {{^}} return "bad"{{$}}
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // i
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: }
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // j
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: {{ }}
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: // k
|
||||
// ASYNC-UNHANDLED-COMMENT-NEXT: }
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT: func callNonAsyncInAsyncComment() async -> String {
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // a
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: let str = await simple()
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // b
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // c
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: return await withCheckedContinuation { continuation in
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: let success = run {
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // d
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: continuation.resume(returning: str)
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // e
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: {{^}} return true{{$}}
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // f
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: }
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // g
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: if !success {
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // h
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: continuation.resume(returning: "bad")
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // i
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: }
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // j
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: {{ }}
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // k
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: }
|
||||
// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: }
|
||||
|
||||
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-HANDLER %s
|
||||
func voidAndErrorCompletion(completion: (Void?, Error?) -> Void) {
|
||||
@@ -289,7 +293,9 @@ func testReturnHandling3(_ completion: (String?, Error?) -> Void) {
|
||||
return (completion("", nil))
|
||||
}
|
||||
// RETURN-HANDLING3: func testReturnHandling3() async throws -> String {
|
||||
// RETURN-HANDLING3-NEXT: {{^}} return (<#completion#>("", nil)){{$}}
|
||||
// RETURN-HANDLING3-NEXT: return try await withCheckedThrowingContinuation { continuation in
|
||||
// RETURN-HANDLING3-NEXT: (continuation.resume(returning: ""))
|
||||
// RETURN-HANDLING3-NEXT: }
|
||||
// RETURN-HANDLING3-NEXT: }
|
||||
|
||||
// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RDAR78693050 %s
|
||||
|
||||
Reference in New Issue
Block a user