Skip to content

Commit 2a0c83d

Browse files
committed
Fix up effect specifiers in function signatures
1 parent b2ed797 commit 2a0c83d

File tree

3 files changed

+100
-54
lines changed

3 files changed

+100
-54
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 83 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,25 @@ 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

15201505
return RawFunctionSignatureSyntax(
15211506
input: input,
1522-
asyncOrReasyncKeyword: async,
1523-
throwsOrRethrowsKeyword: throwsKeyword,
1507+
asyncOrReasyncKeyword: asyncOrReasync,
1508+
throwsOrRethrowsKeyword: throwsOrRethrows,
1509+
unexpectedAfterThrows,
15241510
output: output,
15251511
arena: self.arena)
15261512
}
@@ -1553,8 +1539,8 @@ extension Parser {
15531539
let indices = self.parseParameterClause(for: .indices)
15541540

15551541
let result: RawReturnClauseSyntax
1556-
if self.at(.arrow) {
1557-
result = self.parseFunctionReturnClause().returnClause
1542+
if let handle = self.canRecoverTo(.arrow) {
1543+
result = self.parseFunctionReturnClause(handle).returnClause
15581544
} else {
15591545
result = RawReturnClauseSyntax(
15601546
arrow: RawTokenSyntax(missing: .arrow, arena: self.arena),
@@ -1728,33 +1714,14 @@ extension Parser {
17281714
attributes: attrs, modifier: modifier, kind: kind, token: introducer)
17291715
}
17301716

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
1741-
}
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
1717+
mutating func parseEffectsSpecifier() -> RawTokenSyntax? {
1718+
if let (_, handle) = self.at(anyIn: EffectsSpecifier.self) {
1719+
return self.eat(handle)
17511720
}
1752-
17531721
return nil
17541722
}
17551723

1756-
@_spi(RawSyntax)
1757-
public mutating func parseEffectsSpecifiers() -> [RawTokenSyntax] {
1724+
mutating func parseAllEffectsSpecifiers() -> [RawTokenSyntax] {
17581725
var specifiers = [RawTokenSyntax]()
17591726
var loopProgress = LoopProgressCondition()
17601727
while let specifier = self.parseEffectsSpecifier(), loopProgress.evaluate(currentToken) {
@@ -1763,6 +1730,68 @@ extension Parser {
17631730
return specifiers
17641731
}
17651732

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

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)