// REQUIRES: concurrency // RUN: %empty-directory(%t) enum E : Error { case e } func anyCompletion(_ completion: @escaping (Any?, Error?) -> Void) {} func anyResultCompletion(_ completion: @escaping (Result) -> Void) {} func stringTupleParam(_ completion: @escaping ((String, String)?, Error?) -> Void) {} func stringTupleParam() async throws -> (String, String) {} func stringTupleResult(_ completion: @escaping (Result<(String, String), Error>) -> Void) {} func stringTupleResult() async throws -> (String, String) {} func mixedTupleResult(_ completion: @escaping (Result<((Int, Float), String), Error>) -> Void) {} func mixedTupleResult() async throws -> ((Int, Float), String) {} func multipleTupleParam(_ completion: @escaping ((String, String)?, (Int, Int)?, Error?) -> Void) {} func multipleTupleParam() async throws -> ((String, String), (Int, Int)) {} func testPatterns() async throws { // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE %s stringTupleParam { strs, err in guard let (str1, str2) = strs else { return } print(str1, str2) } // INLINE: let (str1, str2) = try await stringTupleParam() // INLINE-NEXT: print(str1, str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-VAR %s stringTupleParam { strs, err in guard var (str1, str2) = strs else { return } print(str1, str2) } // INLINE-VAR: var (str1, str2) = try await stringTupleParam() // INLINE-VAR-NEXT: print(str1, str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-BLANK %s stringTupleParam { strs, err in guard var (_, str2) = strs else { return } print(str2) } // INLINE-BLANK: var (_, str2) = try await stringTupleParam() // INLINE-BLANK-NEXT: print(str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-TYPED %s stringTupleParam { strs, err in guard let (str1, str2): (String, String) = strs else { return } print(str1, str2) } // INLINE-TYPED: let (str1, str2) = try await stringTupleParam() // INLINE-TYPED-NEXT: print(str1, str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-CASE %s stringTupleParam { strs, err in guard case (let str1, var str2)? = strs else { return } print(str1, str2) } // INLINE-CASE: var (str1, str2) = try await stringTupleParam() // INLINE-CASE-NEXT: print(str1, str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-CASE-TYPED %s stringTupleParam { strs, err in guard case let (str1, str2)?: (String, String)? = strs else { return } print(str1, str2) } // INLINE-CASE-TYPED: let (str1, str2) = try await stringTupleParam() // INLINE-CASE-TYPED-NEXT: print(str1, str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=OUT-OF-LINE %s stringTupleParam { strs, err in guard let (str1, str2) = strs else { return } print(str1, str2, strs!) } // OUT-OF-LINE: let strs = try await stringTupleParam() // OUT-OF-LINE-NEXT: let (str1, str2) = strs // OUT-OF-LINE-NEXT: print(str1, str2, strs) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=OUT-OF-LINE-VAR %s stringTupleParam { strs, err in guard var (str1, _) = strs else { return } str1 = "" print(str1, {strs!}) } // OUT-OF-LINE-VAR: let strs = try await stringTupleParam() // OUT-OF-LINE-VAR-NEXT: var (str1, _) = strs // OUT-OF-LINE-VAR-NEXT: str1 = "" // OUT-OF-LINE-VAR-NEXT: print(str1, {strs}) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=OUT-OF-LINE-CASE %s stringTupleParam { strs, err in guard case (let str1, var str2)? = strs else { return } print(str1, str2, strs!) } // OUT-OF-LINE-CASE: let strs = try await stringTupleParam() // OUT-OF-LINE-CASE-NEXT: var (str1, str2) = strs // OUT-OF-LINE-CASE-NEXT: print(str1, str2, strs) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=FALLBACK %s stringTupleParam { strs, err in guard let (str1, str2) = strs, str1 == "hi" else { fatalError() } print(str1, str2, err) } // FALLBACK: var strs: (String, String)? = nil // FALLBACK-NEXT: var err: (any Error)? = nil // FALLBACK-NEXT: do { // FALLBACK-NEXT: strs = try await stringTupleParam() // FALLBACK-NEXT: } catch { // FALLBACK-NEXT: err = error // FALLBACK-NEXT: } // FALLBACK-EMPTY: // FALLBACK-NEXT: guard let (str1, str2) = strs, str1 == "hi" else { fatalError() } // FALLBACK-NEXT: print(str1, str2, err) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=GUARD-AND-UNHANDLED %s stringTupleParam { strs, err in guard let (str1, str2) = strs else { fatalError() } print(str1, str2) if .random(), err == nil { print("yay") } else { print("nay") } } // GUARD-AND-UNHANDLED: do { // GUARD-AND-UNHANDLED-NEXT: let (str1, str2) = try await stringTupleParam() // GUARD-AND-UNHANDLED-NEXT: print(str1, str2) // GUARD-AND-UNHANDLED-NEXT: if .random(), <#err#> == nil { // GUARD-AND-UNHANDLED-NEXT: print("yay") // GUARD-AND-UNHANDLED-NEXT: } else { // GUARD-AND-UNHANDLED-NEXT: print("nay") // GUARD-AND-UNHANDLED-NEXT: } // GUARD-AND-UNHANDLED-NEXT: } catch let err { // GUARD-AND-UNHANDLED-NEXT: fatalError() // GUARD-AND-UNHANDLED-NEXT: } // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-BINDINGS %s stringTupleParam { strs, err in guard let x = strs else { return } guard var (str1, str2) = strs else { return } guard var y = strs else { return } guard let z = strs else { return } y = ("hello", "there") print(x, y, z, str1, str2) } // Make sure that we // 1. Coalesce the z binding, as it is a let // 2. Preserve the y binding, as it is a var // 3. Print the multi-var binding out of line // // MIXED-BINDINGS: let x = try await stringTupleParam() // MIXED-BINDINGS-NEXT: var (str1, str2) = x // MIXED-BINDINGS-NEXT: var y = x // MIXED-BINDINGS-NEXT: y = ("hello", "there") // MIXED-BINDINGS-NEXT: print(x, y, x, str1, str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-BINDINGS2 %s stringTupleParam { strs, err in guard var (str1, str2) = strs else { return } str1 = "hi" guard var x = strs else { return } x = ("hello", "there") print(x, str1, str2) } // MIXED-BINDINGS2: var x = try await stringTupleParam() // MIXED-BINDINGS2-NEXT: var (str1, str2) = x // MIXED-BINDINGS2-NEXT: str1 = "hi" // MIXED-BINDINGS2-NEXT: x = ("hello", "there") // MIXED-BINDINGS2-NEXT: print(x, str1, str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-BINDINGS3 %s stringTupleParam { strs, err in guard let (str1, str2) = strs else { return } guard let x = strs else { return } print(x, str1, str2) } // MIXED-BINDINGS3: let x = try await stringTupleParam() // MIXED-BINDINGS3-NEXT: let (str1, str2) = x // MIXED-BINDINGS3-NEXT: print(x, str1, str2) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS %s stringTupleParam { strs, err in guard let x = strs else { return } guard let y = strs else { return } guard let z = strs else { return } print(x, y, z) } // ALIAS-BINDINGS: let x = try await stringTupleParam() // ALIAS-BINDINGS-NEXT: print(x, x, x) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS2 %s stringTupleParam { strs, err in guard var x = strs else { return } guard var y = strs else { return } guard let z = strs else { return } print(x, y, z) } // ALIAS-BINDINGS2: let z = try await stringTupleParam() // ALIAS-BINDINGS2-NEXT: var x = z // ALIAS-BINDINGS2-NEXT: var y = z // ALIAS-BINDINGS2-NEXT: print(x, y, z) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS3 %s stringTupleParam { strs, err in guard var x = strs else { return } guard var y = strs else { return } guard var z = strs else { return } print(x, y, z) } // ALIAS-BINDINGS3: var x = try await stringTupleParam() // ALIAS-BINDINGS3-NEXT: var y = x // ALIAS-BINDINGS3-NEXT: var z = x // ALIAS-BINDINGS3-NEXT: print(x, y, z) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS4 %s stringTupleParam { strs, err in guard var x = strs else { return } guard let y = strs else { return } guard let z = strs else { return } print(x, y, z) } // ALIAS-BINDINGS4: let y = try await stringTupleParam() // ALIAS-BINDINGS4-NEXT: var x = y // ALIAS-BINDINGS4-NEXT: print(x, y, y) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS5 %s stringTupleParam { strs, err in guard var x = strs else { return } print(x) } // ALIAS-BINDINGS5: var x = try await stringTupleParam() // ALIAS-BINDINGS5-NEXT: print(x) // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=STRING-TUPLE-RESULT %s stringTupleResult { res in switch res { case .success((let x, let y)): print(x, y) case .failure: print("oh no") } } // STRING-TUPLE-RESULT: do { // STRING-TUPLE-RESULT-NEXT: let (x, y) = try await stringTupleResult() // STRING-TUPLE-RESULT-NEXT: print(x, y) // STRING-TUPLE-RESULT-NEXT: } catch { // STRING-TUPLE-RESULT-NEXT: print("oh no") // STRING-TUPLE-RESULT-NEXT: } // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=STRING-TUPLE-RESULT %s stringTupleResult { res in switch res { case let .success((x, y)): print(x, y) case .failure: print("oh no") } } // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-TUPLE-RESULT %s mixedTupleResult { res in if case .failure(let err) = res { print("oh no") } if case .success(((let x, let y), let z)) = res { print("a", x, y, z) } switch res { case .success(let ((x, _), z)): print("b", x, z) case .failure: print("oh no again") } } // MIXED-TUPLE-RESULT: do { // MIXED-TUPLE-RESULT-NEXT: let res = try await mixedTupleResult() // MIXED-TUPLE-RESULT-NEXT: let ((x, y), z) = res // MIXED-TUPLE-RESULT-NEXT: let ((x1, _), z1) = res // MIXED-TUPLE-RESULT-NEXT: print("a", x, y, z) // MIXED-TUPLE-RESULT-NEXT: print("b", x1, z1) // MIXED-TUPLE-RESULT-NEXT: } catch let err { // MIXED-TUPLE-RESULT-NEXT: print("oh no") // MIXED-TUPLE-RESULT-NEXT: print("oh no again") // MIXED-TUPLE-RESULT-NEXT: } // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-TUPLE-RESULT2 %s mixedTupleResult { res in switch res { case .success(((let x, let _), let z)): print(x, z) case .failure(let err): print("oh no \(err)") } } // MIXED-TUPLE-RESULT2: do { // MIXED-TUPLE-RESULT2-NEXT: let ((x, _), z) = try await mixedTupleResult() // MIXED-TUPLE-RESULT2-NEXT: print(x, z) // MIXED-TUPLE-RESULT2-NEXT: } catch let err { // MIXED-TUPLE-RESULT2-NEXT: print("oh no \(err)") // MIXED-TUPLE-RESULT2-NEXT: } // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-TUPLE-RESULT3 %s mixedTupleResult { res in if let ((_, y), z) = try? res.get() { print(y, z) } else { print("boo") } } // MIXED-TUPLE-RESULT3: do { // MIXED-TUPLE-RESULT3-NEXT: let ((_, y), z) = try await mixedTupleResult() // MIXED-TUPLE-RESULT3-NEXT: print(y, z) // MIXED-TUPLE-RESULT3-NEXT: } catch { // MIXED-TUPLE-RESULT3-NEXT: print("boo") // MIXED-TUPLE-RESULT3-NEXT: } // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIPLE-TUPLE-PARAM %s multipleTupleParam { strs, ints, err in guard let (str1, str2) = strs, let (int1, int2) = ints else { print("ohno") return } print(str1, str2, int1, int2) } // MULTIPLE-TUPLE-PARAM: do { // MULTIPLE-TUPLE-PARAM-NEXT: let ((str1, str2), (int1, int2)) = try await multipleTupleParam() // MULTIPLE-TUPLE-PARAM-NEXT: print(str1, str2, int1, int2) // MULTIPLE-TUPLE-PARAM-NEXT: } catch let err { // MULTIPLE-TUPLE-PARAM-NEXT: print("ohno") // MULTIPLE-TUPLE-PARAM-NEXT: } // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIPLE-TUPLE-PARAM2 %s multipleTupleParam { strs, ints, err in guard let (str1, str2) = strs, var (int1, int2) = ints else { print("ohno") return } print(str1, str2, int1, int2) } // MULTIPLE-TUPLE-PARAM2: do { // MULTIPLE-TUPLE-PARAM2-NEXT: var ((str1, str2), (int1, int2)) = try await multipleTupleParam() // MULTIPLE-TUPLE-PARAM2-NEXT: print(str1, str2, int1, int2) // MULTIPLE-TUPLE-PARAM2-NEXT: } catch let err { // MULTIPLE-TUPLE-PARAM2-NEXT: print("ohno") // MULTIPLE-TUPLE-PARAM2-NEXT: } // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIPLE-TUPLE-PARAM3 %s multipleTupleParam { strs, ints, err in guard let (str1, str2) = strs, let (int1, int2) = ints else { print("ohno") return } print(strs!) print(str1, str2, int1, int2) } // MULTIPLE-TUPLE-PARAM3: do { // MULTIPLE-TUPLE-PARAM3-NEXT: let (strs, (int1, int2)) = try await multipleTupleParam() // MULTIPLE-TUPLE-PARAM3-NEXT: let (str1, str2) = strs // MULTIPLE-TUPLE-PARAM3-NEXT: print(strs) // MULTIPLE-TUPLE-PARAM3-NEXT: print(str1, str2, int1, int2) // MULTIPLE-TUPLE-PARAM3-NEXT: } catch let err { // MULTIPLE-TUPLE-PARAM3-NEXT: print("ohno") // MULTIPLE-TUPLE-PARAM3-NEXT: } } // RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NAME-COLLISION %s func testNameCollision(_ completion: @escaping () -> Void) { let a = "" stringTupleParam { strs, err in guard let (a, b) = strs else { return } print(a, b) } let b = "" stringTupleParam { strs, err in guard let (a, b) = strs else { return } print(a, b, strs!) } print(a, b) completion() } // TODO: `throws` isn't added to the function declaration // NAME-COLLISION: func testNameCollision() async { // NAME-COLLISION-NEXT: let a = "" // NAME-COLLISION-NEXT: let (a1, b) = try await stringTupleParam() // NAME-COLLISION-NEXT: print(a1, b) // NAME-COLLISION-NEXT: let b1 = "" // NAME-COLLISION-NEXT: let strs = try await stringTupleParam() // NAME-COLLISION-NEXT: let (a2, b2) = strs // NAME-COLLISION-NEXT: print(a2, b2, strs) // NAME-COLLISION-NEXT: print(a, b1) // NAME-COLLISION-NEXT: return // NAME-COLLISION-NEXT: } // RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NAME-COLLISION2 %s func testNameCollision2(_ completion: @escaping () -> Void) { mixedTupleResult { res in guard case let .success((x, y), z) = res else { return } stringTupleParam { strs, err in if let (x, y) = strs { print("a", x, y) } } print("b", x, y, z) } } // TODO: `throws` isn't added to the function declaration // NAME-COLLISION2: func testNameCollision2() async { // NAME-COLLISION2-NEXT: let ((x, y), z) = try await mixedTupleResult() // NAME-COLLISION2-NEXT: let (x1, y1) = try await stringTupleParam() // NAME-COLLISION2-NEXT: print("a", x1, y1) // NAME-COLLISION2-NEXT: print("b", x, y, z) // NAME-COLLISION2-NEXT: } // Cannot use refactor-check-compiles, as cannot pattern match with placeholders. // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=TEST-UNHANDLED %s anyCompletion { val, err in if let x = val { print("a") } if let _ = val { print("b") } if case let x? = val { print("c") } if case let _? = val { print("d") } if case E.e? = val { print("e") } if case (let x as String)? = val { print("f") } if case let "" as String = val { print("g") } } // TEST-UNHANDLED: let x = try await anyCompletion() // TEST-UNHANDLED-NEXT: print("a") // TEST-UNHANDLED-NEXT: print("b") // TEST-UNHANDLED-NEXT: print("c") // TEST-UNHANDLED-NEXT: print("d") // TEST-UNHANDLED-NEXT: if case E.e? = <#x#> { // TEST-UNHANDLED-NEXT: print("e") // TEST-UNHANDLED-NEXT: } // TEST-UNHANDLED-NEXT: if case (let x as String)? = <#x#> { // TEST-UNHANDLED-NEXT: print("f") // TEST-UNHANDLED-NEXT: } // TEST-UNHANDLED-NEXT: if case let "" as String = <#x#> { // TEST-UNHANDLED-NEXT: print("g") // TEST-UNHANDLED-NEXT: } // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 anyResultCompletion { res in switch res { case .success(let unwrapped?): print(unwrapped) case .success(let x): print(x) case .failure: print("oh no") } } // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 anyResultCompletion { res in switch res { case .success(let str as String): print(str) case .success(let x): print(x) case .failure: print("oh no") } } // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 anyResultCompletion { res in switch res { case .success(E.e?): print("ee") case .success(let x): print(x) case .failure: print("oh no") } } // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 anyResultCompletion { res in switch res { case .success("" as String): print("empty string") case .success(let x): print(x) case .failure: print("oh no") } } // FIXME: This should ideally be turned into a 'catch let e as E'. // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 anyResultCompletion { res in switch res { case .success(let a): print(a) case .failure(let e as E): print("oh no e") case .failure: print("oh no") } } // FIXME: This should ideally be turned into a 'catch E.e'. // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 anyResultCompletion { res in switch res { case .success(let a): print(a) case .failure(E.e): print("oh no ee") case .failure: print("oh no") } } // Make sure we handle a capture list okay. class C { // RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=CAPTURE %s func foo() { let _ = { [weak self] in print(self!) } } } // CAPTURE: func foo() async { // CAPTURE-NEXT: let _ = { [weak self] in // CAPTURE-NEXT: print(self!) // CAPTURE-NEXT: } // CAPTURE-NEXT: }