Repository: swiftlang/swift-syntax (swift)

Issue:
swift-format fails to parse valid swift code
The following snippet fails to parse in `swift-format` but `swiftc` is able to compile it:
```swift
func foo<E: Error, T>(a: T, b: T, body: (T, T) throws(E) -> T) throws(E) -> T {
  try body(a, b)
}

struct S: Error { }

let x = try foo(a: 0, b: 1) { a, b throws(S) in 2 }
print(x)
```

repro:

```console
# compile
$ xcrun -toolchain org.swift.59202405011a swiftc test.swift
$ ./test
2

# format
$ xcrun -toolchain org.swift.59202405011a swift-format format test.swift
/Users/rauhul/Desktop/test.swift:7:32: error: unexpected code ', b throws(S) in 2' in closure
```

Pull request description:
Lookahead was not consuming typed throw's error argument during lookahead for closures. `atFunctionTypeArrow` did mostly handle it, but only consumed a single token and thus wouldn't match `throws(any Error)`. Handle this in `consumeEffectsSpecifiers` and then use this for both.

Fixes #2648.
Resolves rdar://127750606.

Patch:
diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift
index 7c560368694..2a7535d4fb2 100644
--- a/Sources/SwiftParser/Expressions.swift
+++ b/Sources/SwiftParser/Expressions.swift
@@ -2428,10 +2428,15 @@ extension Parser.Lookahead {
   // Consume 'async', 'throws', and 'rethrows', but in any order.
   mutating func consumeEffectsSpecifiers() {
     var loopProgress = LoopProgressCondition()
-    while let (_, handle) = self.at(anyIn: EffectSpecifier.self),
+    while let (spec, handle) = self.at(anyIn: EffectSpecifier.self),
       self.hasProgressed(&loopProgress)
     {
       self.eat(handle)
+
+      if spec.isThrowsSpecifier, self.consume(if: .leftParen) != nil {
+        _ = self.canParseSimpleOrCompositionType()
+        self.consume(if: .rightParen)
+      }
     }
   }
 
diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift
index 133ecccd43f..73be3d11c53 100644
--- a/Sources/SwiftParser/Types.swift
+++ b/Sources/SwiftParser/Types.swift
@@ -37,7 +37,7 @@ extension Parser {
   mutating func parseTypeScalar(misplacedSpecifiers: [RawTokenSyntax] = []) -> RawTypeSyntax {
     let specifiersAndAttributes = self.parseTypeAttributeList(misplacedSpecifiers: misplacedSpecifiers)
     var base = RawTypeSyntax(self.parseSimpleOrCompositionType())
-    if self.withLookahead({ $0.atFunctionTypeArrow() }) {
+    if self.withLookahead({ $0.canParseFunctionTypeArrow() }) {
       var effectSpecifiers = self.parseTypeEffectSpecifiers()
       let returnClause = self.parseFunctionReturnClause(
         effectSpecifiers: &effectSpecifiers,
@@ -656,21 +656,9 @@ extension Parser.Lookahead {
       return false
     }
 
-    if self.atFunctionTypeArrow() {
-      // Handle type-function if we have an '->' with optional
-      // 'async' and/or 'throws'.
-      var loopProgress = LoopProgressCondition()
-      while let (_, handle) = self.at(anyIn: EffectSpecifier.self), self.hasProgressed(&loopProgress) {
-        self.eat(handle)
-      }
-
-      guard self.consume(if: .arrow) != nil else {
-        return false
-      }
-
+    if self.canParseFunctionTypeArrow() {
       return self.canParseType()
     }
-
     return true
   }
 
@@ -819,33 +807,12 @@ extension Parser.Lookahead {
     return self.consume(if: .rightParen) != nil
   }
 
-  mutating func atFunctionTypeArrow() -> Bool {
-    if self.at(.arrow) {
+  mutating func canParseFunctionTypeArrow() -> Bool {
+    if self.consume(if: .arrow) != nil {
       return true
     }
-
-    if let effect = self.at(anyIn: EffectSpecifier.self) {
-      if self.peek().rawTokenKind == .arrow {
-        return true
-      }
-
-      if effect.spec.isThrowsSpecifier && self.peek().rawTokenKind == .leftParen {
-        var lookahead = self.lookahead()
-        lookahead.consumeAnyToken()
-        lookahead.skipSingle()
-        return lookahead.atFunctionTypeArrow()
-      }
-
-      if peek(isAtAnyIn: EffectSpecifier.self) != nil {
-        var lookahead = self.lookahead()
-        lookahead.consumeAnyToken()
-        return lookahead.atFunctionTypeArrow()
-      }
-
-      return false
-    }
-
-    return false
+    self.consumeEffectsSpecifiers()
+    return self.consume(if: .arrow) != nil
   }
 
   mutating func canParseTypeIdentifier(allowKeyword: Bool = false) -> Bool {


Test patch:
diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift
index eba62a0aec5..786c61bda43 100644
--- a/Tests/SwiftParserTest/ExpressionTests.swift
+++ b/Tests/SwiftParserTest/ExpressionTests.swift
@@ -2972,6 +2972,9 @@ final class StatementExpressionTests: ParserTestCase {
     assertParse(
       "[() throws(MyError) -> Void]()"
     )
+    assertParse(
+      "[() throws(any Error) -> Void]()"
+    )
     assertParse(
       "X<() throws(MyError) -> Int>()"
     )
@@ -2980,6 +2983,22 @@ final class StatementExpressionTests: ParserTestCase {
     )
   }
 
+  func testTypedThrowsClosureParam() {
+    assertParse(
+      """
+      try foo { (a, b) throws(S) in 1 }
+      """
+    )
+  }
+
+  func testTypedThrowsShorthandClosureParams() {
+    assertParse(
+      """
+      try foo { a, b throws(S) in 1 }
+      """
+    )
+  }
+
   func testArrayExprWithNoCommas() {
     assertParse("[() ()]")