Files
swift-mirror/test/refactoring/ConvertAsync/convert_pattern.swift
Hamish Knight 6d34fd9ed1 Handle parent vars in async transform
If we're in a case stmt body, any DeclRefExprs
to vars bound by a pattern will actually be to an
implicit var decl that's declared for the body. We
therefore need to walk to its "parent" to get to
the var referenced by the pattern, which will have
the correct entries in the naming and placeholder
maps.
2021-06-09 10:43:25 +01:00

562 lines
20 KiB
Swift

// RUN: %empty-directory(%t)
enum E : Error { case e }
func anyCompletion(_ completion: (Any?, Error?) -> Void) {}
func anyResultCompletion(_ completion: (Result<Any?, Error>) -> Void) {}
func stringTupleParam(_ completion: ((String, String)?, Error?) -> Void) {}
func stringTupleParam() async throws -> (String, String) {}
func stringTupleResult(_ completion: (Result<(String, String), Error>) -> Void) {}
func stringTupleResult() async throws -> (String, String) {}
func mixedTupleResult(_ completion: (Result<((Int, Float), String), Error>) -> Void) {}
func mixedTupleResult() async throws -> ((Int, Float), String) {}
func multipleTupleParam(_ completion: ((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: 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)
// FIXME: Arguably we should be able to classify everything after the guard as
// a success path and avoid the fallback in this case.
// RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=FALLBACK2 %s
stringTupleParam { strs, err in
guard let (str1, str2) = strs else { fatalError() }
print(str1, str2)
if .random(), err == nil {
print("yay")
} else {
print("nay")
}
}
// FALLBACK2: var strs: (String, String)? = nil
// FALLBACK2-NEXT: var err: Error? = nil
// FALLBACK2-NEXT: do {
// FALLBACK2-NEXT: strs = try await stringTupleParam()
// FALLBACK2-NEXT: } catch {
// FALLBACK2-NEXT: err = error
// FALLBACK2-NEXT: }
// FALLBACK2-EMPTY:
// FALLBACK2-NEXT: guard let (str1, str2) = strs else { fatalError() }
// FALLBACK2-NEXT: print(str1, str2)
// FALLBACK2-NEXT: if .random(), err == nil {
// FALLBACK2-NEXT: print("yay")
// FALLBACK2-NEXT: } else {
// FALLBACK2-NEXT: print("nay")
// FALLBACK2-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=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: () -> 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: () -> 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: }
// 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 -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: }