Skip to content

Commit 3621dfe

Browse files
authored
Merge pull request #38880 from apple/cherry-rdar81230908
[5.5][Async Refactoring] Handle multiple trailing closures
2 parents 400e761 + 19817dc commit 3621dfe

File tree

2 files changed

+43
-14
lines changed

2 files changed

+43
-14
lines changed

lib/IDE/Refactoring.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7005,20 +7005,26 @@ class AsyncConverter : private SourceEntityWalker {
70057005
Scopes.back().Names.insert(ArgName);
70067006
OS << tok::kw_guard << ' ' << tok::kw_let << ' ' << ArgName << ' '
70077007
<< tok::equal << ' ';
7008+
7009+
// If the argument is a call with a trailing closure, the generated
7010+
// guard statement will not compile.
7011+
// e.g. 'guard let result1 = value.map { $0 + 1 } else { ... }'
7012+
// doesn't compile. Adding parentheses makes the code compile.
7013+
auto HasTrailingClosure = false;
7014+
if (auto *CE = dyn_cast<CallExpr>(Arg)) {
7015+
if (CE->getUnlabeledTrailingClosureIndex().hasValue())
7016+
HasTrailingClosure = true;
7017+
}
7018+
7019+
if (HasTrailingClosure)
7020+
OS << tok::l_paren;
7021+
70087022
convertNode(Arg, /*StartOverride=*/CE->getArgumentLabelLoc(ArgIndex),
70097023
/*ConvertCalls=*/false);
7010-
if (auto CE = dyn_cast<CallExpr>(Arg)) {
7011-
if (CE->hasTrailingClosure()) {
7012-
// If the argument is a call with trailing closure, the generated
7013-
// guard statement does not compile.
7014-
// e.g. 'guard let result1 = value.map { $0 + 1 } else { ... }'
7015-
// doesn't compile.
7016-
// Adding a '.self' at the end makes the code compile (although it
7017-
// will still issue a warning about a trailing closure use inside a
7018-
// guard condition).
7019-
OS << tok::period << tok::kw_self;
7020-
}
7021-
}
7024+
7025+
if (HasTrailingClosure)
7026+
OS << tok::r_paren;
7027+
70227028
OS << ' ' << tok::kw_else << ' ' << tok::l_brace << '\n';
70237029
OS << "fatalError" << tok::l_paren;
70247030
OS << "\"Expected non-nil result ";

test/refactoring/ConvertAsync/convert_to_continuation.swift

Lines changed: 25 additions & 2 deletions
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
@@ -173,7 +175,7 @@ func testThrowingContinuationRelayingErrorAndTwoComplexResults(completionHandler
173175
// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: }
174176
// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: }
175177

176-
// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE %s
178+
// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE %s
177179
func testThrowingContinuationRelayingErrorAndComplexResultWithTrailingClosure(completionHandler: @escaping (Int?, Error?) -> Void) {
178180
withoutAsyncAlternativeThrowing { (theValue, theError) in
179181
completionHandler(theValue.map { $0 + 1 }, theError)
@@ -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)