Skip to content

Commit a1d5d0c

Browse files
caldanicklockwood
authored andcommitted
Update parseType to support async / throwing closures (#1955)
1 parent 014c12c commit a1d5d0c

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

Sources/ParsingHelpers.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ extension Formatter {
12641264
/// - `[...]`
12651265
/// - `(...)`
12661266
/// - `Foo<...>`
1267-
/// - `(...) -> ...`
1267+
/// - `(...) (async|throws|throws(Error)) -> ...`
12681268
/// - `...?`
12691269
/// - `...!`
12701270
/// - `any ...`
@@ -1364,8 +1364,23 @@ extension Formatter {
13641364

13651365
// Parse types of the form `(...)` or `(...) -> ...`
13661366
if startToken == .startOfScope("("), let endOfScope = endOfScope(at: startOfTypeIndex) {
1367-
// Parse types of the form `(...) -> ...`
1368-
if let closureReturnIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: endOfScope),
1367+
// Parse types of the form `(...) (async|throws|throws(Error)) -> ...`.
1368+
// Look for the `->` token, skipping over any `async`, `throws`, or `throws(Error)`s.
1369+
let allowedTokensBeforeReturnArrow: [Token] = [.keyword("throws"), .identifier("async"), .startOfScope("(")]
1370+
var searchIndex = endOfScope
1371+
while let nextToken = index(of: .nonSpaceOrCommentOrLinebreak, after: searchIndex),
1372+
allowedTokensBeforeReturnArrow.contains(tokens[nextToken])
1373+
{
1374+
// Skip over any tokens inside parens
1375+
if tokens[nextToken].isStartOfScope, let endOfScope = self.endOfScope(at: nextToken) {
1376+
searchIndex = endOfScope
1377+
} else {
1378+
searchIndex = nextToken
1379+
}
1380+
}
1381+
1382+
// If we find a return arrow, this is a closure with a return type.
1383+
if let closureReturnIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: searchIndex),
13691384
tokens[closureReturnIndex] == .operator("->", .infix),
13701385
let returnTypeIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: closureReturnIndex),
13711386
let returnTypeRange = parseType(at: returnTypeIndex)?.range

Tests/ParsingHelpersTests.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,41 @@ class ParsingHelpersTests: XCTestCase {
20902090
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) -> (Foo, Bar)")
20912091
}
20922092

2093+
func testParseThrowingClosureType() {
2094+
let formatter = Formatter(tokenize("""
2095+
let foo: (Foo, Bar) throws -> Void
2096+
"""))
2097+
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) throws -> Void")
2098+
}
2099+
2100+
func testParseTypedThrowingClosureType() {
2101+
let formatter = Formatter(tokenize("""
2102+
let foo: (Foo, Bar) throws(MyFeatureError) -> Void
2103+
"""))
2104+
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) throws(MyFeatureError) -> Void")
2105+
}
2106+
2107+
func testParseAsyncClosureType() {
2108+
let formatter = Formatter(tokenize("""
2109+
let foo: (Foo, Bar) async -> Void
2110+
"""))
2111+
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) async -> Void")
2112+
}
2113+
2114+
func testParseAsyncThrowsClosureType() {
2115+
let formatter = Formatter(tokenize("""
2116+
let foo: (Foo, Bar) async throws -> Void
2117+
"""))
2118+
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) async throws -> Void")
2119+
}
2120+
2121+
func testParseTypedAsyncThrowsClosureType() {
2122+
let formatter = Formatter(tokenize("""
2123+
let foo: (Foo, Bar) async throws(MyCustomError) -> Void
2124+
"""))
2125+
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) async throws(MyCustomError) -> Void")
2126+
}
2127+
20932128
func testParseClosureTypeWithOwnership() {
20942129
let formatter = Formatter(tokenize("""
20952130
let foo: (consuming Foo, borrowing Bar) -> (Foo, Bar) = { foo, bar in (foo, bar) }

0 commit comments

Comments
 (0)