Skip to content

Closure Parsing Omnibus #727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions Sources/SwiftParser/Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1668,12 +1668,18 @@ extension Parser {
arena: self.arena))
} while keepGoing != nil && loopProgress.evaluate(currentToken)
}
// We were promised a right square bracket, so we're going to get it.
var unexpectedNodes = [RawSyntax]()
while !self.at(.eof) && !self.at(.rightSquareBracket) && !self.at(.inKeyword) {
unexpectedNodes.append(RawSyntax(self.consumeAnyToken()))
}
let (unexpectedBeforeRSquare, rsquare) = self.expect(.rightSquareBracket)
unexpectedNodes.append(contentsOf: unexpectedBeforeRSquare?.elements ?? [])

captures = RawClosureCaptureSignatureSyntax(
leftSquare: lsquare,
items: elements.isEmpty ? nil : RawClosureCaptureItemListSyntax(elements: elements, arena: self.arena),
unexpectedBeforeRSquare,
unexpectedNodes.isEmpty ? nil : RawUnexpectedNodesSyntax(elements: unexpectedNodes, arena: self.arena),
rightSquare: rsquare, arena: self.arena)
} else {
captures = nil
Expand All @@ -1694,15 +1700,17 @@ extension Parser {
// Parse identifier (',' identifier)*
var keepGoing: RawTokenSyntax? = nil
repeat {
let unexpected: RawUnexpectedNodesSyntax?
let name: RawTokenSyntax
if self.currentToken.isIdentifier {
unexpected = nil
name = self.consumeIdentifier()
} else {
name = self.eat(.wildcardKeyword)
(unexpected, name) = self.expect(.wildcardKeyword)
}
keepGoing = consume(if: .comma)
params.append(RawClosureParamSyntax(
name: name, trailingComma: keepGoing, arena: self.arena))
unexpected, name: name, trailingComma: keepGoing, arena: self.arena))
} while keepGoing != nil && loopProgress.evaluate(currentToken)
}

Expand Down Expand Up @@ -1751,10 +1759,14 @@ extension Parser {
specifiers.append(self.consumeIdentifier())
if let lparen = self.consume(if: .leftParen) {
specifiers.append(lparen)
specifiers.append(self.expectWithoutLookahead(.identifier, "unsafe"))
if self.currentToken.tokenText == "safe" {
specifiers.append(self.expectWithoutLookahead(.identifier, "safe"))
} else {
specifiers.append(self.expectWithoutLookahead(.identifier, "unsafe"))
}
specifiers.append(self.expectWithoutLookahead(.rightParen))
}
} else if (self.currentToken.isIdentifier || self.at(.selfKeyword)) {
} else if self.currentToken.isIdentifier || self.at(.selfKeyword) {
let next = self.peek()
// "x = 42", "x," and "x]" are all strong captures of x.
guard next.tokenKind == .equal || next.tokenKind == .comma
Expand Down Expand Up @@ -1991,7 +2003,6 @@ extension Parser.Lookahead {
mutating func consumeEffectsSpecifiers() {
var loopProgress = LoopProgressCondition()
while self.currentToken.isEffectsSpecifier
&& !self.currentToken.isAtStartOfLine
&& loopProgress.evaluate(currentToken) {
self.consumeAnyToken()
}
Expand Down
16 changes: 14 additions & 2 deletions Tests/SwiftParserTest/Assertions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,25 @@ struct DiagnosticSpec {
/// If not `nil`, assert that the diagnostic contains fix-its with these messages.
/// Use the `fixedSource` parameter on `AssertParse` to check that applying the Fix-It yields the expected result.
let fixIts: [String]?
let file: StaticString
let line: UInt

init(locationMarker: String = "DIAG", id: MessageID? = nil, message: String?, highlight: String? = nil, fixIts: [String]? = nil) {
init(
locationMarker: String = "DIAG",
id: MessageID? = nil,
message: String?,
highlight: String? = nil,
fixIts: [String]? = nil,
file: StaticString = #file,
line: UInt = #line
) {
self.locationMarker = locationMarker
self.id = id
self.message = message
self.highlight = highlight
self.fixIts = fixIts
self.file = file
self.line = line
}
}

Expand Down Expand Up @@ -232,7 +244,7 @@ func AssertParse<Node: RawSyntaxNodeProtocol>(
""", file: file, line: line)
}
for (diag, expectedDiag) in zip(diags, expectedDiagnostics) {
AssertDiagnostic(diag, in: tree, markerLocations: markerLocations, expected: expectedDiag, file: file, line: line)
AssertDiagnostic(diag, in: tree, markerLocations: markerLocations, expected: expectedDiag, file: expectedDiag.file, line: expectedDiag.line)
}

// Applying Fix-Its
Expand Down
2 changes: 1 addition & 1 deletion Tests/SwiftParserTest/Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ final class ExpressionTests: XCTestCase {
)
}

func testClouserExpression() {
func testClosureExpression() {
AssertParse(
"""
let :(#^DIAG_1^#..)->
Expand Down
21 changes: 21 additions & 0 deletions Tests/SwiftParserTest/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,25 @@ final class TypeTests: XCTestCase {
DiagnosticSpec(message: "Unexpected text '..' found in function type")
])
}

func testClosureSignatures() throws {
AssertParse("""
{ ()
throws -> Void in }
""",
{ $0.parseClosureExpression() })

AssertParse("""
{ [weak a, unowned(safe) self, b = 3] (a: Int, b: Int, _: Int) -> Int in }
""",
{ $0.parseClosureExpression() })

AssertParse("{[#^DIAG_1^#class]in#^DIAG_2^#",
{ $0.parseClosureExpression() },
diagnostics: [
DiagnosticSpec(locationMarker: "DIAG_1", message: "Expected '' in closure capture item"),
DiagnosticSpec(locationMarker: "DIAG_1", message: "Unexpected text 'class' found in closure capture signature"),
DiagnosticSpec(locationMarker: "DIAG_2", message: "Expected '}' to end closure"),
])
}
}