Commit Graph

196 Commits

Author SHA1 Message Date
Hamish Knight
06908adb08 [Async Refactoring] Handle multiple trailing closures
Update the trailing closure handling logic to
handle multiple trailing closures, and adjust the
refactoring output to surround the call in
parentheses rather than adding '.self'. This allows
the parser to deal with the multiple trailing
closures and also silences a warning that would
previously occur.

rdar://81230908
2021-07-28 20:50:40 +01:00
Hamish Knight
91c9b189cf [test] Change a %refactor into %refactor-check-compiles 2021-07-28 20:50:39 +01:00
Alex Hoppen
3bea2b728c [Async Refactoring] Handle parenthesis around params that no longer need to be unwrapped after refactoring
Throw another `getSemanticsProvidingExpr` in the async refactoring code for better results.
2021-07-28 18:13:02 +02:00
Rintaro Ishizaki
63124b5b7a Merge pull request #38637 from rintaro/disable-rdar81128571
[Test] Disable refactoring/ConvertAsync/convert_bool.swift
2021-07-26 14:47:49 -07:00
Rintaro Ishizaki
a18e3e6244 [Test] Disable refactoring/ConvertAsync/convert_bool.swift
rdar://81128571
2021-07-26 14:46:36 -07:00
Hamish Knight
40e20a270d Don't apply "Convert to Trailing Closure" to call with multiple trailing closures
Previously we'd perform an invalid transform or hit
an assertion failure. For now, disallow the transform
for a call that already uses trailing closure syntax.

rdar://81106400
2021-07-26 17:15:22 +01:00
Ben Barham
fabb02100f [Test] Add @escaping to async refactoring tests
The async refactorings ignore whether a completion handler had
`@escaping` or not. In preparation of fixing this, fix up all functions
to have `@escaping` for their completion handler parameter.

Also some small miscellaneous fixes in order to reduce the number of
warnings output on test failures and also the addition of `REQUIRES:
concurrency` on all tests.
2021-07-24 09:53:17 +10:00
Hamish Knight
1ebb1fd2f5 [Async Refactoring] Add parens around custom error cast
When passing a forwarded error to a CustomError?
completion handler parameter, wrap the cast in a
set of parentheses to silence a warning in the
refactored code.

rdar://80409905
2021-07-22 23:20:24 +01:00
Hamish Knight
007b91cf7b [Async Refactoring] Update to use Task's init
Use the Task initializer instead of the top-level
`async` function to match the latest Concurrency
API.

rdar:80888385
2021-07-21 11:40:35 +01:00
Alex Hoppen
ebfb3a5f5e Merge pull request #38331 from ahoppen/pr/rename-captured-variables-once
[Refactoring] Only rename variables in capture lists once
2021-07-13 19:11:20 +02:00
Alex Hoppen
a535203096 [Refactoring] Only rename variables in capture lists once
Inside capture lists like `{ [test] in }`, `test` refers to both the newly declared, captured variable and the referenced variable it is initialized from. We currently try to rename it twice, yielding invalid, confusing results. Make sure to only record this situation once.

Fixes rdar://78522816 [SR-14661]
2021-07-09 11:38:37 +02:00
Alex Hoppen
cd31fa0f73 [Async Refactoring] Improve fatalError message to unwrap result arguments in continuation
In the previous `fatalError` message `Expected non-nil result 'result' for nil error`, it wasn’t exactly clear wht the `error` referred to. In some cases, when the error was ignored, there wasn’t even an `error` variable in the refactored code.

The new `Expected non-nil result '...' in the non-error case` is a little more generic and also fits if an error is ignored.
2021-07-07 12:05:37 +02:00
Alex Hoppen
edc1393291 [Async Refactoring] Rename 'success param/argument' to 'result' in fatalError messages
The 'success param/argument' is a terminology we use internally in the refactoring and not one we want to present to the user. Just name it 'result' instead.
2021-07-07 11:54:11 +02:00
Alex Hoppen
06219cdfa2 [Async Refactoring] Remove assertions for nil result values in async wrapper
Since we aren’t checking that all result arguments are `nil` if there was an `error` in all the other refactorings, also remove the assertions in the async wrapper.
2021-07-07 11:54:11 +02:00
Alex Hoppen
6cbbb7cb1f [Async Refactoring] Split ambiguous (error + success) completion handler calls into success and error case
Previously, when a completion handler call had arguments for both the success and error parameters, we were always interpreting it as an error call. In case the error argument was an `Optional`, this could cause us to generate code that didn't compile, because we `throw`ed the `Error?` or passed it to `continuation.resume(throwing)`, both of which didn't work.

