Skip to content

Commit 14c8cd8

Browse files
committed
Fix up effect specifiers in function signatures
1 parent b2ed797 commit 14c8cd8

File tree

3 files changed

+103
-54
lines changed

3 files changed

+103
-54
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 86 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,26 +1391,19 @@ extension Parser {
13911391
arena: self.arena)
13921392
}
13931393

1394-
/// If a `throws` keyword appears right in front of the `arrow`, it is returned as `misplacedThrowsKeyword` so it can be synthesized in front of the arrow.
1394+
/// If effect specifiers appear in front of the `arrow` or after the return type, they are returned so that they can be synthesized in front of the arrow.
13951395
@_spi(RawSyntax)
1396-
public mutating func parseFunctionReturnClause() -> (returnClause: RawReturnClauseSyntax, misplacedThrowsKeyword: RawTokenSyntax?) {
1397-
let (unexpectedBeforeArrow, arrow) = self.expect(.arrow)
1398-
var misplacedThrowsKeyword: RawTokenSyntax? = nil
1399-
let unexpectedBeforeReturnType: RawUnexpectedNodesSyntax?
1400-
if let throwsKeyword = self.consume(ifAny: [.rethrowsKeyword, .throwsKeyword]) {
1401-
misplacedThrowsKeyword = throwsKeyword
1402-
unexpectedBeforeReturnType = RawUnexpectedNodesSyntax(elements: [RawSyntax(throwsKeyword)], arena: self.arena)
1403-
} else {
1404-
unexpectedBeforeReturnType = nil
1405-
}
1396+
public mutating func parseFunctionReturnClause(_ handle: RecoveryConsumptionHandle) -> (returnClause: RawReturnClauseSyntax, misplacedEffects: RawUnexpectedNodesSyntax?) {
1397+
let (unexpectedBeforeArrow, arrow) = self.eat(handle)
1398+
let unexpectedBeforeReturnType = RawUnexpectedNodesSyntax(unexpectedTokens: parseAllEffectsSpecifiers(), arena: self.arena)
14061399
let result = self.parseResultType()
14071400
let returnClause = RawReturnClauseSyntax(
14081401
unexpectedBeforeArrow,
14091402
arrow: arrow,
14101403
unexpectedBeforeReturnType,
14111404
returnType: result,
14121405
arena: self.arena)
1413-
return (returnClause, misplacedThrowsKeyword)
1406+
return (returnClause, unexpectedBeforeReturnType)
14141407
}
14151408
}
14161409

