Skip to content

Commit 06908ad

Browse files
committed
[Async Refactoring] Handle multiple trailing closures
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
1 parent 91c9b18 commit 06908ad

File tree

2 files changed

+42
-13
lines changed

2 files changed

+42
-13
lines changed

lib/IDE/Refactoring.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6901,20 +6901,26 @@ class AsyncConverter : private SourceEntityWalker {
69016901
Scopes.back().Names.insert(ArgName);
69026902
OS << tok::kw_guard << ' ' << tok::kw_let << ' ' << ArgName << ' '
69036903
<< tok::equal << ' ';
6904+
6905+
// If the argument is a call with a trailing closure, the generated
6906+
// guard statement will not compile.
6907+
// e.g. 'guard let result1 = value.map { $0 + 1 } else { ... }'
6908+
// doesn't compile. Adding parentheses makes the code compile.
6909+
auto HasTrailingClosure = false;
6910+
if (auto *CE = dyn_cast<CallExpr>(Arg)) {
6911+
if (CE->getUnlabeledTrailingClosureIndex().hasValue())
6912+
HasTrailingClosure = true;
6913+
}
6914+
6915+
if (HasTrailingClosure)
6916+
OS << tok::l_paren;
6917+
69046918
convertNode(Arg, /*StartOverride=*/CE->getArgumentLabelLoc(ArgIndex),
69056919
/*ConvertCalls=*/false);
6906-
if (auto CE = dyn_cast<CallExpr>(Arg)) {
6907-
if (CE->hasTrailingClosure()) {
6908-
// If the argument is a call with trailing closure, the generated
6909-
// guard statement does not compile.
6910-
// e.g. 'guard let result1 = value.map { $0 + 1 } else { ... }'
6911-
// doesn't compile.
6912-
// Adding a '.self' at the end makes the code compile (although it
6913-
// will still issue a warning about a trailing closure use inside a
6914-
// guard condition).
6915-
OS << tok::period << tok::kw_self;
6916-
}
6917-
}
6920+
6921+
if (HasTrailingClosure)
6922+
OS << tok::r_paren;
6923+
69186924
OS << ' ' << tok::kw_else << ' ' << tok::l_brace << '\n';
69196925
OS << "fatalError" << tok::l_paren;
69206926
OS << "\"Expected non-nil result ";

test/refactoring/ConvertAsync/convert_to_continuation.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ func withoutAsyncAlternativeThrowingWithMultipleResults(closure: @escaping (Int?
1212
func asyncVoidWithoutAlternative(completionHandler2: @escaping () -> Void) {}
1313
func resultWithoutAlternative(completionHandler2: @escaping (Result<Int, Error>) -> Void) {}
1414

15+
func lottaClosures(x: () -> Void, y: () -> Void) -> Int? { nil }
16+
1517
struct MyError: Error {}
1618

1719
// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CREATE-CONTINUATION %s
@@ -185,7 +187,7 @@ func testThrowingContinuationRelayingErrorAndComplexResultWithTrailingClosure(co
185187
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: if let error = theError {
186188
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: continuation.resume(throwing: error)
187189
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: } else {
188-
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: guard let result = theValue.map { $0 + 1 }.self else {
190+
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: guard let result = (theValue.map { $0 + 1 }) else {
189191
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: fatalError("Expected non-nil result in the non-error case")
190192
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: }
191193
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: continuation.resume(returning: result)
@@ -194,6 +196,27 @@ func testThrowingContinuationRelayingErrorAndComplexResultWithTrailingClosure(co
194196
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: }
195197
// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: }
196198

199+
// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-TRAILING-CLOSURES %s
200+
func testThrowingContinuationRelayingErrorAndComplexResultWithMultipleTrailingClosures(completionHandler: @escaping (Int?, Error?) -> Void) {
201+
withoutAsyncAlternativeThrowing { theValue, theError in
202+
completionHandler(lottaClosures {} y: {}, theError)
203+
}
204+
}
205+
// MULTIPLE-TRAILING-CLOSURES: func testThrowingContinuationRelayingErrorAndComplexResultWithMultipleTrailingClosures() async throws -> Int {
206+
// MULTIPLE-TRAILING-CLOSURES-NEXT: return try await withCheckedThrowingContinuation { continuation in
207+
// MULTIPLE-TRAILING-CLOSURES-NEXT: withoutAsyncAlternativeThrowing { theValue, theError in
208+
// MULTIPLE-TRAILING-CLOSURES-NEXT: if let error = theError {
209+
// MULTIPLE-TRAILING-CLOSURES-NEXT: continuation.resume(throwing: error)
210+
// MULTIPLE-TRAILING-CLOSURES-NEXT: } else {
211+
// MULTIPLE-TRAILING-CLOSURES-NEXT: guard let result = (lottaClosures {} y: {}) else {
212+
// MULTIPLE-TRAILING-CLOSURES-NEXT: fatalError("Expected non-nil result in the non-error case")
213+
// MULTIPLE-TRAILING-CLOSURES-NEXT: }
214+
// MULTIPLE-TRAILING-CLOSURES-NEXT: continuation.resume(returning: result)
215+
// MULTIPLE-TRAILING-CLOSURES-NEXT: }
216+
// MULTIPLE-TRAILING-CLOSURES-NEXT: }
217+
// MULTIPLE-TRAILING-CLOSURES-NEXT: }
218+
// MULTIPLE-TRAILING-CLOSURES-NEXT: }
219+
197220
// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT %s
198221
func testAlwaysReturnBothResultAndCompletionHandler(completionHandler: @escaping (Int?, Error?) -> Void) {
199222
withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { theValue in

0 commit comments

Comments
 (0)