Skip to content

Commit

Permalink
Update parseType to support async / throwing closures
Browse files Browse the repository at this point in the history
  • Loading branch information
calda committed Jan 3, 2025
1 parent 6db7324 commit d31a6b5
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 3 deletions.
21 changes: 18 additions & 3 deletions Sources/ParsingHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,7 @@ extension Formatter {
/// - `[...]`
/// - `(...)`
/// - `Foo<...>`
/// - `(...) -> ...`
/// - `(...) (async|throws|throws(Error)) -> ...`
/// - `...?`
/// - `...!`
/// - `any ...`
Expand Down Expand Up @@ -1364,8 +1364,23 @@ extension Formatter {

// Parse types of the form `(...)` or `(...) -> ...`
if startToken == .startOfScope("("), let endOfScope = endOfScope(at: startOfTypeIndex) {
// Parse types of the form `(...) -> ...`
if let closureReturnIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: endOfScope),
// Parse types of the form `(...) (async|throws|throws(Error)) -> ...`.
// Look for the `->` token, skipping over any `async`, `throws`, or `throws(Error)`s.
let allowedTokensBeforeReturnArrow: [Token] = [.keyword("throws"), .identifier("async"), .startOfScope("(")]
var searchIndex = endOfScope
while let nextToken = index(of: .nonSpaceOrCommentOrLinebreak, after: searchIndex),
allowedTokensBeforeReturnArrow.contains(tokens[nextToken])
{
// Skip over any tokens inside parens
if tokens[nextToken].isStartOfScope, let endOfScope = self.endOfScope(at: nextToken) {
searchIndex = endOfScope
} else {
searchIndex = nextToken
}
}

// If we find a return arrow, this is a closure with a return type.
if let closureReturnIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: searchIndex),
tokens[closureReturnIndex] == .operator("->", .infix),
let returnTypeIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: closureReturnIndex),
let returnTypeRange = parseType(at: returnTypeIndex)?.range
Expand Down
35 changes: 35 additions & 0 deletions Tests/ParsingHelpersTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,41 @@ class ParsingHelpersTests: XCTestCase {
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) -> (Foo, Bar)")
}

func testParseThrowingClosureType() {
let formatter = Formatter(tokenize("""
let foo: (Foo, Bar) throws -> Void
"""))
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) throws -> Void")
}

func testParseTypedThrowingClosureType() {
let formatter = Formatter(tokenize("""
let foo: (Foo, Bar) throws(MyFeatureError) -> Void
"""))
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) throws(MyFeatureError) -> Void")
}

func testParseAsyncClosureType() {
let formatter = Formatter(tokenize("""
let foo: (Foo, Bar) async -> Void
"""))
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) async -> Void")
}

func testParseAsyncThrowsClosureType() {
let formatter = Formatter(tokenize("""
let foo: (Foo, Bar) async throws -> Void
"""))
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) async throws -> Void")
}

func testParseTypedAsyncThrowsClosureType() {
let formatter = Formatter(tokenize("""
let foo: (Foo, Bar) async throws(MyCustomError) -> Void
"""))
XCTAssertEqual(formatter.parseType(at: 5)?.name, "(Foo, Bar) async throws(MyCustomError) -> Void")
}

func testParseClosureTypeWithOwnership() {
let formatter = Formatter(tokenize("""
let foo: (consuming Foo, borrowing Bar) -> (Foo, Bar) = { foo, bar in (foo, bar) }
Expand Down

0 comments on commit d31a6b5

Please sign in to comment.