Skip to content

Commit 19817dc

Browse files
hamishknightbnbarham
authored andcommitted
[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 817473d commit 19817dc

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
@@ -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: 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)