diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index f69f1d45b37..1fac22ea9ac 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -149,6 +149,9 @@ namespace swift { /// Should potential unavailability on enum cases be downgraded to a warning? bool WarnOnPotentiallyUnavailableEnumCase = false; + /// Should the editor placeholder error be downgraded to a warning? + bool WarnOnEditorPlaceholder = false; + /// Maximum number of typo corrections we are allowed to perform. /// This is disabled by default until we can get typo-correction working within acceptable performance bounds. unsigned TypoCorrectionLimit = 0; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 7df8f37246a..617ab614bb6 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -462,6 +462,10 @@ def warn_on_potentially_unavailable_enum_case : Flag<["-"], "warn-on-potentially-unavailable-enum-case">, HelpText<"Downgrade potential unavailability of enum case to a warning">; +def warn_on_editor_placeholder : Flag<["-"], + "warn-on-editor-placeholder">, + HelpText<"Downgrade the editor placeholder error to a warning">; + def report_errors_to_debugger : Flag<["-"], "report-errors-to-debugger">, HelpText<"Deprecated, will be removed in future versions.">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index fa7278cbff0..4d4ebd0b34a 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -467,6 +467,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.WarnOnPotentiallyUnavailableEnumCase |= Args.hasArg(OPT_warn_on_potentially_unavailable_enum_case); + Opts.WarnOnEditorPlaceholder |= Args.hasArg(OPT_warn_on_editor_placeholder); + if (auto A = Args.getLastArg(OPT_enable_access_control, OPT_disable_access_control)) { Opts.EnableAccessControl diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index fc7da734576..0a346eabd81 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -2145,9 +2145,10 @@ void Lexer::tryLexEditorPlaceholder() { if (Ptr[0] == '<' && Ptr[1] == '#') break; if (Ptr[0] == '#' && Ptr[1] == '>') { - // Found it. Flag it as error (or warning, if in playground mode) for the - // rest of the compiler pipeline and lex it as an identifier. - if (LangOpts.Playground) { + // Found it. Flag it as error (or warning, if in playground mode or we've + // been asked to warn) for the rest of the compiler pipeline and lex it + // as an identifier. + if (LangOpts.Playground || LangOpts.WarnOnEditorPlaceholder) { diagnose(TokStart, diag::lex_editor_placeholder_in_playground); } else { diagnose(TokStart, diag::lex_editor_placeholder); diff --git a/test/Concurrency/Runtime/async_task_locals_groups.swift b/test/Concurrency/Runtime/async_task_locals_groups.swift index 8ee9b91a33e..2a8cf68cfd5 100644 --- a/test/Concurrency/Runtime/async_task_locals_groups.swift +++ b/test/Concurrency/Runtime/async_task_locals_groups.swift @@ -1,5 +1,6 @@ // RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s +// REQUIRES: rdar82092187 // REQUIRES: executable_test // REQUIRES: concurrency // REQUIRES: libdispatch diff --git a/test/refactoring/ConvertAsync/basic.swift b/test/refactoring/ConvertAsync/basic.swift index 89cc5ffa478..eb26940a4a5 100644 --- a/test/refactoring/ConvertAsync/basic.swift +++ b/test/refactoring/ConvertAsync/basic.swift @@ -14,9 +14,9 @@ typealias NestedAliasCallback = SomeCallback // 1. Check various functions for having/not having async alternatives // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+4):1 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+3):6 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):12 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):20 | %FileCheck -check-prefix=ASYNC-SIMPLE %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+3):6 | %FileCheck -check-prefix=ASYNC-SIMPLE %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):12 | %FileCheck -check-prefix=ASYNC-SIMPLE %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):20 | %FileCheck -check-prefix=ASYNC-SIMPLE %s func simple(/*cs*/ completion: @escaping (String) -> Void /*ce*/) { } // ASYNC-SIMPLE: basic.swift [[# @LINE-1]]:1 -> [[# @LINE-1]]:1 // ASYNC-SIMPLE-NEXT: @available(*, renamed: "simple()") @@ -113,7 +113,7 @@ func errorOnly(completion: @escaping (Error?) -> Void) { } // ASYNC-ERRORONLY-NEXT: } // ASYNC-ERRORONLY: func errorOnly() async throws { } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ERRORNONOPTIONALRESULT %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ERRORNONOPTIONALRESULT %s func errorNonOptionalResult(completion: @escaping (String, Error?) -> Void) { } // ASYNC-ERRORNONOPTIONALRESULT: { // ASYNC-ERRORNONOPTIONALRESULT-NEXT: Task { @@ -253,7 +253,7 @@ func mixed(_ completion: @escaping (String?, Int) -> Void) { } // MIXED-NEXT: } // MIXED: func mixed() async -> (String?, Int) { } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-OPTIONAL-ERROR %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-OPTIONAL-ERROR %s func mixedOptionalError(_ completion: @escaping (String?, Int, Error?) -> Void) { } // MIXED-OPTIONAL-ERROR: { // MIXED-OPTIONAL-ERROR-NEXT: Task { @@ -377,7 +377,7 @@ protocol MyProtocol { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-COMPLETION %s +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-COMPLETION %s func nonCompletion(a: Int) { } // NON-COMPLETION: func nonCompletion(a: Int) async { } @@ -387,27 +387,27 @@ func nonEscapingCompletion(completion: (Int) -> Void) { } // NON-ESCAPING-COMPLETION: func nonEscapingCompletion(completion: (Int) -> Void) async { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-RESULTS %s +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-RESULTS %s func multipleResults(completion: @escaping (Result, Result) -> Void) { } // MULTIPLE-RESULTS: func multipleResults(completion: @escaping (Result, Result) -> Void) async { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-VOID %s +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-VOID %s func nonVoid(completion: @escaping (String) -> Void) -> Int { return 0 } // NON-VOID: func nonVoid(completion: @escaping (String) -> Void) async -> Int { return 0 } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=COMPLETION-NON-VOID %s +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=COMPLETION-NON-VOID %s func completionNonVoid(completion: @escaping (String) -> Int) -> Void { } // COMPLETION-NON-VOID: func completionNonVoid(completion: @escaping (String) -> Int) async -> Void { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ALREADY-THROWS %s +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ALREADY-THROWS %s func alreadyThrows(completion: @escaping (String) -> Void) throws { } // ALREADY-THROWS: func alreadyThrows(completion: @escaping (String) -> Void) async throws { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=AUTO-CLOSURE %s +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=AUTO-CLOSURE %s func noParamAutoclosure(completion: @escaping @autoclosure () -> Void) { } // AUTO-CLOSURE: func noParamAutoclosure(completion: @escaping @autoclosure () -> Void) async { } @@ -640,13 +640,17 @@ func testSkipAssign() { // SKIP-ASSIGN-FUNC-NEXT: print("assigned"){{$}} // SKIP-ASSIGN-FUNC-NEXT: }{{$}} +// Same as noParamAutoclosure defined above, but used just for the test below. +// This avoids a compiler error when converting noParamAutoclosure to async. +func noParamAutoclosure2(completion: @escaping @autoclosure () -> Void) {} + // RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefixes=SKIP-AUTOCLOSURE-FUNC %s func testSkipAutoclosure() { // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 - noParamAutoclosure(completion: print("autoclosure")) + noParamAutoclosure2(completion: print("autoclosure")) } // SKIP-AUTOCLOSURE-FUNC: {{^}}func testSkipAutoclosure() async { -// SKIP-AUTOCLOSURE-FUNC: noParamAutoclosure(completion: print("autoclosure")){{$}} +// SKIP-AUTOCLOSURE-FUNC: noParamAutoclosure2(completion: print("autoclosure")){{$}} // RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=EMPTY-CAPTURE %s func testEmptyCapture() { diff --git a/test/refactoring/ConvertAsync/convert_bool.swift b/test/refactoring/ConvertAsync/convert_bool.swift index eb4363a633e..bc32654b47b 100644 --- a/test/refactoring/ConvertAsync/convert_bool.swift +++ b/test/refactoring/ConvertAsync/convert_bool.swift @@ -7,443 +7,458 @@ import Foundation import ConvertBoolObjC +func boolWithErr() async throws -> Bool { true } func boolWithErr(completion: @escaping (Bool, Error?) -> Void) {} + +func multipleBoolWithErr() async throws -> (String, Bool, Bool) { ("", true, true) } func multipleBoolWithErr(completion: @escaping (String?, Bool, Bool, Error?) -> Void) {} + +func optionalBoolWithErr() async throws -> (String, Bool, Bool) { ("", true, true) } func optionalBoolWithErr(completion: @escaping (String?, Bool?, Bool, Error?) -> Void) {} -// All 7 of the below should generate the same refactoring. +func testConvertBool() async throws { + // All 7 of the below should generate the same refactoring. -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s -boolWithErr { b, err in - if !b { - fatalError("oh no \(err!)") - } - print("not err") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s -boolWithErr { b, err in - if b { - fatalError("oh no \(err!)") - } - print("not err") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s -boolWithErr { b, err in - if !b && err != nil { - fatalError("oh no \(err!)") - } - print("not err") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s -boolWithErr { b, err in - if b && err != nil { - fatalError("oh no \(err!)") - } - print("not err") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s -boolWithErr { b, err in - if err != nil && b == false { - fatalError("oh no \(err!)") - } - print("not err") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s -boolWithErr { b, err in - if b == true && err == nil { - } else { - fatalError("oh no \(err!)") - } - print("not err") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s -boolWithErr { b, err in - if !b && err == nil { - } else { - fatalError("oh no \(err!)") - } - print("not err") -} - -// BOOL-WITH-ERR: do { -// BOOL-WITH-ERR-NEXT: let b = try await boolWithErr() -// BOOL-WITH-ERR-NEXT: print("not err") -// BOOL-WITH-ERR-NEXT: } catch let err { -// BOOL-WITH-ERR-NEXT: fatalError("oh no \(err)") -// BOOL-WITH-ERR-NEXT: } - -// These 3 should both generate the same refactoring. - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s -boolWithErr { success, err in - if success == true && err == nil { - print("hi") - } else { - fatalError("oh no \(err!)") - } - print("not err") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s -boolWithErr { success, err in - if success && err == nil { - print("hi") - } else { - fatalError("oh no \(err!)") - } - print("not err") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s -boolWithErr { success, err in - if err == nil { - print("hi") - } else if !success { - fatalError("oh no \(err!)") - } - print("not err") -} - -// BOOL-WITH-ERR2: do { -// BOOL-WITH-ERR2-NEXT: let success = try await boolWithErr() -// BOOL-WITH-ERR2-NEXT: print("hi") -// BOOL-WITH-ERR2-NEXT: print("not err") -// BOOL-WITH-ERR2-NEXT: } catch let err { -// BOOL-WITH-ERR2-NEXT: fatalError("oh no \(err)") -// BOOL-WITH-ERR2-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR3 %s -boolWithErr { failure, err in - if failure { - print("a \(err!)") - } else if .random() { - print("b") - } else { - print("c") - } -} - -// BOOL-WITH-ERR3: do { -// BOOL-WITH-ERR3-NEXT: let failure = try await boolWithErr() -// BOOL-WITH-ERR3-NEXT: if .random() { -// BOOL-WITH-ERR3-NEXT: print("b") -// BOOL-WITH-ERR3-NEXT: } else { -// BOOL-WITH-ERR3-NEXT: print("c") -// BOOL-WITH-ERR3-NEXT: } -// BOOL-WITH-ERR3-NEXT: } catch let err { -// BOOL-WITH-ERR3-NEXT: print("a \(err)") -// BOOL-WITH-ERR3-NEXT: } - -// Don't handle the below example as the force unwrap of err takes place under a different condition. -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE %s -boolWithErr { success, err in - if !success { - if err != nil { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if !b { fatalError("oh no \(err!)") } + print("not err") } - if !success { - _ = err != nil ? fatalError("oh no \(err!)") : fatalError("some worries") - } - print("not err") -} -// BOOL-DONT-HANDLE: let success = try await boolWithErr() -// BOOL-DONT-HANDLE-NEXT: if !success { -// BOOL-DONT-HANDLE-NEXT: if <#err#> != nil { -// BOOL-DONT-HANDLE-NEXT: fatalError("oh no \(<#err#>!)") -// BOOL-DONT-HANDLE-NEXT: } -// BOOL-DONT-HANDLE-NEXT: } -// BOOL-DONT-HANDLE-NEXT: if !success { -// BOOL-DONT-HANDLE-NEXT: _ = <#err#> != nil ? fatalError("oh no \(<#err#>!)") : fatalError("some worries") -// BOOL-DONT-HANDLE-NEXT: } -// BOOL-DONT-HANDLE-NEXT: print("not err") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE2 %s -boolWithErr { success, err in - if !success { - func doThings() { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if b { fatalError("oh no \(err!)") } - doThings() + print("not err") } - if !success { - let doThings = { + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if !b && err != nil { fatalError("oh no \(err!)") } - doThings() + print("not err") } - if !success { - while err != nil { + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if b && err != nil { fatalError("oh no \(err!)") } + print("not err") } - if !success { - for _: Int in [] { + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if err != nil && b == false { fatalError("oh no \(err!)") } + print("not err") } - print("not err") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if b == true && err == nil { + } else { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if !b && err == nil { + } else { + fatalError("oh no \(err!)") + } + print("not err") + } + + // BOOL-WITH-ERR: do { + // BOOL-WITH-ERR-NEXT: let b = try await boolWithErr() + // BOOL-WITH-ERR-NEXT: print("not err") + // BOOL-WITH-ERR-NEXT: } catch let err { + // BOOL-WITH-ERR-NEXT: fatalError("oh no \(err)") + // BOOL-WITH-ERR-NEXT: } + + // These 3 should both generate the same refactoring. + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s + boolWithErr { success, err in + if success == true && err == nil { + print("hi") + } else { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s + boolWithErr { success, err in + if success && err == nil { + print("hi") + } else { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s + boolWithErr { success, err in + if err == nil { + print("hi") + } else if !success { + fatalError("oh no \(err!)") + } + print("not err") + } + + // BOOL-WITH-ERR2: do { + // BOOL-WITH-ERR2-NEXT: let success = try await boolWithErr() + // BOOL-WITH-ERR2-NEXT: print("hi") + // BOOL-WITH-ERR2-NEXT: print("not err") + // BOOL-WITH-ERR2-NEXT: } catch let err { + // BOOL-WITH-ERR2-NEXT: fatalError("oh no \(err)") + // BOOL-WITH-ERR2-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR3 %s + boolWithErr { failure, err in + if failure { + print("a \(err!)") + } else if .random() { + print("b") + } else { + print("c") + } + } + + // BOOL-WITH-ERR3: do { + // BOOL-WITH-ERR3-NEXT: let failure = try await boolWithErr() + // BOOL-WITH-ERR3-NEXT: if .random() { + // BOOL-WITH-ERR3-NEXT: print("b") + // BOOL-WITH-ERR3-NEXT: } else { + // BOOL-WITH-ERR3-NEXT: print("c") + // BOOL-WITH-ERR3-NEXT: } + // BOOL-WITH-ERR3-NEXT: } catch let err { + // BOOL-WITH-ERR3-NEXT: print("a \(err)") + // BOOL-WITH-ERR3-NEXT: } + + // Don't handle the below example as the force unwrap of err takes place under a different condition. + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE %s + boolWithErr { success, err in + if !success { + if err != nil { + fatalError("oh no \(err!)") + } + } + if !success { + _ = err != nil ? fatalError("oh no \(err!)") : fatalError("some worries") + } + print("not err") + } + + // BOOL-DONT-HANDLE: let success = try await boolWithErr() + // BOOL-DONT-HANDLE-NEXT: if !success { + // BOOL-DONT-HANDLE-NEXT: if <#err#> != nil { + // BOOL-DONT-HANDLE-NEXT: fatalError("oh no \(<#err#>!)") + // BOOL-DONT-HANDLE-NEXT: } + // BOOL-DONT-HANDLE-NEXT: } + // BOOL-DONT-HANDLE-NEXT: if !success { + // BOOL-DONT-HANDLE-NEXT: _ = <#err#> != nil ? fatalError("oh no \(<#err#>!)") : fatalError("some worries") + // BOOL-DONT-HANDLE-NEXT: } + // BOOL-DONT-HANDLE-NEXT: print("not err") + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE2 %s + boolWithErr { success, err in + if !success { + func doThings() { + fatalError("oh no \(err!)") + } + doThings() + } + if !success { + let doThings = { + fatalError("oh no \(err!)") + } + doThings() + } + if !success { + while err != nil { + fatalError("oh no \(err!)") + } + } + if !success { + for _: Int in [] { + fatalError("oh no \(err!)") + } + } + print("not err") + } + + // FIXME: The 'err' in doThings() should become a placeholder (rdar://78509286). + // BOOL-DONT-HANDLE2: let success = try await boolWithErr() + // BOOL-DONT-HANDLE2-NEXT: if !success { + // BOOL-DONT-HANDLE2-NEXT: func doThings() { + // BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(err!)") + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: doThings() + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: if !success { + // BOOL-DONT-HANDLE2-NEXT: let doThings = { + // BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: doThings() + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: if !success { + // BOOL-DONT-HANDLE2-NEXT: while <#err#> != nil { + // BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: if !success { + // BOOL-DONT-HANDLE2-NEXT: for _: Int in [] { + // BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: print("not err") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE3 %s + boolWithErr { success, err in + if !success { + fatalError("oh no maybe \(String(describing: err))") + } + print("not err") + } + + // err is not force unwrapped, so don't handle. + + // BOOL-DONT-HANDLE3: let success = try await boolWithErr() + // BOOL-DONT-HANDLE3-NEXT: if !success { + // BOOL-DONT-HANDLE3-NEXT: fatalError("oh no maybe \(String(describing: <#err#>))") + // BOOL-DONT-HANDLE3-NEXT: } + // BOOL-DONT-HANDLE3-NEXT: print("not err") + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE4 %s + boolWithErr { failure, err in + if failure { + print("a") + } else if .random() { + print("b \(err!)") + } else { + print("c") + } + } + + // Don't handle the case where the err unwrap occurs in an unrelated else if + // clause. + + // BOOL-DONT-HANDLE4: let failure = try await boolWithErr() + // BOOL-DONT-HANDLE4-NEXT: if failure { + // BOOL-DONT-HANDLE4-NEXT: print("a") + // BOOL-DONT-HANDLE4-NEXT: } else if .random() { + // BOOL-DONT-HANDLE4-NEXT: print("b \(<#err#>!)") + // BOOL-DONT-HANDLE4-NEXT: } else { + // BOOL-DONT-HANDLE4-NEXT: print("c") + // BOOL-DONT-HANDLE4-NEXT: } + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR-SILLY %s + boolWithErr { success, err in + if success == false && err == nil { + print("ummm wat \(err!)") + return + } + print("not err") + } + + // BOOL-WITH-ERR-SILLY: let success = try await boolWithErr() + // BOOL-WITH-ERR-SILLY-NEXT: if success == false && <#err#> == nil { + // BOOL-WITH-ERR-SILLY-NEXT: print("ummm wat \(<#err#>!)") + // BOOL-WITH-ERR-SILLY-NEXT: <#return#> + // BOOL-WITH-ERR-SILLY-NEXT: } + // BOOL-WITH-ERR-SILLY-NEXT: print("not err") + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR-SILLY2 %s + boolWithErr { success, err in + if success { + print("ummm wat \(err!)") + } else { + print("ummm wat \(err!)") + } + } + + // The err unwrap is in both blocks, so it's not clear what to classify as. + + // BOOL-WITH-ERR-SILLY2: let success = try await boolWithErr() + // BOOL-WITH-ERR-SILLY2-NEXT: if success { + // BOOL-WITH-ERR-SILLY2-NEXT: print("ummm wat \(<#err#>!)") + // BOOL-WITH-ERR-SILLY2-NEXT: } else { + // BOOL-WITH-ERR-SILLY2-NEXT: print("ummm wat \(<#err#>!)") + // BOOL-WITH-ERR-SILLY2-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=MULTI-BOOL-WITH-ERR %s + multipleBoolWithErr { str, b1, b2, err in + if !b1 && !b2 { + print("a \(err!)") + } + if b1, b2 { + print("b \(err!)") + } + if !b1 { + print("c \(err!)") + } + if !b2 { + print("d \(err!)") + } + } + + // Don't handle the case where multiple flag checks are done in a single + // condition, because it's not exactly clear what the user is doing. It's a + // little unfortunate that we'll allow multiple flag checks in seperate + // conditions, but both of these cases seem somewhat uncommon, and there's no + // real way to completely enforce a single flag param across e.g multiple calls + // to the same function, so this is probably okay for now. + + // MULTI-BOOL-WITH-ERR: do { + // MULTI-BOOL-WITH-ERR-NEXT: let (str, b1, b2) = try await multipleBoolWithErr() + // MULTI-BOOL-WITH-ERR-NEXT: } catch let err { + // MULTI-BOOL-WITH-ERR-NEXT: if !<#b1#> && !<#b2#> { + // MULTI-BOOL-WITH-ERR-NEXT: print("a \(err)") + // MULTI-BOOL-WITH-ERR-NEXT: } + // MULTI-BOOL-WITH-ERR-NEXT: if <#b1#>, <#b2#> { + // MULTI-BOOL-WITH-ERR-NEXT: print("b \(err)") + // MULTI-BOOL-WITH-ERR-NEXT: } + // MULTI-BOOL-WITH-ERR-NEXT: print("c \(err)") + // MULTI-BOOL-WITH-ERR-NEXT: print("d \(err)") + // MULTI-BOOL-WITH-ERR-NEXT: } + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OPT-BOOL-WITH-ERR %s + optionalBoolWithErr { str, optBool, b, err in + if optBool != nil { + print("a \(err!)") + } + if optBool == nil { + print("b \(err!)") + } + if optBool == true { + print("c \(err!)") + } + if ((optBool) == (false)) { + print("d \(err!)") + } + if optBool == false { + print("e \(String(describing: err))") + } + if optBool != true { + print("f \(err!)") + } + if b { + print("g \(err!)") + } + } + + // It's a little unfortunate that print("a \(<#err#>!)") gets classified in the success + // block below, but it doesn't seem like a case that's likely to come up, as optBool + // would then be inaccessible in the error block. + + // OPT-BOOL-WITH-ERR: do { + // OPT-BOOL-WITH-ERR-NEXT: let (str, optBool, b) = try await optionalBoolWithErr() + // OPT-BOOL-WITH-ERR-NEXT: print("a \(<#err#>!)") + // OPT-BOOL-WITH-ERR-NEXT: if <#optBool#> == false { + // OPT-BOOL-WITH-ERR-NEXT: print("e \(String(describing: <#err#>))") + // OPT-BOOL-WITH-ERR-NEXT: } + // OPT-BOOL-WITH-ERR-NEXT: if <#optBool#> != true { + // OPT-BOOL-WITH-ERR-NEXT: print("f \(<#err#>!)") + // OPT-BOOL-WITH-ERR-NEXT: } + // OPT-BOOL-WITH-ERR-NEXT: } catch let err { + // OPT-BOOL-WITH-ERR-NEXT: print("b \(err)") + // OPT-BOOL-WITH-ERR-NEXT: print("c \(err)") + // OPT-BOOL-WITH-ERR-NEXT: print("d \(err)") + // OPT-BOOL-WITH-ERR-NEXT: print("g \(err)") + // OPT-BOOL-WITH-ERR-NEXT: } + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OBJC-BOOL-WITH-ERR %s + ClassWithHandlerMethods.firstBoolFlagSuccess("") { str, success, unrelated, err in + if !unrelated { + print(err!) + } + if !success { + print("oh no") + } + if !success { + print(err!) + } + if success { + print("woo") + } + if str != nil { + print("also woo") + } + } + + // OBJC-BOOL-WITH-ERR: do { + // OBJC-BOOL-WITH-ERR-NEXT: let (str, success, unrelated) = try await ClassWithHandlerMethods.firstBoolFlagSuccess("") + // OBJC-BOOL-WITH-ERR-NEXT: if !unrelated { + // OBJC-BOOL-WITH-ERR-NEXT: print(<#err#>!) + // OBJC-BOOL-WITH-ERR-NEXT: } + // OBJC-BOOL-WITH-ERR-NEXT: print("woo") + // OBJC-BOOL-WITH-ERR-NEXT: print("also woo") + // OBJC-BOOL-WITH-ERR-NEXT: } catch let err { + // OBJC-BOOL-WITH-ERR-NEXT: print("oh no") + // OBJC-BOOL-WITH-ERR-NEXT: print(err) + // OBJC-BOOL-WITH-ERR-NEXT: } + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OBJC-BOOL-WITH-ERR2 %s + ClassWithHandlerMethods.secondBoolFlagFailure("") { str, unrelated, failure, err in + if unrelated { + print(err!) + } + if failure { + print("oh no") + } + if failure { + print(err!) + } + if !failure { + print("woo") + } + if str != nil { + print("also woo") + } + if failure && err == nil { + print("wat") + } + if failure && err != nil { + print("neat") + } + if failure, let _ = err { + print("neato") + } + } + + // OBJC-BOOL-WITH-ERR2: do { + // OBJC-BOOL-WITH-ERR2-NEXT: let (str, unrelated, failure) = try await ClassWithHandlerMethods.secondBoolFlagFailure("") + // OBJC-BOOL-WITH-ERR2-NEXT: if unrelated { + // OBJC-BOOL-WITH-ERR2-NEXT: print(<#err#>!) + // OBJC-BOOL-WITH-ERR2-NEXT: } + // OBJC-BOOL-WITH-ERR2-NEXT: print("woo") + // OBJC-BOOL-WITH-ERR2-NEXT: print("also woo") + // OBJC-BOOL-WITH-ERR2-NEXT: if failure && <#err#> == nil { + // OBJC-BOOL-WITH-ERR2-NEXT: print("wat") + // OBJC-BOOL-WITH-ERR2-NEXT: } + // OBJC-BOOL-WITH-ERR2-NEXT: } catch let err { + // OBJC-BOOL-WITH-ERR2-NEXT: print("oh no") + // OBJC-BOOL-WITH-ERR2-NEXT: print(err) + // OBJC-BOOL-WITH-ERR2-NEXT: print("neat") + // OBJC-BOOL-WITH-ERR2-NEXT: print("neato") + // OBJC-BOOL-WITH-ERR2-NEXT: } } - -// FIXME: The 'err' in doThings() should become a placeholder (rdar://78509286). -// BOOL-DONT-HANDLE2: let success = try await boolWithErr() -// BOOL-DONT-HANDLE2-NEXT: if !success { -// BOOL-DONT-HANDLE2-NEXT: func doThings() { -// BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(err!)") -// BOOL-DONT-HANDLE2-NEXT: } -// BOOL-DONT-HANDLE2-NEXT: doThings() -// BOOL-DONT-HANDLE2-NEXT: } -// BOOL-DONT-HANDLE2-NEXT: if !success { -// BOOL-DONT-HANDLE2-NEXT: let doThings = { -// BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") -// BOOL-DONT-HANDLE2-NEXT: } -// BOOL-DONT-HANDLE2-NEXT: doThings() -// BOOL-DONT-HANDLE2-NEXT: } -// BOOL-DONT-HANDLE2-NEXT: if !success { -// BOOL-DONT-HANDLE2-NEXT: while <#err#> != nil { -// BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") -// BOOL-DONT-HANDLE2-NEXT: } -// BOOL-DONT-HANDLE2-NEXT: } -// BOOL-DONT-HANDLE2-NEXT: if !success { -// BOOL-DONT-HANDLE2-NEXT: for _: Int in [] { -// BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") -// BOOL-DONT-HANDLE2-NEXT: } -// BOOL-DONT-HANDLE2-NEXT: } -// BOOL-DONT-HANDLE2-NEXT: print("not err") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE3 %s -boolWithErr { success, err in - if !success { - fatalError("oh no maybe \(String(describing: err))") - } - print("not err") -} - -// err is not force unwrapped, so don't handle. - -// BOOL-DONT-HANDLE3: let success = try await boolWithErr() -// BOOL-DONT-HANDLE3-NEXT: if !success { -// BOOL-DONT-HANDLE3-NEXT: fatalError("oh no maybe \(String(describing: <#err#>))") -// BOOL-DONT-HANDLE3-NEXT: } -// BOOL-DONT-HANDLE3-NEXT: print("not err") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE4 %s -boolWithErr { failure, err in - if failure { - print("a") - } else if .random() { - print("b \(err!)") - } else { - print("c") - } -} - -// Don't handle the case where the err unwrap occurs in an unrelated else if -// clause. - -// BOOL-DONT-HANDLE4: let failure = try await boolWithErr() -// BOOL-DONT-HANDLE4-NEXT: if failure { -// BOOL-DONT-HANDLE4-NEXT: print("a") -// BOOL-DONT-HANDLE4-NEXT: } else if .random() { -// BOOL-DONT-HANDLE4-NEXT: print("b \(<#err#>!)") -// BOOL-DONT-HANDLE4-NEXT: } else { -// BOOL-DONT-HANDLE4-NEXT: print("c") -// BOOL-DONT-HANDLE4-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR-SILLY %s -boolWithErr { success, err in - if success == false && err == nil { - print("ummm wat \(err!)") - return - } - print("not err") -} - -// BOOL-WITH-ERR-SILLY: let success = try await boolWithErr() -// BOOL-WITH-ERR-SILLY-NEXT: if success == false && <#err#> == nil { -// BOOL-WITH-ERR-SILLY-NEXT: print("ummm wat \(<#err#>!)") -// BOOL-WITH-ERR-SILLY-NEXT: <#return#> -// BOOL-WITH-ERR-SILLY-NEXT: } -// BOOL-WITH-ERR-SILLY-NEXT: print("not err") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR-SILLY2 %s -boolWithErr { success, err in - if success { - print("ummm wat \(err!)") - } else { - print("ummm wat \(err!)") - } -} - -// The err unwrap is in both blocks, so it's not clear what to classify as. - -// BOOL-WITH-ERR-SILLY2: let success = try await boolWithErr() -// BOOL-WITH-ERR-SILLY2-NEXT: if success { -// BOOL-WITH-ERR-SILLY2-NEXT: print("ummm wat \(<#err#>!)") -// BOOL-WITH-ERR-SILLY2-NEXT: } else { -// BOOL-WITH-ERR-SILLY2-NEXT: print("ummm wat \(<#err#>!)") -// BOOL-WITH-ERR-SILLY2-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=MULTI-BOOL-WITH-ERR %s -multipleBoolWithErr { str, b1, b2, err in - if !b1 && !b2 { - print("a \(err!)") - } - if b1, b2 { - print("b \(err!)") - } - if !b1 { - print("c \(err!)") - } - if !b2 { - print("d \(err!)") - } -} - -// Don't handle the case where multiple flag checks are done in a single -// condition, because it's not exactly clear what the user is doing. It's a -// little unfortunate that we'll allow multiple flag checks in seperate -// conditions, but both of these cases seem somewhat uncommon, and there's no -// real way to completely enforce a single flag param across e.g multiple calls -// to the same function, so this is probably okay for now. - -// MULTI-BOOL-WITH-ERR: do { -// MULTI-BOOL-WITH-ERR-NEXT: let (str, b1, b2) = try await multipleBoolWithErr() -// MULTI-BOOL-WITH-ERR-NEXT: } catch let err { -// MULTI-BOOL-WITH-ERR-NEXT: if !<#b1#> && !<#b2#> { -// MULTI-BOOL-WITH-ERR-NEXT: print("a \(err)") -// MULTI-BOOL-WITH-ERR-NEXT: } -// MULTI-BOOL-WITH-ERR-NEXT: if <#b1#>, <#b2#> { -// MULTI-BOOL-WITH-ERR-NEXT: print("b \(err)") -// MULTI-BOOL-WITH-ERR-NEXT: } -// MULTI-BOOL-WITH-ERR-NEXT: print("c \(err)") -// MULTI-BOOL-WITH-ERR-NEXT: print("d \(err)") -// MULTI-BOOL-WITH-ERR-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OPT-BOOL-WITH-ERR %s -optionalBoolWithErr { str, optBool, b, err in - if optBool != nil { - print("a \(err!)") - } - if optBool == nil { - print("b \(err!)") - } - if optBool == true { - print("c \(err!)") - } - if ((optBool) == (false)) { - print("d \(err!)") - } - if optBool == false { - print("e \(String(describing: err))") - } - if optBool != true { - print("f \(err!)") - } - if b { - print("g \(err!)") - } -} - -// It's a little unfortunate that print("a \(<#err#>!)") gets classified in the success -// block below, but it doesn't seem like a case that's likely to come up, as optBool -// would then be inaccessible in the error block. - -// OPT-BOOL-WITH-ERR: do { -// OPT-BOOL-WITH-ERR-NEXT: let (str, optBool, b) = try await optionalBoolWithErr() -// OPT-BOOL-WITH-ERR-NEXT: print("a \(<#err#>!)") -// OPT-BOOL-WITH-ERR-NEXT: if <#optBool#> == false { -// OPT-BOOL-WITH-ERR-NEXT: print("e \(String(describing: <#err#>))") -// OPT-BOOL-WITH-ERR-NEXT: } -// OPT-BOOL-WITH-ERR-NEXT: if <#optBool#> != true { -// OPT-BOOL-WITH-ERR-NEXT: print("f \(<#err#>!)") -// OPT-BOOL-WITH-ERR-NEXT: } -// OPT-BOOL-WITH-ERR-NEXT: } catch let err { -// OPT-BOOL-WITH-ERR-NEXT: print("b \(err)") -// OPT-BOOL-WITH-ERR-NEXT: print("c \(err)") -// OPT-BOOL-WITH-ERR-NEXT: print("d \(err)") -// OPT-BOOL-WITH-ERR-NEXT: print("g \(err)") -// OPT-BOOL-WITH-ERR-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OBJC-BOOL-WITH-ERR %s -ClassWithHandlerMethods.firstBoolFlagSuccess("") { str, success, unrelated, err in - if !unrelated { - print(err!) - } - if !success { - print("oh no") - } - if !success { - print(err!) - } - if success { - print("woo") - } - if str != nil { - print("also woo") - } -} - -// OBJC-BOOL-WITH-ERR: do { -// OBJC-BOOL-WITH-ERR-NEXT: let (str, success, unrelated) = try await ClassWithHandlerMethods.firstBoolFlagSuccess("") -// OBJC-BOOL-WITH-ERR-NEXT: if !unrelated { -// OBJC-BOOL-WITH-ERR-NEXT: print(<#err#>!) -// OBJC-BOOL-WITH-ERR-NEXT: } -// OBJC-BOOL-WITH-ERR-NEXT: print("woo") -// OBJC-BOOL-WITH-ERR-NEXT: print("also woo") -// OBJC-BOOL-WITH-ERR-NEXT: } catch let err { -// OBJC-BOOL-WITH-ERR-NEXT: print("oh no") -// OBJC-BOOL-WITH-ERR-NEXT: print(err) -// OBJC-BOOL-WITH-ERR-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OBJC-BOOL-WITH-ERR2 %s -ClassWithHandlerMethods.secondBoolFlagFailure("") { str, unrelated, failure, err in - if unrelated { - print(err!) - } - if failure { - print("oh no") - } - if failure { - print(err!) - } - if !failure { - print("woo") - } - if str != nil { - print("also woo") - } - if failure && err == nil { - print("wat") - } - if failure && err != nil { - print("neat") - } - if failure, let _ = err { - print("neato") - } -} - -// OBJC-BOOL-WITH-ERR2: do { -// OBJC-BOOL-WITH-ERR2-NEXT: let (str, unrelated, failure) = try await ClassWithHandlerMethods.secondBoolFlagFailure("") -// OBJC-BOOL-WITH-ERR2-NEXT: if unrelated { -// OBJC-BOOL-WITH-ERR2-NEXT: print(<#err#>!) -// OBJC-BOOL-WITH-ERR2-NEXT: } -// OBJC-BOOL-WITH-ERR2-NEXT: print("woo") -// OBJC-BOOL-WITH-ERR2-NEXT: print("also woo") -// OBJC-BOOL-WITH-ERR2-NEXT: if failure && <#err#> == nil { -// OBJC-BOOL-WITH-ERR2-NEXT: print("wat") -// OBJC-BOOL-WITH-ERR2-NEXT: } -// OBJC-BOOL-WITH-ERR2-NEXT: } catch let err { -// OBJC-BOOL-WITH-ERR2-NEXT: print("oh no") -// OBJC-BOOL-WITH-ERR2-NEXT: print(err) -// OBJC-BOOL-WITH-ERR2-NEXT: print("neat") -// OBJC-BOOL-WITH-ERR2-NEXT: print("neato") -// OBJC-BOOL-WITH-ERR2-NEXT: } diff --git a/test/refactoring/ConvertAsync/convert_function.swift b/test/refactoring/ConvertAsync/convert_function.swift index be204e14cce..9b8a2a75b03 100644 --- a/test/refactoring/ConvertAsync/convert_function.swift +++ b/test/refactoring/ConvertAsync/convert_function.swift @@ -269,7 +269,7 @@ func voidResultCompletion(completion: @escaping (Result) -> Void) { // VOID-RESULT-HANDLER-NEXT: } // rdar://77789360 Make sure we don't print a double return statement. -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING %s +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING %s func testReturnHandling(_ completion: @escaping (String?, Error?) -> Void) { return completion("", nil) } @@ -279,6 +279,8 @@ func testReturnHandling(_ completion: @escaping (String?, Error?) -> Void) { // rdar://77789360 Make sure we don't print a double return statement and don't // completely drop completion(a). +// Note we cannot use refactor-check-compiles here, as the placeholders mean we +// don't form valid AST. // RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING2 %s func testReturnHandling2(completion: @escaping (String) -> ()) { simpleErr(arg: "") { x, err in diff --git a/test/refactoring/ConvertAsync/convert_params_multi.swift b/test/refactoring/ConvertAsync/convert_params_multi.swift index aa55cae1797..40606ffa897 100644 --- a/test/refactoring/ConvertAsync/convert_params_multi.swift +++ b/test/refactoring/ConvertAsync/convert_params_multi.swift @@ -1,215 +1,226 @@ +// RUN: %empty-directory(%t) + // REQUIRES: concurrency +func manyWithError() async throws -> (String, Int) { ("", 0) } func manyWithError(_ completion: @escaping (String?, Int?, Error?) -> Void) { } + +func mixed() async -> (String, Int) { ("", 0) } func mixed(_ completion: @escaping (String?, Int) -> Void) { } + +func mixedError() async throws -> (String, Int) { ("", 0) } func mixedError(_ completion: @escaping (String?, Int, Error?) -> Void) { } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s -manyWithError { res1, res2, err in - print("before") - if let bad = err { - print("got error \(bad)") - return +func testParamsMulti() async throws { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s + manyWithError { res1, res2, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + if let str = res1, let i = res2 { + print("got result \(str)") + print("got result \(i)") + } + print("after") } - if let str = res1, let i = res2 { + // MANYBOUND: do { + // MANYBOUND-NEXT: let (str, i) = try await manyWithError() + // MANYBOUND-NEXT: print("before") + // MANYBOUND-NEXT: print("got result \(str)") + // MANYBOUND-NEXT: print("got result \(i)") + // MANYBOUND-NEXT: print("after") + // MANYBOUND-NEXT: } catch let bad { + // MANYBOUND-NEXT: print("got error \(bad)") + // MANYBOUND-NEXT: } + + // FIXME: This case is a little tricky: Being in the else block of 'if let str = res1' + // should allow us to place 'if let i = res2' in the failure block. However, this + // is a success condition, so we still place it in the success block. Really what + // we need to do here is check to see if manyWithError has an existing async + // alternative that still returns optional success values, and allow success + // classification in that case. Otherwise, we'd probably be better off leaving + // the condition unhandled, as it's not clear what the user is doing. + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s + manyWithError { res1, res2, err in + print("before") + if let str = res1 { + print("got result \(str)") + } else if let i = res2 { + print("got result \(i)") + } else { + print("got error \(err!)") + } + print("after") + } + // MANYUNBOUND-ERR: do { + // MANYUNBOUND-ERR-NEXT: let (str, i) = try await manyWithError() + // MANYUNBOUND-ERR-NEXT: print("before") + // MANYUNBOUND-ERR-NEXT: print("got result \(str)") + // MANYUNBOUND-ERR-NEXT: print("got result \(i)") + // MANYUNBOUND-ERR-NEXT: print("after") + // MANYUNBOUND-ERR-NEXT: } catch let err { + // MANYUNBOUND-ERR-NEXT: print("got error \(err)") + // MANYUNBOUND-ERR-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s + manyWithError { res1, res2, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + if let str = res1 { + print("got result \(str)") + } + if let i = res2 { + print("got result \(i)") + } + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-COND %s + manyWithError { res1, res2, err in + print("before") + if res1 != nil && res2 == nil { + print("got result \(res1!)") + } + print("after") + } + // MIXED-COND: convert_params_multi.swift + // MIXED-COND-NEXT: let (res1, res2) = try await manyWithError() + // MIXED-COND-NEXT: print("before") + // MIXED-COND-NEXT: if <#res1#> != nil && <#res2#> == nil { + // MIXED-COND-NEXT: print("got result \(res1)") + // MIXED-COND-NEXT: } + // MIXED-COND-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-CONDELSE %s + manyWithError { res1, res2, err in + print("before") + if res1 != nil && res2 == nil { + print("got result \(res1!)") + } else { + print("bad") + } + print("after") + } + // MIXED-CONDELSE: var res1: String? = nil + // MIXED-CONDELSE-NEXT: var res2: Int? = nil + // MIXED-CONDELSE-NEXT: var err: Error? = nil + // MIXED-CONDELSE-NEXT: do { + // MIXED-CONDELSE-NEXT: (res1, res2) = try await manyWithError() + // MIXED-CONDELSE-NEXT: } catch { + // MIXED-CONDELSE-NEXT: err = error + // MIXED-CONDELSE-NEXT: } + // MIXED-CONDELSE-EMPTY: + // MIXED-CONDELSE-NEXT: print("before") + // MIXED-CONDELSE-NEXT: if res1 != nil && res2 == nil { + // MIXED-CONDELSE-NEXT: print("got result \(res1!)") + // MIXED-CONDELSE-NEXT: } else { + // MIXED-CONDELSE-NEXT: print("bad") + // MIXED-CONDELSE-NEXT: } + // MIXED-CONDELSE-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s + manyWithError { res1, res2, err in + print("before") + guard let str = res1, let i = res2 else { + print("got error \(err!)") + return + } print("got result \(str)") print("got result \(i)") + print("after") } - print("after") -} -// MANYBOUND: do { -// MANYBOUND-NEXT: let (str, i) = try await manyWithError() -// MANYBOUND-NEXT: print("before") -// MANYBOUND-NEXT: print("got result \(str)") -// MANYBOUND-NEXT: print("got result \(i)") -// MANYBOUND-NEXT: print("after") -// MANYBOUND-NEXT: } catch let bad { -// MANYBOUND-NEXT: print("got error \(bad)") -// MANYBOUND-NEXT: } -// FIXME: This case is a little tricky: Being in the else block of 'if let str = res1' -// should allow us to place 'if let i = res2' in the failure block. However, this -// is a success condition, so we still place it in the success block. Really what -// we need to do here is check to see if manyWithError has an existing async -// alternative that still returns optional success values, and allow success -// classification in that case. Otherwise, we'd probably be better off leaving -// the condition unhandled, as it's not clear what the user is doing. -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s -manyWithError { res1, res2, err in - print("before") - if let str = res1 { - print("got result \(str)") - } else if let i = res2 { - print("got result \(i)") - } else { - print("got error \(err!)") - } - print("after") -} -// MANYUNBOUND-ERR: do { -// MANYUNBOUND-ERR-NEXT: let (str, i) = try await manyWithError() -// MANYUNBOUND-ERR-NEXT: print("before") -// MANYUNBOUND-ERR-NEXT: print("got result \(str)") -// MANYUNBOUND-ERR-NEXT: print("got result \(i)") -// MANYUNBOUND-ERR-NEXT: print("after") -// MANYUNBOUND-ERR-NEXT: } catch let err { -// MANYUNBOUND-ERR-NEXT: print("got error \(err)") -// MANYUNBOUND-ERR-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s -manyWithError { res1, res2, err in - print("before") - if let bad = err { - print("got error \(bad)") - return - } - if let str = res1 { - print("got result \(str)") - } - if let i = res2 { - print("got result \(i)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-COND %s -manyWithError { res1, res2, err in - print("before") - if res1 != nil && res2 == nil { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s + manyWithError { res1, res2, err in + print("before") + guard res1 != nil && res2 != nil && err == nil else { + print("got error \(err!)") + return + } print("got result \(res1!)") + print("got result \(res2!)") + print("after") } - print("after") -} -// MIXED-COND: convert_params_multi.swift -// MIXED-COND-NEXT: let (res1, res2) = try await manyWithError() -// MIXED-COND-NEXT: print("before") -// MIXED-COND-NEXT: if <#res1#> != nil && <#res2#> == nil { -// MIXED-COND-NEXT: print("got result \(res1)") -// MIXED-COND-NEXT: } -// MIXED-COND-NEXT: print("after") + // MANYUNBOUND: do { + // MANYUNBOUND-NEXT: let (res1, res2) = try await manyWithError() + // MANYUNBOUND-NEXT: print("before") + // MANYUNBOUND-NEXT: print("got result \(res1)") + // MANYUNBOUND-NEXT: print("got result \(res2)") + // MANYUNBOUND-NEXT: print("after") + // MANYUNBOUND-NEXT: } catch let err { + // MANYUNBOUND-NEXT: print("got error \(err)") + // MANYUNBOUND-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-CONDELSE %s -manyWithError { res1, res2, err in - print("before") - if res1 != nil && res2 == nil { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s + manyWithError { res1, res2, err in + print("before") + guard res1 != nil else { + print("got error \(err!)") + return + } print("got result \(res1!)") - } else { - print("bad") + print("got result \(res2!)") + print("after") } - print("after") -} -// MIXED-CONDELSE: var res1: String? = nil -// MIXED-CONDELSE-NEXT: var res2: Int? = nil -// MIXED-CONDELSE-NEXT: var err: Error? = nil -// MIXED-CONDELSE-NEXT: do { -// MIXED-CONDELSE-NEXT: (res1, res2) = try await manyWithError() -// MIXED-CONDELSE-NEXT: } catch { -// MIXED-CONDELSE-NEXT: err = error -// MIXED-CONDELSE-NEXT: } -// MIXED-CONDELSE-EMPTY: -// MIXED-CONDELSE-NEXT: print("before") -// MIXED-CONDELSE-NEXT: if res1 != nil && res2 == nil { -// MIXED-CONDELSE-NEXT: print("got result \(res1!)") -// MIXED-CONDELSE-NEXT: } else { -// MIXED-CONDELSE-NEXT: print("bad") -// MIXED-CONDELSE-NEXT: } -// MIXED-CONDELSE-NEXT: print("after") -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s -manyWithError { res1, res2, err in - print("before") - guard let str = res1, let i = res2 else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s + manyWithError { res1, res2, err in + print("before") + guard err == nil else { + print("got error \(err!)") + return + } + print("got result \(res1!)") + print("got result \(res2!)") + print("after") } - print("got result \(str)") - print("got result \(i)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s -manyWithError { res1, res2, err in - print("before") - guard res1 != nil && res2 != nil && err == nil else { - print("got error \(err!)") - return + // Cannot use refactor-check-compiles, as cannot use non-optional 'str' in + // optional binding. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED %s + mixed { str, num in + print("before") + if let res = str { + print("got result \(res)") + } + print("\(num)") + print("after") } - print("got result \(res1!)") - print("got result \(res2!)") - print("after") -} -// MANYUNBOUND: do { -// MANYUNBOUND-NEXT: let (res1, res2) = try await manyWithError() -// MANYUNBOUND-NEXT: print("before") -// MANYUNBOUND-NEXT: print("got result \(res1)") -// MANYUNBOUND-NEXT: print("got result \(res2)") -// MANYUNBOUND-NEXT: print("after") -// MANYUNBOUND-NEXT: } catch let err { -// MANYUNBOUND-NEXT: print("got error \(err)") -// MANYUNBOUND-NEXT: } + // MIXED: convert_params_multi.swift + // MIXED-NEXT: let (str, num) = await mixed() + // MIXED-NEXT: print("before") + // MIXED-NEXT: if let res = str { + // MIXED-NEXT: print("got result \(res)") + // MIXED-NEXT: } + // MIXED-NEXT: print("\(num)") + // MIXED-NEXT: print("after") + // MIXED-NOT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s -manyWithError { res1, res2, err in - print("before") - guard res1 != nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-ERROR %s + mixedError { str, num, err in + print("before") + if let res = str { + print("got result \(res)") + } else { + print("got \(err!)") + } + print("\(num)") + print("after") } - print("got result \(res1!)") - print("got result \(res2!)") - print("after") + // MIXED-ERROR: convert_params_multi.swift + // MIXED-ERROR-NEXT: do { + // MIXED-ERROR-NEXT: let (res, num) = try await mixedError() + // MIXED-ERROR-NEXT: print("before") + // MIXED-ERROR-NEXT: print("got result \(res)") + // MIXED-ERROR-NEXT: print("\(num)") + // MIXED-ERROR-NEXT: print("after") + // MIXED-ERROR-NEXT: } catch let err { + // MIXED-ERROR-NEXT: print("got \(err)") + // MIXED-ERROR-NEXT: } + // MIXED-ERROR-NOT: } } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s -manyWithError { res1, res2, err in - print("before") - guard err == nil else { - print("got error \(err!)") - return - } - print("got result \(res1!)") - print("got result \(res2!)") - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED %s -mixed { str, num in - print("before") - if let res = str { - print("got result \(res)") - } - print("\(num)") - print("after") -} -// MIXED: convert_params_multi.swift -// MIXED-NEXT: let (str, num) = await mixed() -// MIXED-NEXT: print("before") -// MIXED-NEXT: if let res = str { -// MIXED-NEXT: print("got result \(res)") -// MIXED-NEXT: } -// MIXED-NEXT: print("\(num)") -// MIXED-NEXT: print("after") -// MIXED-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-ERROR %s -mixedError { str, num, err in - print("before") - if let res = str { - print("got result \(res)") - } else { - print("got \(err!)") - } - print("\(num)") - print("after") -} -// MIXED-ERROR: convert_params_multi.swift -// MIXED-ERROR-NEXT: do { -// MIXED-ERROR-NEXT: let (res, num) = try await mixedError() -// MIXED-ERROR-NEXT: print("before") -// MIXED-ERROR-NEXT: print("got result \(res)") -// MIXED-ERROR-NEXT: print("\(num)") -// MIXED-ERROR-NEXT: print("after") -// MIXED-ERROR-NEXT: } catch let err { -// MIXED-ERROR-NEXT: print("got \(err)") -// MIXED-ERROR-NEXT: } -// MIXED-ERROR-NOT: } diff --git a/test/refactoring/ConvertAsync/convert_params_single.swift b/test/refactoring/ConvertAsync/convert_params_single.swift index d68045562f7..b3f47b5ab36 100644 --- a/test/refactoring/ConvertAsync/convert_params_single.swift +++ b/test/refactoring/ConvertAsync/convert_params_single.swift @@ -1,524 +1,537 @@ +// RUN: %empty-directory(%t) + // REQUIRES: concurrency +func withError() async throws -> String { "" } func withError(_ completion: @escaping (String?, Error?) -> Void) { } + +func notOptional() async throws -> String { "" } func notOptional(_ completion: @escaping (String, Error?) -> Void) { } + +func errorOnly() async throws { } func errorOnly(_ completion: @escaping (Error?) -> Void) { } + func test(_ str: String) -> Bool { return false } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNRELATED %s -withError { res, err in - if test("unrelated") { - print("unrelated") - } else { - print("else unrelated") +func testParamsSingle() async throws { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNRELATED %s + withError { res, err in + if test("unrelated") { + print("unrelated") + } else { + print("else unrelated") + } } -} -// UNRELATED: convert_params_single.swift -// UNRELATED-NEXT: let res = try await withError() -// UNRELATED-NEXT: if test("unrelated") { -// UNRELATED-NEXT: print("unrelated") -// UNRELATED-NEXT: } else { -// UNRELATED-NEXT: print("else unrelated") -// UNRELATED-NEXT: } + // UNRELATED: convert_params_single.swift + // UNRELATED-NEXT: let res = try await withError() + // UNRELATED-NEXT: if test("unrelated") { + // UNRELATED-NEXT: print("unrelated") + // UNRELATED-NEXT: } else { + // UNRELATED-NEXT: print("else unrelated") + // UNRELATED-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - return - } - if let str = res { - print("got result \(str)") - } - print("after") -} -// BOUND: do { -// BOUND-NEXT: let str = try await withError() -// BOUND-NEXT: print("before") -// BOUND-NEXT: print("got result \(str)") -// BOUND-NEXT: print("after") -// BOUND-NEXT: } catch let bad { -// BOUND-NEXT: print("got error \(bad)") -// BOUND-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND-COMMENT %s -withError { res, err in // a - // b - print("before") - // c - if let bad = err { // d - // e - print("got error \(bad)") - // f - return - // g - } - // h - if let str = res { // i - // j - print("got result \(str)") - // k - } - // l - print("after") - // m -} -// BOUND-COMMENT: do { -// BOUND-COMMENT-NEXT: let str = try await withError() -// BOUND-COMMENT-NEXT: // a -// BOUND-COMMENT-NEXT: // b -// BOUND-COMMENT-NEXT: print("before") -// BOUND-COMMENT-NEXT: // c -// BOUND-COMMENT-NEXT: // h -// BOUND-COMMENT-NEXT: // i -// BOUND-COMMENT-NEXT: // j -// BOUND-COMMENT-NEXT: print("got result \(str)") -// BOUND-COMMENT-NEXT: // k -// BOUND-COMMENT-NEXT: // l -// BOUND-COMMENT-NEXT: print("after") -// BOUND-COMMENT-NEXT: // m -// BOUND-COMMENT-EMPTY: -// BOUND-COMMENT-NEXT: } catch let bad { -// BOUND-COMMENT-NEXT: // d -// BOUND-COMMENT-NEXT: // e -// BOUND-COMMENT-NEXT: print("got error \(bad)") -// BOUND-COMMENT-NEXT: // f -// BOUND-COMMENT-NEXT: // g -// BOUND-COMMENT-NEXT: {{ }} -// BOUND-COMMENT-NEXT: } - - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s -withError { res, err in - print("before") - guard let str = res else { - print("got error \(err!)") - return - } - print("got result \(str)") - print("after") -} -// UNBOUND-ERR: do { -// UNBOUND-ERR-NEXT: let str = try await withError() -// UNBOUND-ERR-NEXT: print("before") -// UNBOUND-ERR-NEXT: print("got result \(str)") -// UNBOUND-ERR-NEXT: print("after") -// UNBOUND-ERR-NEXT: } catch let err { -// UNBOUND-ERR-NEXT: print("got error \(err)") -// UNBOUND-ERR-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - } else if let str = res { - print("got result \(str)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - return - } - if let str = res { - print("got result \(str)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - return - } - print("got result \(res!)") - print("after") -} -// UNBOUND-RES: do { -// UNBOUND-RES-NEXT: let res = try await withError() -// UNBOUND-RES-NEXT: print("before") -// UNBOUND-RES-NEXT: print("got result \(res)") -// UNBOUND-RES-NEXT: print("after") -// UNBOUND-RES-NEXT: } catch let bad { -// UNBOUND-RES-NEXT: print("got error \(bad)") -// UNBOUND-RES-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s -withError { res, err in - print("before") - if let str = res { - print("got result \(str)") - print("after") - return - } - print("got error \(err!)") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - } else { - print("got result \(res!)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s -withError { res, err in - print("before") - if let str = res { - print("got result \(str)") - } else { - print("got error \(err!)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if err != nil { - print("got error \(err!)") - return - } - print("got result \(res!)") - print("after") -} -// UNBOUND: do { -// UNBOUND-NEXT: let res = try await withError() -// UNBOUND-NEXT: print("before") -// UNBOUND-NEXT: print("got result \(res)") -// UNBOUND-NEXT: print("after") -// UNBOUND-NEXT: } catch let err { -// UNBOUND-NEXT: print("got error \(err)") -// UNBOUND-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if res != nil { - print("got result \(res!)") - print("after") - return - } - print("got error \(err!)") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if err != nil { - print("got error \(err!)") - } else { - print("got result \(res!)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if res != nil { - print("got result \(res!)") - } else { - print("got error \(err!)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if err == nil { - print("got result \(res!)") - } else { - print("got error \(err!)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if res == nil { - print("got error \(err!)") - } else { - print("got result \(res!)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNHANDLEDNESTED %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - } else { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } if let str = res { print("got result \(str)") } + print("after") } - print("after") -} -// UNHANDLEDNESTED: do { -// UNHANDLEDNESTED-NEXT: let res = try await withError() -// UNHANDLEDNESTED-NEXT: print("before") -// UNHANDLEDNESTED-NEXT: if let str = <#res#> { -// UNHANDLEDNESTED-NEXT: print("got result \(str)") -// UNHANDLEDNESTED-NEXT: } -// UNHANDLEDNESTED-NEXT: print("after") -// UNHANDLEDNESTED-NEXT: } catch let bad { -// UNHANDLEDNESTED-NEXT: print("got error \(bad)") -// UNHANDLEDNESTED-NEXT: } + // BOUND: do { + // BOUND-NEXT: let str = try await withError() + // BOUND-NEXT: print("before") + // BOUND-NEXT: print("got result \(str)") + // BOUND-NEXT: print("after") + // BOUND-NEXT: } catch let bad { + // BOUND-NEXT: print("got error \(bad)") + // BOUND-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOERR %s -withError { res, err in - print("before") - if let str = res { - print("got result \(str)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND-COMMENT %s + withError { res, err in // a + // b + print("before") + // c + if let bad = err { // d + // e + print("got error \(bad)") + // f + return + // g + } + // h + if let str = res { // i + // j + print("got result \(str)") + // k + } + // l + print("after") + // m } - print("after") -} -// NOERR: convert_params_single.swift -// NOERR-NEXT: let str = try await withError() -// NOERR-NEXT: print("before") -// NOERR-NEXT: print("got result \(str)") -// NOERR-NEXT: print("after") -// NOERR-NOT: } + // BOUND-COMMENT: do { + // BOUND-COMMENT-NEXT: let str = try await withError() + // BOUND-COMMENT-NEXT: // a + // BOUND-COMMENT-NEXT: // b + // BOUND-COMMENT-NEXT: print("before") + // BOUND-COMMENT-NEXT: // c + // BOUND-COMMENT-NEXT: // h + // BOUND-COMMENT-NEXT: // i + // BOUND-COMMENT-NEXT: // j + // BOUND-COMMENT-NEXT: print("got result \(str)") + // BOUND-COMMENT-NEXT: // k + // BOUND-COMMENT-NEXT: // l + // BOUND-COMMENT-NEXT: print("after") + // BOUND-COMMENT-NEXT: // m + // BOUND-COMMENT-NEXT: {{ }} + // BOUND-COMMENT-NEXT: } catch let bad { + // BOUND-COMMENT-NEXT: // d + // BOUND-COMMENT-NEXT: // e + // BOUND-COMMENT-NEXT: print("got error \(bad)") + // BOUND-COMMENT-NEXT: // f + // BOUND-COMMENT-NEXT: // g + // BOUND-COMMENT-NEXT: {{ }} + // BOUND-COMMENT-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NORES %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - } - print("after") -} -// NORES: do { -// NORES-NEXT: let res = try await withError() -// NORES-NEXT: print("before") -// NORES-NEXT: print("after") -// NORES-NEXT: } catch let bad { -// NORES-NEXT: print("got error \(bad)") -// NORES-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if ((res != (nil)) && err == nil) { - print("got result \(res!)") - } else { - print("got error \(err!)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-COND %s -withError { res, err in - print("before") - if res != nil && test(res!) { - print("got result \(res!)") - } - print("after") -} -// UNKNOWN-COND: convert_params_single.swift -// UNKNOWN-COND-NEXT: let res = try await withError() -// UNKNOWN-COND-NEXT: print("before") -// UNKNOWN-COND-NEXT: if <#res#> != nil && test(res) { -// UNKNOWN-COND-NEXT: print("got result \(res)") -// UNKNOWN-COND-NEXT: } -// UNKNOWN-COND-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-CONDELSE %s -withError { res, err in - print("before") - if res != nil && test(res!) { - print("got result \(res!)") - } else { - print("bad") - } - print("after") -} -// UNKNOWN-CONDELSE: var res: String? = nil -// UNKNOWN-CONDELSE-NEXT: var err: Error? = nil -// UNKNOWN-CONDELSE-NEXT: do { -// UNKNOWN-CONDELSE-NEXT: res = try await withError() -// UNKNOWN-CONDELSE-NEXT: } catch { -// UNKNOWN-CONDELSE-NEXT: err = error -// UNKNOWN-CONDELSE-NEXT: } -// UNKNOWN-CONDELSE-EMPTY: -// UNKNOWN-CONDELSE-NEXT: print("before") -// UNKNOWN-CONDELSE-NEXT: if res != nil && test(res!) { -// UNKNOWN-CONDELSE-NEXT: print("got result \(res!)") -// UNKNOWN-CONDELSE-NEXT: } else { -// UNKNOWN-CONDELSE-NEXT: print("bad") -// UNKNOWN-CONDELSE-NEXT: } -// UNKNOWN-CONDELSE-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIBIND %s -withError { res, err in - print("before") - if let str = res { - print("got result \(str)") - } - if let str2 = res { - print("got result \(str2)") - } - if case (let str3?) = (res) { - print("got result \(str3)") - } - print("after") -} -// MULTIBIND: let str = try await withError() -// MULTIBIND-NEXT: print("before") -// MULTIBIND-NEXT: print("got result \(str)") -// MULTIBIND-NEXT: print("got result \(str)") -// MULTIBIND-NEXT: print("got result \(str)") -// MULTIBIND-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDRET %s -withError { res, err in - print("before") - if let str = res { - if test(str) { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s + withError { res, err in + print("before") + guard let str = res else { + print("got error \(err!)") return } print("got result \(str)") + print("after") } - print("after") -} -// NESTEDRET: convert_params_single.swift -// NESTEDRET-NEXT: let str = try await withError() -// NESTEDRET-NEXT: print("before") -// NESTEDRET-NEXT: if test(str) { -// NESTEDRET-NEXT: <#return#> -// NESTEDRET-NEXT: } -// NESTEDRET-NEXT: print("got result \(str)") -// NESTEDRET-NEXT: print("after") -// NESTEDRET-NOT: } + // UNBOUND-ERR: do { + // UNBOUND-ERR-NEXT: let str = try await withError() + // UNBOUND-ERR-NEXT: print("before") + // UNBOUND-ERR-NEXT: print("got result \(str)") + // UNBOUND-ERR-NEXT: print("after") + // UNBOUND-ERR-NEXT: } catch let err { + // UNBOUND-ERR-NEXT: print("got error \(err)") + // UNBOUND-ERR-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s -withError { res, err in - print("before") - guard let str = res, err == nil else { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + } else if let str = res { + print("got result \(str)") + } + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + if let str = res { + print("got result \(str)") + } + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + print("got result \(res!)") + print("after") + } + // UNBOUND-RES: do { + // UNBOUND-RES-NEXT: let res = try await withError() + // UNBOUND-RES-NEXT: print("before") + // UNBOUND-RES-NEXT: print("got result \(res)") + // UNBOUND-RES-NEXT: print("after") + // UNBOUND-RES-NEXT: } catch let bad { + // UNBOUND-RES-NEXT: print("got error \(bad)") + // UNBOUND-RES-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s + withError { res, err in + print("before") + if let str = res { + print("got result \(str)") + print("after") + return + } print("got error \(err!)") - return } - print("got result \(str)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - guard res != nil else { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + } else { + print("got result \(res!)") + } + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s + withError { res, err in + print("before") + if let str = res { + print("got result \(str)") + } else { + print("got error \(err!)") + } + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if err != nil { + print("got error \(err!)") + return + } + print("got result \(res!)") + print("after") + } + // UNBOUND: do { + // UNBOUND-NEXT: let res = try await withError() + // UNBOUND-NEXT: print("before") + // UNBOUND-NEXT: print("got result \(res)") + // UNBOUND-NEXT: print("after") + // UNBOUND-NEXT: } catch let err { + // UNBOUND-NEXT: print("got error \(err)") + // UNBOUND-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if res != nil { + print("got result \(res!)") + print("after") + return + } print("got error \(err!)") - return } - print("got result \(res!)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - guard err == nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if err != nil { + print("got error \(err!)") + } else { + print("got result \(res!)") + } + print("after") } - print("got result \(res!)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - guard res != nil && err == nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if res != nil { + print("got result \(res!)") + } else { + print("got error \(err!)") + } + print("after") } - print("got result \(res!)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNWRAPPING %s -withError { str, err in - print("before") - guard err == nil else { return } - _ = str!.count - _ = /*before*/str!/*after*/.count - _ = str?.count - _ = str!.count.bitWidth - _ = str?.count.bitWidth - _ = (str?.count.bitWidth)! - _ = str!.first?.isWhitespace - _ = str?.first?.isWhitespace - _ = (str?.first?.isWhitespace)! - print("after") -} -// UNWRAPPING: let str = try await withError() -// UNWRAPPING-NEXT: print("before") -// UNWRAPPING-NEXT: _ = str.count -// UNWRAPPING-NEXT: _ = /*before*/str/*after*/.count -// UNWRAPPING-NEXT: _ = str.count -// UNWRAPPING-NEXT: _ = str.count.bitWidth -// UNWRAPPING-NEXT: _ = str.count.bitWidth - -// Note this transform results in invalid code as str.count.bitWidth is no -// longer optional, but arguably it's more useful than leaving str as a -// placeholder. In general, the tranform we perform here is locally valid -// within the optional chain, but may change the type of the overall chain. -// UNWRAPPING-NEXT: _ = (str.count.bitWidth)! - -// UNWRAPPING-NEXT: _ = str.first?.isWhitespace -// UNWRAPPING-NEXT: _ = str.first?.isWhitespace -// UNWRAPPING-NEXT: _ = (str.first?.isWhitespace)! -// UNWRAPPING-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NOT-OPTIONAL %s -notOptional { str, err in - print("before") - if let err2 = err { - print("got error \(err2)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if err == nil { + print("got result \(res!)") + } else { + print("got error \(err!)") + } + print("after") } - print("got result \(str)") - print("after") -} -// NOT-OPTIONAL: do { -// NOT-OPTIONAL-NEXT: let str = try await notOptional() -// NOT-OPTIONAL-NEXT: print("before") -// NOT-OPTIONAL-NEXT: print("got result \(str)") -// NOT-OPTIONAL-NEXT: print("after") -// NOT-OPTIONAL-NEXT: } catch let err2 { -// NOT-OPTIONAL-NEXT: print("got error \(err2)") -// NOT-OPTIONAL-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ERROR-ONLY %s -errorOnly { err in - print("before") - if let err2 = err { - print("got error \(err2)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if res == nil { + print("got error \(err!)") + } else { + print("got result \(res!)") + } + print("after") } - print("after") + + // Cannot use refactor-check-compiles because of the placeholder, compiler is unable to infer 'str'. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNHANDLEDNESTED %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + } else { + if let str = res { + print("got result \(str)") + } + } + print("after") + } + // UNHANDLEDNESTED: do { + // UNHANDLEDNESTED-NEXT: let res = try await withError() + // UNHANDLEDNESTED-NEXT: print("before") + // UNHANDLEDNESTED-NEXT: if let str = <#res#> { + // UNHANDLEDNESTED-NEXT: print("got result \(str)") + // UNHANDLEDNESTED-NEXT: } + // UNHANDLEDNESTED-NEXT: print("after") + // UNHANDLEDNESTED-NEXT: } catch let bad { + // UNHANDLEDNESTED-NEXT: print("got error \(bad)") + // UNHANDLEDNESTED-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOERR %s + withError { res, err in + print("before") + if let str = res { + print("got result \(str)") + } + print("after") + } + // NOERR: convert_params_single.swift + // NOERR-NEXT: let str = try await withError() + // NOERR-NEXT: print("before") + // NOERR-NEXT: print("got result \(str)") + // NOERR-NEXT: print("after") + // NOERR-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NORES %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + } + print("after") + } + // NORES: do { + // NORES-NEXT: let res = try await withError() + // NORES-NEXT: print("before") + // NORES-NEXT: print("after") + // NORES-NEXT: } catch let bad { + // NORES-NEXT: print("got error \(bad)") + // NORES-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if ((res != (nil)) && err == nil) { + print("got result \(res!)") + } else { + print("got error \(err!)") + } + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-COND %s + withError { res, err in + print("before") + if res != nil && test(res!) { + print("got result \(res!)") + } + print("after") + } + // UNKNOWN-COND: convert_params_single.swift + // UNKNOWN-COND-NEXT: let res = try await withError() + // UNKNOWN-COND-NEXT: print("before") + // UNKNOWN-COND-NEXT: if <#res#> != nil && test(res) { + // UNKNOWN-COND-NEXT: print("got result \(res)") + // UNKNOWN-COND-NEXT: } + // UNKNOWN-COND-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-CONDELSE %s + withError { res, err in + print("before") + if res != nil && test(res!) { + print("got result \(res!)") + } else { + print("bad") + } + print("after") + } + // UNKNOWN-CONDELSE: var res: String? = nil + // UNKNOWN-CONDELSE-NEXT: var err: Error? = nil + // UNKNOWN-CONDELSE-NEXT: do { + // UNKNOWN-CONDELSE-NEXT: res = try await withError() + // UNKNOWN-CONDELSE-NEXT: } catch { + // UNKNOWN-CONDELSE-NEXT: err = error + // UNKNOWN-CONDELSE-NEXT: } + // UNKNOWN-CONDELSE-EMPTY: + // UNKNOWN-CONDELSE-NEXT: print("before") + // UNKNOWN-CONDELSE-NEXT: if res != nil && test(res!) { + // UNKNOWN-CONDELSE-NEXT: print("got result \(res!)") + // UNKNOWN-CONDELSE-NEXT: } else { + // UNKNOWN-CONDELSE-NEXT: print("bad") + // UNKNOWN-CONDELSE-NEXT: } + // UNKNOWN-CONDELSE-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIBIND %s + withError { res, err in + print("before") + if let str = res { + print("got result \(str)") + } + if let str2 = res { + print("got result \(str2)") + } + if case (let str3?) = (res) { + print("got result \(str3)") + } + print("after") + } + // MULTIBIND: let str = try await withError() + // MULTIBIND-NEXT: print("before") + // MULTIBIND-NEXT: print("got result \(str)") + // MULTIBIND-NEXT: print("got result \(str)") + // MULTIBIND-NEXT: print("got result \(str)") + // MULTIBIND-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDRET %s + withError { res, err in + print("before") + if let str = res { + if test(str) { + return + } + print("got result \(str)") + } + print("after") + } + // NESTEDRET: convert_params_single.swift + // NESTEDRET-NEXT: let str = try await withError() + // NESTEDRET-NEXT: print("before") + // NESTEDRET-NEXT: if test(str) { + // NESTEDRET-NEXT: <#return#> + // NESTEDRET-NEXT: } + // NESTEDRET-NEXT: print("got result \(str)") + // NESTEDRET-NEXT: print("after") + // NESTEDRET-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s + withError { res, err in + print("before") + guard let str = res, err == nil else { + print("got error \(err!)") + return + } + print("got result \(str)") + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + guard res != nil else { + print("got error \(err!)") + return + } + print("got result \(res!)") + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + guard err == nil else { + print("got error \(err!)") + return + } + print("got result \(res!)") + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + guard res != nil && err == nil else { + print("got error \(err!)") + return + } + print("got result \(res!)") + print("after") + } + + // Cannot use refactor-check-compiles as transform results in invalid code, + // see comment below. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNWRAPPING %s + withError { str, err in + print("before") + guard err == nil else { return } + _ = str!.count + _ = /*before*/str!/*after*/.count + _ = str?.count + _ = str!.count.bitWidth + _ = str?.count.bitWidth + _ = (str?.count.bitWidth)! + _ = str!.first?.isWhitespace + _ = str?.first?.isWhitespace + _ = (str?.first?.isWhitespace)! + print("after") + } + // UNWRAPPING: let str = try await withError() + // UNWRAPPING-NEXT: print("before") + // UNWRAPPING-NEXT: _ = str.count + // UNWRAPPING-NEXT: _ = /*before*/str/*after*/.count + // UNWRAPPING-NEXT: _ = str.count + // UNWRAPPING-NEXT: _ = str.count.bitWidth + // UNWRAPPING-NEXT: _ = str.count.bitWidth + + // Note this transform results in invalid code as str.count.bitWidth is no + // longer optional, but arguably it's more useful than leaving str as a + // placeholder. In general, the tranform we perform here is locally valid + // within the optional chain, but may change the type of the overall chain. + // UNWRAPPING-NEXT: _ = (str.count.bitWidth)! + + // UNWRAPPING-NEXT: _ = str.first?.isWhitespace + // UNWRAPPING-NEXT: _ = str.first?.isWhitespace + // UNWRAPPING-NEXT: _ = (str.first?.isWhitespace)! + // UNWRAPPING-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOT-OPTIONAL %s + notOptional { str, err in + print("before") + if let err2 = err { + print("got error \(err2)") + return + } + print("got result \(str)") + print("after") + } + // NOT-OPTIONAL: do { + // NOT-OPTIONAL-NEXT: let str = try await notOptional() + // NOT-OPTIONAL-NEXT: print("before") + // NOT-OPTIONAL-NEXT: print("got result \(str)") + // NOT-OPTIONAL-NEXT: print("after") + // NOT-OPTIONAL-NEXT: } catch let err2 { + // NOT-OPTIONAL-NEXT: print("got error \(err2)") + // NOT-OPTIONAL-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ERROR-ONLY %s + errorOnly { err in + print("before") + if let err2 = err { + print("got error \(err2)") + return + } + print("after") + } + // ERROR-ONLY: convert_params_single.swift + // ERROR-ONLY-NEXT: do { + // ERROR-ONLY-NEXT: try await errorOnly() + // ERROR-ONLY-NEXT: print("before") + // ERROR-ONLY-NEXT: print("after") + // ERROR-ONLY-NEXT: } catch let err2 { + // ERROR-ONLY-NEXT: print("got error \(err2)") + // ERROR-ONLY-NEXT: } + // ERROR-ONLY-NOT: } } -// ERROR-ONLY: convert_params_single.swift -// ERROR-ONLY-NEXT: do { -// ERROR-ONLY-NEXT: try await errorOnly() -// ERROR-ONLY-NEXT: print("before") -// ERROR-ONLY-NEXT: print("after") -// ERROR-ONLY-NEXT: } catch let err2 { -// ERROR-ONLY-NEXT: print("got error \(err2)") -// ERROR-ONLY-NEXT: } -// ERROR-ONLY-NOT: } diff --git a/test/refactoring/ConvertAsync/convert_pattern.swift b/test/refactoring/ConvertAsync/convert_pattern.swift index 491ff6a1569..ae2d4377ae6 100644 --- a/test/refactoring/ConvertAsync/convert_pattern.swift +++ b/test/refactoring/ConvertAsync/convert_pattern.swift @@ -123,7 +123,7 @@ func testPatterns() async throws { // FALLBACK-NEXT: guard let (str1, str2) = strs, str1 == "hi" else { fatalError() } // FALLBACK-NEXT: print(str1, str2, err) - // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=GUARD-AND-UNHANDLED %s + // 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) @@ -436,6 +436,7 @@ func testNameCollision2(_ completion: @escaping () -> Void) { // 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 { @@ -552,7 +553,7 @@ anyResultCompletion { res in // 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 + // 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!) diff --git a/test/refactoring/ConvertAsync/convert_result.swift b/test/refactoring/ConvertAsync/convert_result.swift index dd0e70b29ac..c63a608b0f7 100644 --- a/test/refactoring/ConvertAsync/convert_result.swift +++ b/test/refactoring/ConvertAsync/convert_result.swift @@ -1,513 +1,531 @@ +// RUN: %empty-directory(%t) + // REQUIRES: concurrency func simple(_ completion: @escaping (Result) -> Void) { } +func simple() async throws -> String { "" } + +func simpleWithArg(_ arg: Int) async throws -> String { "" } func simpleWithArg(_ arg: Int, _ completion: @escaping (Result) -> Void) { } + +func noError() async -> String { "" } func noError(_ completion: @escaping (Result) -> Void) { } + +func voidNoError() async {} +func voidNoError(completion: @escaping (Result) -> Void) {} + +func voidError() async throws {} +func voidError(completion: @escaping (Result) -> Void) {} + func test(_ str: String) -> Bool { return false } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-RESULT %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-RESULT %s func voidResult(completion: @escaping (Result) -> Void) {} // VOID-RESULT: func voidResult() async {} -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-RESULT %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-RESULT %s func voidAndErrorResult(completion: @escaping (Result) -> Void) {} // VOID-AND-ERROR-RESULT: func voidAndErrorResult() async throws {} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SIMPLE %s -simple { res in - print("result \(res)") -} -// SIMPLE: let res = try await simple() -// SIMPLE-NEXT: print("result \(<#res#>)") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NOERROR %s -noError { res in - print("result \(res)") -} -// NOERROR: let res = await noError() -// NOERROR-NEXT: print("result \(<#res#>)") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCK %s -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - case .failure(let err): - print("error \(err)") +func testResultConversion() async throws { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SIMPLE %s + simple { res in + print("result \(res)") } - print("after") -} -// DOBLOCK: do { -// DOBLOCK-NEXT: let str = try await simple() -// DOBLOCK-NEXT: print("before") -// DOBLOCK-NEXT: print("result \(str)") -// DOBLOCK-NEXT: print("after") -// DOBLOCK-NEXT: } catch let err { -// DOBLOCK-NEXT: print("error \(err)") -// DOBLOCK-NEXT: } + // SIMPLE: let res = try await simple() + // SIMPLE-NEXT: print("result \(<#res#>)") -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCK %s -simple { res in - print("before") - if case .success(let str) = res { - print("result \(str)") - } else if case .failure(let err) = res { - print("error \(err)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOERROR %s + noError { res in + print("result \(res)") } - print("after") -} + // NOERROR: let res = await noError() + // NOERROR-NEXT: print("result \(<#res#>)") -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCK %s -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - break - case .failure(let err): - print("error \(err)") - break + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCK %s + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + case .failure(let err): + print("error \(err)") + } + print("after") } - print("after") -} + // DOBLOCK: do { + // DOBLOCK-NEXT: let str = try await simple() + // DOBLOCK-NEXT: print("before") + // DOBLOCK-NEXT: print("result \(str)") + // DOBLOCK-NEXT: print("after") + // DOBLOCK-NEXT: } catch let err { + // DOBLOCK-NEXT: print("error \(err)") + // DOBLOCK-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCK %s -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - return - case .failure(let err): - print("error \(err)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCK %s + simple { res in + print("before") + if case .success(let str) = res { + print("result \(str)") + } else if case .failure(let err) = res { + print("error \(err)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SUCCESS %s -simple { res in - print("before") - if case .success(let str) = res { - print("result \(str)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCK %s + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + break + case .failure(let err): + print("error \(err)") + break + } + print("after") } - print("after") -} -// SUCCESS: convert_result.swift -// SUCCESS-NEXT: let str = try await simple() -// SUCCESS-NEXT: print("before") -// SUCCESS-NEXT: print("result \(str)") -// SUCCESS-NEXT: print("after") -// SUCCESS-NOT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SUCCESS %s -simple { res in - print("before") - guard case .success(let str) = res else { - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCK %s + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + return + case .failure(let err): + print("error \(err)") + return + } + print("after") } - print("result \(str)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCKUNBOUND %s -simple { res in - print("before") - guard case .success(let str) = res else { - print("err") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SUCCESS %s + simple { res in + print("before") + if case .success(let str) = res { + print("result \(str)") + } + print("after") } - print("result \(str)") - print("after") -} -// DOBLOCKUNBOUND: do { -// DOBLOCKUNBOUND-NEXT: let str = try await simple() -// DOBLOCKUNBOUND-NEXT: print("before") -// DOBLOCKUNBOUND-NEXT: print("result \(str)") -// DOBLOCKUNBOUND-NEXT: print("after") -// DOBLOCKUNBOUND-NEXT: } catch { -// DOBLOCKUNBOUND-NEXT: print("err") -// DOBLOCKUNBOUND-NEXT: } + // SUCCESS: convert_result.swift + // SUCCESS-NEXT: let str = try await simple() + // SUCCESS-NEXT: print("before") + // SUCCESS-NEXT: print("result \(str)") + // SUCCESS-NEXT: print("after") + // SUCCESS-NOT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SUCCESS %s -simple { res in - print("before") - if let str = try? res.get() { - print("result \(str)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCKUNBOUND %s -simple { res in - print("before") - guard let str = try? res.get() else { - print("err") - return - } - print("result \(str)") - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=UNKNOWN %s -simple { res in - print("before \(res)") - if case .success(let str) = res { - print("result \(str) \(try! res.get())") - } - print("after") -} -// UNKNOWN: convert_result.swift -// UNKNOWN-NEXT: let str = try await simple() -// UNKNOWN-NEXT: print("before \(<#str#>)") -// UNKNOWN-NEXT: print("result \(str) \(try! <#str#>.get())") -// UNKNOWN-NEXT: print("after") -// UNKNOWN-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=UNKNOWNUNBOUND %s -simple { res in - print("before \(res)") - if case .success = res { - print("result \(res) \(try! res.get())") - } - print("after") -} -// UNKNOWNUNBOUND: convert_result.swift -// UNKNOWNUNBOUND-NEXT: let res = try await simple() -// UNKNOWNUNBOUND-NEXT: print("before \(<#res#>)") -// UNKNOWNUNBOUND-NEXT: print("result \(<#res#>) \(try! <#res#>.get())") -// UNKNOWNUNBOUND-NEXT: print("after") -// UNKNOWN-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-BINDS %s -simple { res in - print("before") - if case .success(let str) = res { - print("result \(str)") - } - if case .success(let str2) = res { - print("result \(str2)") - } - print("after") -} -// MULTIPLE-BINDS: convert_result.swift -// MULTIPLE-BINDS-NEXT: let str = try await simple() -// MULTIPLE-BINDS-NEXT: print("before") -// MULTIPLE-BINDS-NEXT: print("result \(str)") -// MULTIPLE-BINDS-NEXT: print("result \(str)") -// MULTIPLE-BINDS-NEXT: print("after") -// MULTIPLE-BINDS-NOT: } - -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - case .failure(let err): - print("error \(err)") - default: - print("default") - } - print("after") -} - -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - default: - print("err") - } - print("after") -} - -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success, .failure: - print("either") - } - print("after") -} - -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success, .failure: - print("either") - } - print("after") -} - -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success: - fallthrough - case .failure: - print("either") - } - print("after") -} - -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success(let str) where str.hasPrefix("match"): - print("pattern matched result \(str)") - case .success(let str): - print("result \(str)") - case .failure(let err): - print("error \(err)") - } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NESTEDRET %s -simple { res in - print("before") - switch res { - case .success(let str): - if test(str) { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SUCCESS %s + simple { res in + print("before") + guard case .success(let str) = res else { return } print("result \(str)") - case .failure: - break + print("after") } - print("after") -} -// NESTEDRET: convert_result.swift -// NESTEDRET-NEXT: let str = try await simple() -// NESTEDRET-NEXT: print("before") -// NESTEDRET-NEXT: if test(str) { -// NESTEDRET-NEXT: <#return#> -// NESTEDRET-NEXT: } -// NESTEDRET-NEXT: print("result \(str)") -// NESTEDRET-NEXT: print("after") -// NESTEDRET-NOT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NESTEDBREAK %s -simple { res in - print("before") - switch res { - case .success(let str): - if test(str) { - break + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCKUNBOUND %s + simple { res in + print("before") + guard case .success(let str) = res else { + print("err") + return } print("result \(str)") - case .failure: - break + print("after") } - print("after") -} -// NESTEDBREAK: convert_result.swift -// NESTEDBREAK-NEXT: let str = try await simple() -// NESTEDBREAK-NEXT: print("before") -// NESTEDBREAK-NEXT: if test(str) { -// NESTEDBREAK-NEXT: <#break#> -// NESTEDBREAK-NEXT: } -// NESTEDBREAK-NEXT: print("result \(str)") -// NESTEDBREAK-NEXT: print("after") -// NESTEDBREAK-NOT: } + // DOBLOCKUNBOUND: do { + // DOBLOCKUNBOUND-NEXT: let str = try await simple() + // DOBLOCKUNBOUND-NEXT: print("before") + // DOBLOCKUNBOUND-NEXT: print("result \(str)") + // DOBLOCKUNBOUND-NEXT: print("after") + // DOBLOCKUNBOUND-NEXT: } catch { + // DOBLOCKUNBOUND-NEXT: print("err") + // DOBLOCKUNBOUND-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NESTEDBREAK-COMMENT %s -simple { res in // a - // b - print("before") - // c - switch res { - // d - case .success(let str): // e - if test(str) { - // f + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SUCCESS %s + simple { res in + print("before") + if let str = try? res.get() { + print("result \(str)") + } + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCKUNBOUND %s + simple { res in + print("before") + guard let str = try? res.get() else { + print("err") + return + } + print("result \(str)") + print("after") + } + + // Cannot use refactor-check-compiles, as cannot infer type of the 'get' member on the placeholder. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN %s + simple { res in + print("before \(res)") + if case .success(let str) = res { + print("result \(str) \(try! res.get())") + } + print("after") + } + // UNKNOWN: convert_result.swift + // UNKNOWN-NEXT: let str = try await simple() + // UNKNOWN-NEXT: print("before \(<#str#>)") + // UNKNOWN-NEXT: print("result \(str) \(try! <#str#>.get())") + // UNKNOWN-NEXT: print("after") + // UNKNOWN-NOT: } + + // Cannot use refactor-check-compiles, as cannot infer type of the 'get' member on the placeholder. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWNUNBOUND %s + simple { res in + print("before \(res)") + if case .success = res { + print("result \(res) \(try! res.get())") + } + print("after") + } + // UNKNOWNUNBOUND: convert_result.swift + // UNKNOWNUNBOUND-NEXT: let res = try await simple() + // UNKNOWNUNBOUND-NEXT: print("before \(<#res#>)") + // UNKNOWNUNBOUND-NEXT: print("result \(<#res#>) \(try! <#res#>.get())") + // UNKNOWNUNBOUND-NEXT: print("after") + // UNKNOWN-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIPLE-BINDS %s + simple { res in + print("before") + if case .success(let str) = res { + print("result \(str)") + } + if case .success(let str2) = res { + print("result \(str2)") + } + print("after") + } + // MULTIPLE-BINDS: convert_result.swift + // MULTIPLE-BINDS-NEXT: let str = try await simple() + // MULTIPLE-BINDS-NEXT: print("before") + // MULTIPLE-BINDS-NEXT: print("result \(str)") + // MULTIPLE-BINDS-NEXT: print("result \(str)") + // MULTIPLE-BINDS-NEXT: print("after") + // MULTIPLE-BINDS-NOT: } + + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + case .failure(let err): + print("error \(err)") + default: + print("default") + } + print("after") + } + + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + default: + print("err") + } + print("after") + } + + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success, .failure: + print("either") + } + print("after") + } + + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success, .failure: + print("either") + } + print("after") + } + + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success: + fallthrough + case .failure: + print("either") + } + print("after") + } + + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success(let str) where str.hasPrefix("match"): + print("pattern matched result \(str)") + case .success(let str): + print("result \(str)") + case .failure(let err): + print("error \(err)") + } + print("after") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDRET %s + simple { res in + print("before") + switch res { + case .success(let str): + if test(str) { + return + } + print("result \(str)") + case .failure: break + } + print("after") + } + // NESTEDRET: convert_result.swift + // NESTEDRET-NEXT: let str = try await simple() + // NESTEDRET-NEXT: print("before") + // NESTEDRET-NEXT: if test(str) { + // NESTEDRET-NEXT: <#return#> + // NESTEDRET-NEXT: } + // NESTEDRET-NEXT: print("result \(str)") + // NESTEDRET-NEXT: print("after") + // NESTEDRET-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDBREAK %s + simple { res in + print("before") + switch res { + case .success(let str): + if test(str) { + break + } + print("result \(str)") + case .failure: + break + } + print("after") + } + // NESTEDBREAK: convert_result.swift + // NESTEDBREAK-NEXT: let str = try await simple() + // NESTEDBREAK-NEXT: print("before") + // NESTEDBREAK-NEXT: if test(str) { + // NESTEDBREAK-NEXT: <#break#> + // NESTEDBREAK-NEXT: } + // NESTEDBREAK-NEXT: print("result \(str)") + // NESTEDBREAK-NEXT: print("after") + // NESTEDBREAK-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDBREAK-COMMENT %s + simple { res in // a + // b + print("before") + // c + switch res { + // d + case .success(let str): // e + if test(str) { + // f + break + // g + } + // h + print("result \(str)") + // i + case .failure: + // j + break + // k + } + // l + print("after") + // m + } + // NESTEDBREAK-COMMENT: let str = try await simple() + // NESTEDBREAK-COMMENT-NEXT: // a + // NESTEDBREAK-COMMENT-NEXT: // b + // NESTEDBREAK-COMMENT-NEXT: print("before") + // NESTEDBREAK-COMMENT-NEXT: // c + // NESTEDBREAK-COMMENT-NEXT: // d + // NESTEDBREAK-COMMENT-NEXT: // e + // NESTEDBREAK-COMMENT-NEXT: if test(str) { + // NESTEDBREAK-COMMENT-NEXT: // f + // NESTEDBREAK-COMMENT-NEXT: <#break#> + // NESTEDBREAK-COMMENT-NEXT: // g + // NESTEDBREAK-COMMENT-NEXT: } + // NESTEDBREAK-COMMENT-NEXT: // h + // NESTEDBREAK-COMMENT-NEXT: print("result \(str)") + // NESTEDBREAK-COMMENT-NEXT: // i + // NESTEDBREAK-COMMENT-NEXT: // j + // NESTEDBREAK-COMMENT-NEXT: // k + // NESTEDBREAK-COMMENT-NEXT: // l + // NESTEDBREAK-COMMENT-NEXT: print("after") + // NESTEDBREAK-COMMENT-NEXT: // m + // NESTEDBREAK-COMMENT-NEXT: {{ }} + // NESTEDBREAK-COMMENT-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ERROR-BLOCK-COMMENT %s + simple { res in + // a + print("before") + // b + switch res { + case .success(let str): + // c + print("result \(str)") + // d + case .failure: + // e + print("fail") + // f + return // g } // h - print("result \(str)") + print("after") // i - case .failure: - // j - break - // k } - // l - print("after") - // m -} -// NESTEDBREAK-COMMENT: let str = try await simple() -// NESTEDBREAK-COMMENT-NEXT: // a -// NESTEDBREAK-COMMENT-NEXT: // b -// NESTEDBREAK-COMMENT-NEXT: print("before") -// NESTEDBREAK-COMMENT-NEXT: // c -// NESTEDBREAK-COMMENT-NEXT: // d -// NESTEDBREAK-COMMENT-NEXT: // e -// NESTEDBREAK-COMMENT-NEXT: if test(str) { -// NESTEDBREAK-COMMENT-NEXT: // f -// NESTEDBREAK-COMMENT-NEXT: <#break#> -// NESTEDBREAK-COMMENT-NEXT: // g -// NESTEDBREAK-COMMENT-NEXT: } -// NESTEDBREAK-COMMENT-NEXT: // h -// NESTEDBREAK-COMMENT-NEXT: print("result \(str)") -// NESTEDBREAK-COMMENT-NEXT: // i -// NESTEDBREAK-COMMENT-NEXT: // j -// NESTEDBREAK-COMMENT-NEXT: // k -// NESTEDBREAK-COMMENT-NEXT: // l -// NESTEDBREAK-COMMENT-NEXT: print("after") -// NESTEDBREAK-COMMENT-NEXT: // m -// NESTEDBREAK-COMMENT-EMPTY: -// NESTEDBREAK-COMMENT-NOT: } + // ERROR-BLOCK-COMMENT: do { + // ERROR-BLOCK-COMMENT-NEXT: let str = try await simple() + // ERROR-BLOCK-COMMENT-NEXT: // a + // ERROR-BLOCK-COMMENT-NEXT: print("before") + // ERROR-BLOCK-COMMENT-NEXT: // b + // ERROR-BLOCK-COMMENT-NEXT: // c + // ERROR-BLOCK-COMMENT-NEXT: print("result \(str)") + // ERROR-BLOCK-COMMENT-NEXT: // d + // ERROR-BLOCK-COMMENT-NEXT: // h + // ERROR-BLOCK-COMMENT-NEXT: print("after") + // ERROR-BLOCK-COMMENT-NEXT: // i + // ERROR-BLOCK-COMMENT-NEXT: {{ }} + // ERROR-BLOCK-COMMENT-NEXT: } catch { + // ERROR-BLOCK-COMMENT-NEXT: // e + // ERROR-BLOCK-COMMENT-NEXT: print("fail") + // ERROR-BLOCK-COMMENT-NEXT: // f + // ERROR-BLOCK-COMMENT-NEXT: // g + // ERROR-BLOCK-COMMENT-NEXT: {{ }} + // ERROR-BLOCK-COMMENT-NEXT: } + // ERROR-BLOCK-COMMENT-NOT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ERROR-BLOCK-COMMENT %s -simple { res in - // a - print("before") - // b - switch res { - case .success(let str): - // c - print("result \(str)") - // d - case .failure: - // e - print("fail") - // f - return - // g + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=VOID-RESULT-CALL %s + voidNoError { res in + print(res) } - // h - print("after") - // i -} -// ERROR-BLOCK-COMMENT: do { -// ERROR-BLOCK-COMMENT-NEXT: let str = try await simple() -// ERROR-BLOCK-COMMENT-NEXT: // a -// ERROR-BLOCK-COMMENT-NEXT: print("before") -// ERROR-BLOCK-COMMENT-NEXT: // b -// ERROR-BLOCK-COMMENT-NEXT: // c -// ERROR-BLOCK-COMMENT-NEXT: print("result \(str)") -// ERROR-BLOCK-COMMENT-NEXT: // d -// ERROR-BLOCK-COMMENT-NEXT: // h -// ERROR-BLOCK-COMMENT-NEXT: print("after") -// ERROR-BLOCK-COMMENT-NEXT: // i -// ERROR-BLOCK-COMMENT-EMPTY: -// ERROR-BLOCK-COMMENT-NEXT: } catch { -// ERROR-BLOCK-COMMENT-NEXT: // e -// ERROR-BLOCK-COMMENT-NEXT: print("fail") -// ERROR-BLOCK-COMMENT-NEXT: // f -// ERROR-BLOCK-COMMENT-NEXT: // g -// ERROR-BLOCK-COMMENT-NEXT: {{ }} -// ERROR-BLOCK-COMMENT-NEXT: } -// ERROR-BLOCK-COMMENT-NOT: } + // VOID-RESULT-CALL: {{^}}await voidNoError() + // VOID-RESULT-CALL: {{^}}print(<#res#>) -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-RESULT-CALL %s -voidResult { res in - print(res) -} -// VOID-RESULT-CALL: {{^}}await voidResult() -// VOID-RESULT-CALL: {{^}}print(<#res#>) - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-AND-ERROR-RESULT-CALL %s -voidAndErrorResult { res in - print(res) -} -// VOID-AND-ERROR-RESULT-CALL: {{^}}try await voidAndErrorResult() -// VOID-AND-ERROR-RESULT-CALL: {{^}}print(<#res#>) - -// Make sure we ignore an unrelated switch. -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=IGNORE-UNRELATED %s -simple { res in - print("before") - switch Bool.random() { - case true: - break - case false: - break + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=VOID-AND-ERROR-RESULT-CALL %s + voidError { res in + print(res) } - print("after") -} -// IGNORE-UNRELATED: let res = try await simple() -// IGNORE-UNRELATED-NEXT: print("before") -// IGNORE-UNRELATED-NEXT: switch Bool.random() { -// IGNORE-UNRELATED-NEXT: case true: -// IGNORE-UNRELATED-NEXT: {{^}} break{{$}} -// IGNORE-UNRELATED-NEXT: case false: -// IGNORE-UNRELATED-NEXT: {{^}} break{{$}} -// IGNORE-UNRELATED-NEXT: } -// IGNORE-UNRELATED-NEXT: print("after") + // VOID-AND-ERROR-RESULT-CALL: {{^}}try await voidError() + // VOID-AND-ERROR-RESULT-CALL: {{^}}print(<#res#>) -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=BREAK-RET-PLACEHOLDER %s -simpleWithArg({ return 0 }()) { res in - switch res { - case .success: - if .random() { break } - x: if .random() { break x } - case .failure: - break + // Make sure we ignore an unrelated switch. + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=IGNORE-UNRELATED %s + simple { res in + print("before") + switch Bool.random() { + case true: + break + case false: + break + } + print("after") } + // IGNORE-UNRELATED: let res = try await simple() + // IGNORE-UNRELATED-NEXT: print("before") + // IGNORE-UNRELATED-NEXT: switch Bool.random() { + // IGNORE-UNRELATED-NEXT: case true: + // IGNORE-UNRELATED-NEXT: {{^}} break{{$}} + // IGNORE-UNRELATED-NEXT: case false: + // IGNORE-UNRELATED-NEXT: {{^}} break{{$}} + // IGNORE-UNRELATED-NEXT: } + // IGNORE-UNRELATED-NEXT: print("after") - func foo(_ x: T) { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BREAK-RET-PLACEHOLDER %s + simpleWithArg({ return 0 }()) { res in + switch res { + case .success: + if .random() { break } + x: if .random() { break x } + case .failure: + break + } + + func foo(_ x: T) { + if .random() { return } + } + foo(res) + + let fn = { + if .random() { return } + return + } + fn() + + _ = { return }() + + switch Bool.random() { + case true: + break + case false: + if .random() { break } + y: if .random() { break y } + return + } + + x: if .random() { + break x + } if .random() { return } } - foo(res) - let fn = { - if .random() { return } - return - } - fn() + // Make sure we replace lifted break/returns with placeholders, but keep nested + // break/returns in e.g closures or labelled control flow in place. - _ = { return }() - - switch Bool.random() { - case true: - break - case false: - if .random() { break } - y: if .random() { break y } - return - } - - x: if .random() { - break x - } - if .random() { return } + // BREAK-RET-PLACEHOLDER: let res = try await simpleWithArg({ return 0 }()) + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { <#break#> } + // BREAK-RET-PLACEHOLDER-NEXT: x: if .random() { break x } + // BREAK-RET-PLACEHOLDER-NEXT: func foo(_ x: T) { + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { return } + // BREAK-RET-PLACEHOLDER-NEXT: } + // BREAK-RET-PLACEHOLDER-NEXT: foo(<#res#>) + // BREAK-RET-PLACEHOLDER-NEXT: let fn = { + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { return } + // BREAK-RET-PLACEHOLDER-NEXT: {{^}} return{{$}} + // BREAK-RET-PLACEHOLDER-NEXT: } + // BREAK-RET-PLACEHOLDER-NEXT: fn() + // BREAK-RET-PLACEHOLDER-NEXT: _ = { return }() + // BREAK-RET-PLACEHOLDER-NEXT: switch Bool.random() { + // BREAK-RET-PLACEHOLDER-NEXT: case true: + // BREAK-RET-PLACEHOLDER-NEXT: {{^}} break{{$}} + // BREAK-RET-PLACEHOLDER-NEXT: case false: + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { break } + // BREAK-RET-PLACEHOLDER-NEXT: y: if .random() { break y } + // BREAK-RET-PLACEHOLDER-NEXT: <#return#> + // BREAK-RET-PLACEHOLDER-NEXT: } + // BREAK-RET-PLACEHOLDER-NEXT: x: if .random() { + // BREAK-RET-PLACEHOLDER-NEXT: {{^}} break x{{$}} + // BREAK-RET-PLACEHOLDER-NEXT: } + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { <#return#> } } - -// Make sure we replace lifted break/returns with placeholders, but keep nested -// break/returns in e.g closures or labelled control flow in place. - -// BREAK-RET-PLACEHOLDER: let res = try await simpleWithArg({ return 0 }()) -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { <#break#> } -// BREAK-RET-PLACEHOLDER-NEXT: x: if .random() { break x } -// BREAK-RET-PLACEHOLDER-NEXT: func foo(_ x: T) { -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { return } -// BREAK-RET-PLACEHOLDER-NEXT: } -// BREAK-RET-PLACEHOLDER-NEXT: foo(<#res#>) -// BREAK-RET-PLACEHOLDER-NEXT: let fn = { -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { return } -// BREAK-RET-PLACEHOLDER-NEXT: {{^}} return{{$}} -// BREAK-RET-PLACEHOLDER-NEXT: } -// BREAK-RET-PLACEHOLDER-NEXT: fn() -// BREAK-RET-PLACEHOLDER-NEXT: _ = { return }() -// BREAK-RET-PLACEHOLDER-NEXT: switch Bool.random() { -// BREAK-RET-PLACEHOLDER-NEXT: case true: -// BREAK-RET-PLACEHOLDER-NEXT: {{^}} break{{$}} -// BREAK-RET-PLACEHOLDER-NEXT: case false: -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { break } -// BREAK-RET-PLACEHOLDER-NEXT: y: if .random() { break y } -// BREAK-RET-PLACEHOLDER-NEXT: <#return#> -// BREAK-RET-PLACEHOLDER-NEXT: } -// BREAK-RET-PLACEHOLDER-NEXT: x: if .random() { -// BREAK-RET-PLACEHOLDER-NEXT: {{^}} break x{{$}} -// BREAK-RET-PLACEHOLDER-NEXT: } -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { <#return#> } diff --git a/test/refactoring/ConvertAsync/path_classification.swift b/test/refactoring/ConvertAsync/path_classification.swift index 530ddd655c2..d2d73120096 100644 --- a/test/refactoring/ConvertAsync/path_classification.swift +++ b/test/refactoring/ConvertAsync/path_classification.swift @@ -62,7 +62,7 @@ func testPathClassification() async throws { // ELSE-IF-CLASSIFICATION-NEXT: print("d") // ELSE-IF-CLASSIFICATION-NEXT: } - // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ELSE-IF-CLASSIFICATION2 %s + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ELSE-IF-CLASSIFICATION2 %s simpleWithError { str, err in if err == nil { print("a") @@ -172,6 +172,8 @@ func testPathClassification() async throws { // ELSE-IF-CLASSIFICATION5-NEXT: } // ELSE-IF-CLASSIFICATION5-NEXT: } + // Cannot use refactor-check-compiles, as 'err' cannot have its type inferred + // from placeholder. // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=IF-LET-RETURN-CLASSIFICATION %s simpleWithError { str, err in if let str = str { @@ -196,7 +198,7 @@ func testPathClassification() async throws { // IF-LET-RETURN-CLASSIFICATION-NEXT: } // IF-LET-RETURN-CLASSIFICATION-NEXT: } - // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=GUARD-CLASSIFICATION %s + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=GUARD-CLASSIFICATION %s simpleWithError { str, err in guard let str = str else { print("a") diff --git a/utils/refactor-check-compiles.py b/utils/refactor-check-compiles.py index 13771867285..7af422672e5 100755 --- a/utils/refactor-check-compiles.py +++ b/utils/refactor-check-compiles.py @@ -23,7 +23,8 @@ def parse_args(): A drop-in replacement for a 'swift-refactor -dump-text' call that 1. Checks that the file still compiles after the refactoring by doing 'swift-refactor -dump-rewritten' and feeding the result to - 'swift-frontend -typecheck' + 'swift-frontend -typecheck -disable-availability-checking + -warn-on-editor-placeholder' 2. Outputting the result of the 'swift-refactor -dump-text' call All arguments other than the following will be forwarded to @@ -32,6 +33,9 @@ def parse_args(): - swift-refactor - temp-dir - enable-experimental-concurrency (sent to both) + - I (sent to both) + - sdk (sent to both) + - target (sent to both) """) parser.add_argument( @@ -70,6 +74,19 @@ def parse_args(): swift-frontend ''' ) + parser.add_argument( + '-I', + action='append', + help='Add a directory to the import search path' + ) + parser.add_argument( + '-sdk', + help='Path to the SDK to build against' + ) + parser.add_argument( + '-target', + help='The target triple to build for' + ) return parser.parse_known_args() @@ -80,10 +97,16 @@ def main(): args.pos.replace(':', '.') temp_file_path = os.path.join(args.temp_dir, temp_file_name) - extra_frontend_args = [] + extra_both_args = [] if args.enable_experimental_concurrency: - extra_refactor_args.append('-enable-experimental-concurrency') - extra_frontend_args.append('-enable-experimental-concurrency') + extra_both_args.append('-enable-experimental-concurrency') + if args.I: + for path in args.I: + extra_both_args += ['-I', path] + if args.sdk: + extra_both_args += ['-sdk', args.sdk] + if args.target: + extra_both_args += ['-target', args.target] dump_text_output = run_cmd([ args.swift_refactor, @@ -91,15 +114,16 @@ def main(): '-source-filename', args.source_filename, '-rewritten-output-file', temp_file_path, '-pos', args.pos - ] + extra_refactor_args, desc='producing edit').decode("utf-8") + ] + extra_refactor_args + extra_both_args, desc='producing edit').decode("utf-8") sys.stdout.write(dump_text_output) run_cmd([ args.swift_frontend, '-typecheck', temp_file_path, - '-disable-availability-checking' - ] + extra_frontend_args, desc='checking that rewritten file compiles') + '-disable-availability-checking', + '-warn-on-editor-placeholder' + ] + extra_both_args, desc='checking that rewritten file compiles') if __name__ == '__main__':