We now generate an `if let` statement that checks if the error is `nil`. If it is, the error is thrown. Otherwise, we interpret the call as a success call and return the result.

- Example 1 (convert to continuation)
-- Base Code
```swift
func test(completionHandler: (Int?, Error?) -> Void) {
  withoutAsyncAlternativeThrowing { (theValue, theError) in
    completionHandler(theValue, theError)
  }
}
```
-- Old Refactoring Result
```swift
func test() async throws -> Int {
  return try await withCheckedThrowingContinuation { continuation in
    withoutAsyncAlternativeThrowing { (theValue, theError) in
      continuation.resume(throwing: theError) // error: Argument type 'Error?' does not conform to expected type 'Error'
    }
  }
}
-- New Refactoring Result
```swift
func testThrowingContinuationRelayingErrorAndResult() async throws -> Int {
  return try await withCheckedThrowingContinuation { continuation in
    withoutAsyncAlternativeThrowing { (theValue, theError) in
      if let error = theError {
        continuation.resume(throwing: error)
      } else {
        guard let theValue = theValue else {
          fatalError("Expected non-nil success argument 'theValue' for nil error")
        }
        continuation.resume(returning: theValue)
      }
    }
  }
}
```

- Example 2 (convert to async/await)
-- Base Code
```swift
func test(completion: (String?, Error?) -> Void) {
  simpleErr() { (res, err) in
    completion(res, err)
  }
}
```
-- Old Refactoring Result
```swift
func test() async throws -> String {
  let res = try await simpleErr()
  throw <#err#>
}
```
-- New Refactoring Result
```swift
func test() async throws -> String {
  let res = try await simpleErr()
  return res
}
```
2021-07-07 11:54:11 +02:00
Alex Hoppen
c3e9676cb1 Merge pull request #38261 from ahoppen/pr/check-semantics-expr-for-completion-handler-call
[Async Refactoring] Get semantics providing expr to decide if call is to completion handler
2021-07-06 10:53:56 +02:00
Alex Hoppen
ec8957972a [Async Refactoring] Get semantics providing expr to decide if call is to completion handler
Resolves rdar://78011350
2021-07-05 17:41:39 +02:00
Alex Hoppen
3dcb08a369 [Async Refactoring] Cosmetic Improvements to "Add Async Wrapper" Refactoring
Two mostly-cosmetic change to the "Add Async Wrapper" refactoring
- Rename the continuation from `cont` to `continuation` and error from `err` to `error` to better match Swifts naming guidelines
- Add assertions that all result parameters are `nil` if an error is passed to the completion handler.

Resolves rdar://80172152
2021-07-05 13:34:27 +02:00
Alex Hoppen
54fcc90841 [Async Refactoring] Wrap code in a continuation if conversion doesn't yield reasonable results
If we are requested to convert a function to async, but the call in the function’s body that eventually calls the completion handler doesn’t have an async alternative, we are currently copying the call as-is, replacing any calls to the completion handler by placeholders.

For example,
```swift
func testDispatch(completionHandler: @escaping (Int) -> Void) {
  DispatchQueue.global.async {
     completionHandler(longSyncFunc())
  }
}
```
becomes
```swift
func testDispatch() async -> Int  {
  DispatchQueue.global.async {
     <#completionHandler#>(longSyncFunc())
  }
}
```

and

```swift
func testUrlSession(completionHandler: @escaping (Data) -> Void) {
  let task = URLSession.shared.dataTask(with: request) { data, response, error in
    completion(data!)
  }
  task.resume()
}
```
becomes
```swift
func testUrlSession() async -> Data {
  let task = URLSession.shared.dataTask(with: request) { data, response, error in
    <#completion#>(data!)
  }
  task.resume()
}
```

Both of these are better modelled using continuations. Thus, if we find an expression that contains a call to the completion handler and can’t be hoisted to an await statement, we are wrapping the rest of the current scope in a `withChecked(Throwing)Continuation`, producing the following results:

```swift
func testDispatch() async -> Int {
  return await withCheckedContinuation { (continuation: CheckedContinuation<Int, Never>) in
    DispatchQueue.global.async {
      continuation.resume(returning: syncComputation())
    }
  }
}
```

and

```swift
func testDataTask() async -> Int?
  return await withCheckedContinuation { (continuation: CheckedContinuation<Data, Never>) in
    let task = URLSession.shared.dataTask { data, response, error in
      continuation.resume(returning: data!)
    }
    task.resume()
  }
}
```

I think both are much closer to what the developer is actually expecting.

Resolves rdar://79304583
2021-07-02 15:48:41 +02:00
Alex Hoppen
7a68a1d834 Merge pull request #38191 from ahoppen/pr/implict-return-async-refactoring
[Async Refactoring] Add `return` keyword if wrapping `ReturnStmt` is implicit
2021-07-01 15:40:00 +02:00
Alex Hoppen
f7c7599a8c [Async Refactoring] Add return keyword if wrapping ReturnStmt is implicit
Previously, in the following case we were failing to add a `return` keyword inside `withImplicitReturn`.
```
func withImplicitReturn(completionHandler: (String) -> Void) {
  simple {
    completionHandler($0)
  }
}
```

This is because the call of `completionHandler($0)` is wrapped by an implicit `ReturnStmt` and thus we assumed that there was already a `return` keyword present.

Fix this issue by checking if the wrapping `ReturnStmt` is implicit and if it is, add the `return` keyword.

Fixes rdar://80009760
2021-07-01 10:21:46 +02:00
Hamish Knight
d05e79de9e Merge pull request #38139 from hamishknight/null-check 2021-06-29 13:41:52 +01:00
Hamish Knight
0ffc0ef3f8 [test] Re-enable convert_bool.swift
Make sure to build and include the mock overlays.

rdar://78879483
2021-06-28 17:27:43 +01:00
Hamish Knight
0d4eb978da [Async Refactoring] Add missing null type check
Don't crash if we have a boolean condition without
a type, as that may occur in invalid code.

rdar://79864182
2021-06-28 16:03:54 +01:00
Alex Hoppen
ffbbbaffca Merge pull request #38052 from ahoppen/pr/use-closure-label-for-async-return-type
[Refactoring] Use internal completion handler labels for async function's return type
2021-06-25 12:57:00 +02:00
Alex Hoppen
d944b8bd49 [Refactoring] Use internal completion handler labels for async function's return type
If a completion handler specifies internal parameter labels, we can use those to label the elements of the tuple returned by the async alternative.

For example
```swift
func foo(completion: (_ first: String, _ second: String) -> Void) { }
```
gets refactored to
```swift
func foo() async -> (first: String, second: String) { }
```

Resolves rdar://77268040
2021-06-24 08:00:28 +02:00
Hamish Knight
f5b00fcff3 [test] Fix a couple of RUN directives 2021-06-17 10:17:38 +01:00
Alex Hoppen
81b37fdb4c [Refactoring] Support refactoring for case let patterns
Previously we only supported `case` patterns that bound with a `let` inside the associated value like `case .success(let value)`. With this change, we also support `case let .success(value)`.

Fixes rdar://79279846 [SR-14772]
2021-06-14 12:31:01 +02:00
Hamish Knight
e97c781ca1 [Async Refactoring] Add @discardableResult for defaulted completion
If the completion handler parameter has a default
argument, mark the resulting async function as
`@discardableResult`, as the caller may not care
about the result.

rdar://79190170
2021-06-11 11:39:16 +01:00
Mishal Shah
5a7b8c7922 Merge pull request #37823 from apple/xcode-13-beta-main
[main] Support Xcode 13 beta
2021-06-10 09:21:09 -07:00
Hamish Knight
0c21310ab6 [Async Refactoring] Better handle known condition paths
If we're going to be classifying nodes in either
an `else if` block or after a `guard` statement
with a known condition path, be more lenient with
unhandled conditions, as we already know what path
they should be classified along.

rdar://78564388
2021-06-09 21:27:08 +01:00
Hamish Knight
6d34fd9ed1 Handle parent vars in async transform
If we're in a case stmt body, any DeclRefExprs
to vars bound by a pattern will actually be to an
implicit var decl that's declared for the body. We
therefore need to walk to its "parent" to get to
the var referenced by the pattern, which will have
the correct entries in the naming and placeholder
maps.
2021-06-09 10:43:25 +01:00
Hamish Knight
f28284dcc3 Better handle non-refutable patterns in async transform
Keep track of patterns that bind multiple vars and
print them out when converting an async call. If
the parameter being bound isn't referenced elsewhere,
we'll print the pattern inline as e.g:

```
let ((x, y), z) = await foo()
```

Otherwise, if the parameter is referenced elsewhere
in the block we'll print the pattern out of line,
such as:

```
let (res, z) = await foo()
let (x, y) = res
```

In addition, make sure to print var bindings out
of line if there's also a let binding, e.g:

```
let x = await foo()
var y = x
```

This ensures any mutations to y doesn't affect x.
If there's only a single var binding, we'll print
it inline.

rdar://77802560
2021-06-09 10:43:25 +01:00
Hamish Knight
2cfd2bf27d Don't handle unclassified switch cases for now
Previously we would silently add these to the
success block. For now, let's leave them
unhandled.
2021-06-09 10:43:24 +01:00
Hamish Knight
c818172df1 Don't handle refutable patterns in async transform
These aren't currently supported.
2021-06-09 10:43:24 +01:00
Hamish Knight
800a9c537c Add a couple of getSemanticsProviding calls 2021-06-09 10:43:24 +01:00
Mishal Shah
23c3b15f5f Support Xcode 13 beta
* Updating availability versions
* Remove all remaining overlays in stdlib/public/Darwin/*:
   - ObjectiveC
   - Dispatch
   - CoreFoundation
   - CoreGraphics
   - Foundation
2021-06-07 12:04:31 -07:00
Hamish Knight
0953737dd0 Merge pull request #37760 from hamishknight/walk-this-way
[ASTWalker] Walk ForEachStmt in source order
2021-06-04 10:07:41 +01:00
Ben Barham
717a932a3f [Utils] Make sure to write the rewritten file in refactor-check-compiles
`ClangFileRewriterHelper` only outputs the rewritten buffer when it's
destroyed, but the consumers were never being freed. Cleanup the
lifetime management of the stream and consumers so that they're properly
destroyed.

Rename `DuplicatingSourceEditConsumer` to
`BroadcastingSourceEditConsumer` to better describe its purpose.
2021-06-03 21:11:00 +10:00
Hamish Knight
a6bb071776 [ASTWalker] Walk ForEachStmt in source order
Make sure to visit the sequence expr before the
the where clause as the refactoring logic expects
nodes to be visited in source order.

rdar://78781061
2021-06-03 11:26:58 +01:00
Hamish Knight
446580ac5b Merge pull request #37713 from hamishknight/im-walkin-here 2021-06-01 11:57:08 +01:00
Hamish Knight
4197bc2325 [Async Refactoring] Fix crash on recursive AST walk
The async refactoring may perform recursive AST
walks in cases such as transforming the body of
a hoisted closure. Make sure this can be handled
by the logic tracking the ASTWalker in the
SourceEntityWalker, such that we don't crash when
later converting the call to a completion callback.

rdar://78693050
2021-05-31 21:47:31 +01:00
Alex Hoppen
58be2bfbea [Refactoring] Don't crash when converting a function to async that contains a call to init
We were trying to retrieve the name of all function calls in the body using `getBaseIdentifier`. But calls to `init` don’t have a base identifier, just a `DeclBaseName` (which is special). Work with the `DeclBaseName` internally to prevent the crash.

Fixes rdar://78024731 [SR-14637]
2021-05-31 12:13:07 +02:00
Hamish Knight
c8a28f4357 Merge pull request #37629 from hamishknight/ghost-of-objc-past 2021-05-28 15:37:25 +01:00
Hamish Knight
7df0786c38 [Refactoring] Support Obj-C style bool flag checks
Add an additional case to `CallbackCondition` to
support boolean checks, and add the necessary
classification logic to determine whether a bool
flag check is for a success or failure path.

The logic currently first checks to see if the
async alternative has a convention that specifies
which parameter the flag is, and whether it's a
success or failure flag. If so, it classifies the
flag check accordingly. If there's no async
convention specified, we use a heuristic to see if
the error parameter is force unwrapped in the
failure block. If so, we treat that as the error
path. If there's no force unwrap of the error, we
leave the condition unhandled.

rdar://74063899
2021-05-28 13:10:26 +01:00
Hamish Knight
39cc1cbb85 [Refactoring] Add some calls to getSemanticsProvidingExpr 2021-05-28 13:10:26 +01:00
Hamish Knight
8d92241ca9 [Refactoring] Support async for function extraction
Adapt the `ThrowingEntityAnalyzer` to pick up any
`await` keywords and add an `async` to the extracted
function if necessary along with an `await` for its
call.

rdar://72199949
2021-05-26 12:40:35 +01:00
Hamish Knight
3752965d8b [IDE] Don't walk into closures in ThrowingEntityAnalyzer
We don't want to consider any `try` exprs here.
2021-05-26 12:40:35 +01:00
Hamish Knight
e25c39c5a5 [Refactoring] Add try for extracted function call
If we're extracting a function that throws, add
a `try` to the call.
2021-05-26 12:40:34 +01:00
Hamish Knight
3aef3fc553 [test] Add test case for rdar://72199992 2021-05-25 16:08:29 +01:00