<commit_before># Forward-scan matching for trailing closures

* Proposal: [SE-NNNN](NNNN-forward-scan-trailing-closures.md)
* Authors: [<NAME>](https://github.com/DougGregor)
* Review Manager: TBD
* Status: **Awaiting implementation**
* Implementation: [apple/swift#32891](https://github.com/apple/swift/pull/32891)
* Toolchains: [Linux](https://ci.swift.org/job/swift-PR-toolchain-Linux/389//artifact/branch-master/swift-PR-32891-389-ubuntu16.04.tar.gz), [macOS](https://ci.swift.org/job/swift-PR-toolchain-osx/558//artifact/branch-master/swift-PR-32891-558-osx.tar.gz)
* Discussion: [Pitch #2](https://forums.swift.org/t/pitch-2-forward-scan-matching-for-trailing-closures/38491), [Pitch #1](https://forums.swift.org/t/pitch-1-forward-scan-matching-for-trailing-closures-source-breaking/38162)

## Introduction

[SE-0279 "Multiple Trailing Closures"](https://github.com/apple/swift-evolution/blob/master/proposals/0279-multiple-trailing-closures.md) threaded the needle between getting the syntax we wanted for multiple trailing closures without breaking source compatibility. One aspect of that compromise was to extend (rather than replace) the existing rule for matching a trailing closure to a parameter by scanning *backward* from the end of the parameter list.

However, the backward-scan matching rule makes it hard to write good API that uses trailing closures, especially multiple trailing closures. This proposal replaces the backward scan with a forward scan, which is simpler, more in line with normal argument matching in a call, and works better for APIs that support trailing closures (whether single or multiple) and default arguments. This change introduces a *minor source break* for code involving multiple, defaulted closure parameters, but the practical impacts of this source break are low, and in its current state SE-0279 multiple trailing closures aren't very useful.

## Motivation

Several folks noted the downsides of the "backward" matching rule. The rule itself is described in the [detailed design](https://github.com/apple/swift-evolution/blob/master/proposals/0279-multiple-trailing-closures.md#detailed-design) section of SE-0279 (search for "backward"). To understand the problem with the backward rule, let's try to declare the UIView [`animate(withDuration:animations:completion:)`](https://developer.apple.com/documentation/uikit/uiview/1622515-animate) method in the obvious way to make use of SE-0279:
 
```swift
class func animate(
    withDuration duration: TimeInterval, 
    animations: @escaping () -> Void, 
    completion: ((Bool) -> Void)? = nil
)
```

SE-0279 matches the named trailing closure arguments backward, matching the last (labeled) trailing closure argument from the back of the parameter list, then proceeding to move to earlier trailing closures and function parameters. Consider the following example (straight from SE-0279):

```swift
UIView.animate(withDuration: 0.3) {
  self.view.alpha = 0
} completion: { _ in
  self.view.removeFromSuperview()
}
```

The `completion:` trailing closure matches the last parameter, and the unnamed trailing closure matches `animations:`. The backward rule worked fine here.

However, things fall apart when a single (therefore unnamed) trailing closure is provided to this API:

```swift
UIView.animate(withDuration: 0.3) {
  self.view.alpha = 0
}
```

Now, the backward rule matches the unnamed trailing closure to `completion:`. The compiler produces an error:

```
error: missing argument for parameter 'animations' in call
  animate(withDuration: 0.3) {
```

Note that the "real" UIView API actually has two different methods---`animate(withDuration:animations:completion:)` and `animate(withDuration:animations:)`---where the latter looks like this:

```swift
class func animate(
    withDuration duration: TimeInterval, 
    animations: @escaping () -> Void
)
```

This second overload only has a closure argument, so the backward-matching rule handles the single-trailing-closure case. These overloads exist because these UIView APIs were imported from Objective-C, which does not have default arguments. A new Swift API would not be written this way---except that SE-0279 forces it due to the backward-matching rule.

## Proposed solution

The idea of the forward-scan matching rule is to match trailing closure arguments to parameters in the same forward, left-to-right manner that other arguments are matched to parameters. The unlabeled trailing closure will be matched to the next parameter that is either unlabeled or has a declared type that structurally resembles a function type (defined below). For the example above, this means the following:

```swift
UIView.animate(withDuration: 0.3) {
  self.view.alpha = 0
}
// equivalent to
UIView.animate(withDuration: 0.3, animations: {
  self.view.alpha = 0
})
```

and

```swift
UIView.animate(withDuration: 0.3) {
  self.view.alpha = 0
} completion: { _ in
  self.view.removeFromSuperview()
}
// equivalent to
UIView.animate(withDuration: 0.3, animation: {
  self.view.alpha = 0
}, completion: { _ in
  self.view.removeFromSuperview()
})<commit_msg>[Forward scan trailing closures] Fix one more "animation:"<commit_after># Forward-scan matching for trailing closures

* Proposal: [SE-NNNN](NNNN-forward-scan-trailing-closures.md)
* Authors: [<NAME>](https://github.com/DougGregor)
* Review Manager: TBD
* Status: **Awaiting implementation**
* Implementation: [apple/swift#32891](https://github.com/apple/swift/pull/32891)
* Toolchains: [Linux](https://ci.swift.org/job/swift-PR-toolchain-Linux/389//artifact/branch-master/swift-PR-32891-389-ubuntu16.04.tar.gz), [macOS](https://ci.swift.org/job/swift-PR-toolchain-osx/558//artifact/branch-master/swift-PR-32891-558-osx.tar.gz)
* Discussion: [Pitch #2](https://forums.swift.org/t/pitch-2-forward-scan-matching-for-trailing-closures/38491), [Pitch #1](https://forums.swift.org/t/pitch-1-forward-scan-matching-for-trailing-closures-source-breaking/38162)

## Introduction

[SE-0279 "Multiple Trailing Closures"](https://github.com/apple/swift-evolution/blob/master/proposals/0279-multiple-trailing-closures.md) threaded the needle between getting the syntax we wanted for multiple trailing closures without breaking source compatibility. One aspect of that compromise was to extend (rather than replace) the existing rule for matching a trailing closure to a parameter by scanning *backward* from the end of the parameter list.

However, the backward-scan matching rule makes it hard to write good API that uses trailing closures, especially multiple trailing closures. This proposal replaces the backward scan with a forward scan, which is simpler, more in line with normal argument matching in a call, and works better for APIs that support trailing closures (whether single or multiple) and default arguments. This change introduces a *minor source break* for code involving multiple, defaulted closure parameters, but the practical impacts of this source break are low, and in its current state SE-0279 multiple trailing closures aren't very useful.

## Motivation

Several folks noted the downsides of the "backward" matching rule. The rule itself is described in the [detailed design](https://github.com/apple/swift-evolution/blob/master/proposals/0279-multiple-trailing-closures.md#detailed-design) section of SE-0279 (search for "backward"). To understand the problem with the backward rule, let's try to declare the UIView [`animate(withDuration:animations:completion:)`](https://developer.apple.com/documentation/uikit/uiview/1622515-animate) method in the obvious way to make use of SE-0279:
 
```swift
class func animate(
    withDuration duration: TimeInterval, 
    animations: @escaping () -> Void, 
    completion: ((Bool) -> Void)? = nil
)
```

SE-0279 matches the named trailing closure arguments backward, matching the last (labeled) trailing closure argument from the back of the parameter list, then proceeding to move to earlier trailing closures and function parameters. Consider the following example (straight from SE-0279):

```swift
UIView.animate(withDuration: 0.3) {
  self.view.alpha = 0
} completion: { _ in
  self.view.removeFromSuperview()
}
```

The `completion:` trailing closure matches the last parameter, and the unnamed trailing closure matches `animations:`. The backward rule worked fine here.

However, things fall apart when a single (therefore unnamed) trailing closure is provided to this API:

```swift
UIView.animate(withDuration: 0.3) {
  self.view.alpha = 0
}
```

Now, the backward rule matches the unnamed trailing closure to `completion:`. The compiler produces an error:

```
error: missing argument for parameter 'animations' in call
  animate(withDuration: 0.3) {
```

Note that the "real" UIView API actually has two different methods---`animate(withDuration:animations:completion:)` and `animate(withDuration:animations:)`---where the latter looks like this:

```swift
class func animate(
    withDuration duration: TimeInterval, 
    animations: @escaping () -> Void
)
```

This second overload only has a closure argument, so the backward-matching rule handles the single-trailing-closure case. These overloads exist because these UIView APIs were imported from Objective-C, which does not have default arguments. A new Swift API would not be written this way---except that SE-0279 forces it due to the backward-matching rule.

## Proposed solution

The idea of the forward-scan matching rule is to match trailing closure arguments to parameters in the same forward, left-to-right manner that other arguments are matched to parameters. The unlabeled trailing closure will be matched to the next parameter that is either unlabeled or has a declared type that structurally resembles a function type (defined below). For the example above, this means the following:

```swift
UIView.animate(withDuration: 0.3) {
  self.view.alpha = 0
}
// equivalent to
UIView.animate(withDuration: 0.3, animations: {
  self.view.alpha = 0
})
```

and

```swift
UIView.animate(withDuration: 0.3) {
  self.view.alpha = 0
} completion: { _ in
  self.view.removeFromSuperview()
}
// equivalent to
UIView.animate(withDuration: 0.3, animations: {
  self.view.alpha = 0
}, completion: { _ in
  self.view.removeFromSuperview()
})
{'fraction_non_alphanumeric': 0.09861760124610591, 'fraction_numerical': 0.022001557632398753, 'mean_word_length': 4.931293302540416, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 18, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '668541', 'n_tokens_mistral': 3112, 'n_tokens_neox': 2736, 'n_words': 1073}