@@ -1495,32 +1488,27 @@ extension Parser {
14951488
public mutating func parseFunctionSignature() -> RawFunctionSignatureSyntax {
14961489
let input = self.parseParameterClause(for: .functionParameters)
14971490

1498-
let async: RawTokenSyntax?
1499-
if let asyncTok = self.consumeIfContextualKeyword("async") {
1500-
async = asyncTok
1501-
} else if let reasync = self.consumeIfContextualKeyword("reasync") {
1502-
async = reasync
1503-
} else {
1504-
async = nil
1505-
}
1506-
1507-
var throwsKeyword = self.consume(ifAny: [.throwsKeyword, .rethrowsKeyword])
1491+
var asyncOrReasync: RawTokenSyntax?
1492+
var throwsOrRethrows: RawTokenSyntax?
1493+
let unexpectedAfterThrows: RawUnexpectedNodesSyntax?
1494+
(asyncOrReasync, throwsOrRethrows, unexpectedAfterThrows) = parseExpectedEffectsSpecifiers()
15081495

15091496
let output: RawReturnClauseSyntax?
1510-
if self.at(.arrow) {
1511-
let result = self.parseFunctionReturnClause()
1497+
if let handle = self.canRecoverTo(.arrow) {
1498+
let result = self.parseFunctionReturnClause(handle)
15121499
output = result.returnClause
1513-
if let misplacedThrowsKeyword = result.misplacedThrowsKeyword, throwsKeyword == nil {
1514-
throwsKeyword = RawTokenSyntax(missing: misplacedThrowsKeyword.tokenKind, arena: self.arena)
1515-
}
1500+
addMissingEffectSpecifiers(from: result.misplacedEffects, asyncOrReasync: &asyncOrReasync, throwsOrRethrows: &throwsOrRethrows)
15161501
} else {
15171502
output = nil
15181503
}
15191504

1505+
// Check for more specifiers?
1506+
15201507
return RawFunctionSignatureSyntax(
15211508
input: input,
1522-
asyncOrReasyncKeyword: async,
1523-
throwsOrRethrowsKeyword: throwsKeyword,
1509+
asyncOrReasyncKeyword: asyncOrReasync,
1510+
throwsOrRethrowsKeyword: throwsOrRethrows,
1511+
unexpectedAfterThrows,
15241512
output: output,
15251513
arena: self.arena)
15261514
}
@@ -1553,8 +1541,8 @@ extension Parser {
15531541
let indices = self.parseParameterClause(for: .indices)
15541542

15551543
let result: RawReturnClauseSyntax
1556-
if self.at(.arrow) {
1557-
result = self.parseFunctionReturnClause().returnClause
1544+
if let handle = self.canRecoverTo(.arrow) {
1545+
result = self.parseFunctionReturnClause(handle).returnClause
15581546
} else {
15591547
result = RawReturnClauseSyntax(
15601548
arrow: RawTokenSyntax(missing: .arrow, arena: self.arena),
@@ -1728,33 +1716,14 @@ extension Parser {
17281716
attributes: attrs, modifier: modifier, kind: kind, token: introducer)
17291717
}
17301718

1731-
@_spi(RawSyntax)
1732-
public mutating func parseEffectsSpecifier() -> RawTokenSyntax? {
1733-
// 'async'
1734-
if let async = self.consumeIfContextualKeyword("async") {
1735-
return async
1736-
}
1737-
1738-
// 'reasync'
1739-
if let reasync = self.consumeIfContextualKeyword("reasync") {
1740-
return reasync
1719+
mutating func parseEffectsSpecifier() -> RawTokenSyntax? {
1720+
if let (_, handle) = self.at(anyIn: EffectsSpecifier.self) {
1721+
return self.eat(handle)
17411722
}
1742-
1743-
// 'throws'/'rethrows'
1744-
if let throwsRethrows = self.consume(ifAny: [.throwsKeyword, .rethrowsKeyword]) {
1745-
return throwsRethrows
1746-
}
1747-
1748-
// diagnose 'throw'/'try'.
1749-
if let throwTry = self.consume(ifAny: [.throwKeyword, .tryKeyword], where: { !$0.isAtStartOfLine }) {
1750-
return throwTry
1751-
}
1752-
17531723
return nil
17541724
}
17551725

1756-
@_spi(RawSyntax)
1757-
public mutating func parseEffectsSpecifiers() -> [RawTokenSyntax] {
1726+
mutating func parseAllEffectsSpecifiers() -> [RawTokenSyntax] {
17581727
var specifiers = [RawTokenSyntax]()
17591728
var loopProgress = LoopProgressCondition()
17601729
while let specifier = self.parseEffectsSpecifier(), loopProgress.evaluate(currentToken) {
@@ -1763,6 +1732,69 @@ extension Parser {
17631732
return specifiers
17641733
}
17651734

1735+
private func addMissingEffectSpecifiers(from unexpectedTokens: RawUnexpectedNodesSyntax?, asyncOrReasync: inout RawTokenSyntax?, throwsOrRethrows: inout RawTokenSyntax?) {
1736+
guard let unexpectedTokens = unexpectedTokens else {
1737+
return
1738+
}
1739+
1740+
for unexpectedToken in unexpectedTokens.elements {
1741+
guard asyncOrReasync == nil || throwsOrRethrows == nil, let specifier = unexpectedToken.as(RawTokenSyntax.self) else {
1742+
break
1743+
}
1744+
1745+
switch specifier.tokenKind {
1746+
case .contextualKeyword:
1747+
if asyncOrReasync == nil, specifier.tokenText == "async" || specifier.tokenText == "reasync" {
1748+
asyncOrReasync = RawTokenSyntax(missing: specifier.tokenKind, text: specifier.tokenText, arena: self.arena)
1749+
}
1750+
break
1751+
case .throwsKeyword:
1752+
fallthrough
1753+
case .rethrowsKeyword:
1754+
if throwsOrRethrows == nil {
1755+
throwsOrRethrows = RawTokenSyntax(missing: specifier.tokenKind, text: specifier.tokenText, arena: self.arena)
1756+
}
1757+
break
1758+
default:
1759+
break
1760+
}
1761+
}
1762+
}
1763+
1764+
mutating func parseExpectedEffectsSpecifiers() -> (asyncOrReasync: RawTokenSyntax?, throwsOrRethrows: RawTokenSyntax?, unexpected: RawUnexpectedNodesSyntax?) {
1765+
guard let firstSpecifier = parseEffectsSpecifier() else {
1766+
return (nil, nil, nil)
1767+
}
1768+
1769+
var asyncOrReasync: RawTokenSyntax? = nil
1770+
var throwsOrRethrows: RawTokenSyntax? = nil
1771+
var unexpectedTokens = [RawTokenSyntax]()
1772+
1773+
// Not a fan of the number of places contextual + async/reasync is checked (or throws/rethrows for that matter). Can we use EffectSpecifier somehow?
1774+
if firstSpecifier.tokenKind == .contextualKeyword,
1775+
firstSpecifier.tokenText == "async" || firstSpecifier.tokenText == "reasync" {
1776+
asyncOrReasync = firstSpecifier
1777+
1778+
if let secondSpecifier = parseEffectsSpecifier() {
1779+
if secondSpecifier.tokenKind == .throwsKeyword || secondSpecifier.tokenKind == .rethrowsKeyword {
1780+
throwsOrRethrows = firstSpecifier
1781+
} else {
1782+
unexpectedTokens.append(secondSpecifier)
1783+
}
1784+
}
1785+
} else if firstSpecifier.tokenKind == .throwsKeyword || firstSpecifier.tokenKind == .rethrowsKeyword {
1786+
throwsOrRethrows = firstSpecifier
1787+
} else {
1788+
unexpectedTokens.append(firstSpecifier)
1789+
}
1790+
unexpectedTokens.append(contentsOf: parseAllEffectsSpecifiers())
1791+
1792+
let unexpected = RawUnexpectedNodesSyntax(unexpectedTokens: unexpectedTokens, arena: self.arena)
1793+
addMissingEffectSpecifiers(from: unexpected, asyncOrReasync: &asyncOrReasync, throwsOrRethrows: &throwsOrRethrows)
1794+
1795+
return (asyncOrReasync, throwsOrRethrows, unexpected)
1796+
}
1797+
17661798
/// Parse the body of a variable declaration. This can include explicit
17671799
/// getters, setters, and observers, or the body of a computed property.
17681800
///

Sources/SwiftParser/RawTokenKindSubset.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,15 @@ enum EffectsSpecifier: RawTokenKindSubset {
418418
default: return nil
419419
}
420420
}
421+
422+
var remappedKind: RawTokenKind? {
423+
switch self {
424+
case .asyncContextualKeyword: return .contextualKeyword
425+
case .awaitContextualKeyword: return .contextualKeyword
426+
case .reasyncContextualKeyword: return .contextualKeyword
427+
default: return nil
428+
}
429+
}
421430
}
422431

423432
enum IdentifierTokens: RawTokenKindSubset {

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,3 +774,11 @@ extension RawSyntax {
774774
}
775775
}
776776

777+
extension RawUnexpectedNodesSyntax {
778+
public init?(unexpectedTokens: [RawTokenSyntax], arena: SyntaxArena) {
779+
if unexpectedTokens.isEmpty {
780+
return nil
781+
}
782+
self.init(elements: unexpectedTokens.map {RawSyntax($0)}, arena: arena)
783+
}
784+
}

0 commit comments

Comments
 (0)