Skip to content

Commit ee7691a

Browse files
committed
Fix up effect specifiers
1 parent 68e5315 commit ee7691a

File tree

8 files changed

+219
-148
lines changed

8 files changed

+219
-148
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 90 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,26 +1440,18 @@ extension Parser {
14401440
arena: self.arena)
14411441
}
14421442

1443-
/// 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.
1444-
@_spi(RawSyntax)
1445-
public mutating func parseFunctionReturnClause() -> (returnClause: RawReturnClauseSyntax, misplacedThrowsKeyword: RawTokenSyntax?) {
1446-
let (unexpectedBeforeArrow, arrow) = self.expect(.arrow)
1447-
var misplacedThrowsKeyword: RawTokenSyntax? = nil
1448-
let unexpectedBeforeReturnType: RawUnexpectedNodesSyntax?
1449-
if let throwsKeyword = self.consume(ifAny: [.rethrowsKeyword, .throwsKeyword]) {
1450-
misplacedThrowsKeyword = throwsKeyword
1451-
unexpectedBeforeReturnType = RawUnexpectedNodesSyntax(elements: [RawSyntax(throwsKeyword)], arena: self.arena)
1452-
} else {
1453-
unexpectedBeforeReturnType = nil
1454-
}
1443+
/// 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.
1444+
mutating func parseReturnClause(_ handle: RecoveryConsumptionHandle) -> (returnClause: RawReturnClauseSyntax, misplacedSpecifiers: [SpecifierWithToken]) {
1445+
let (unexpectedBeforeArrow, arrow) = self.eat(handle)
1446+
let misplacedSpecifiers = parseAllEffectsSpecifiers()
14551447
let result = self.parseResultType()
14561448
let returnClause = RawReturnClauseSyntax(
14571449
unexpectedBeforeArrow,
14581450
arrow: arrow,
1459-
unexpectedBeforeReturnType,
1451+
RawUnexpectedNodesSyntax(misplacedSpecifiers, arena: self.arena),
14601452
returnType: result,
14611453
arena: self.arena)
1462-
return (returnClause, misplacedThrowsKeyword)
1454+
return (returnClause, misplacedSpecifiers)
14631455
}
14641456
}
14651457

@@ -1544,33 +1536,27 @@ extension Parser {
15441536
public mutating func parseFunctionSignature() -> RawFunctionSignatureSyntax {
15451537
let input = self.parseParameterClause(for: .functionParameters)
15461538

1547-
let async: RawTokenSyntax?
1548-
if let asyncTok = self.consumeIfContextualKeyword("async") {
1549-
async = asyncTok
1550-
} else if let reasync = self.consumeIfContextualKeyword("reasync") {
1551-
async = reasync
1552-
} else {
1553-
async = nil
1554-
}
1555-
1556-
var throwsKeyword = self.consume(ifAny: [.throwsKeyword, .rethrowsKeyword])
1539+
var (asyncOrReasync, throwsOrRethrows, specifiersAfterThrows) = parseEffectsSpecifiers(atDecl: true)
15571540

15581541
let output: RawReturnClauseSyntax?
1559-
if self.at(.arrow) {
1560-
let result = self.parseFunctionReturnClause()
1561-
output = result.returnClause
1562-
if let misplacedThrowsKeyword = result.misplacedThrowsKeyword, throwsKeyword == nil {
1563-
throwsKeyword = RawTokenSyntax(missing: misplacedThrowsKeyword.tokenKind, arena: self.arena)
1564-
}
1542+
if let handle = self.canRecoverTo(.arrow) {
1543+
let (returnClause, misplacedSpecifiers) = parseReturnClause(handle)
1544+
addMissingEffectsSpecifiers(atDecl: true, from: misplacedSpecifiers, asyncOrReasync: &asyncOrReasync, throwsOrRethrows: &throwsOrRethrows)
1545+
output = returnClause
15651546
} else {
15661547
output = nil
15671548
}
15681549

1550+
let specifiersAfterReturn = parseAllEffectsSpecifiers()
1551+
addMissingEffectsSpecifiers(atDecl: true, from: specifiersAfterReturn, asyncOrReasync: &asyncOrReasync, throwsOrRethrows: &throwsOrRethrows)
1552+
15691553
return RawFunctionSignatureSyntax(
15701554
input: input,
1571-
asyncOrReasyncKeyword: async,
1572-
throwsOrRethrowsKeyword: throwsKeyword,
1555+
asyncOrReasyncKeyword: asyncOrReasync,
1556+
throwsOrRethrowsKeyword: throwsOrRethrows,
1557+
RawUnexpectedNodesSyntax(specifiersAfterThrows, arena: self.arena),
15731558
output: output,
1559+
RawUnexpectedNodesSyntax(specifiersAfterReturn, arena: self.arena),
15741560
arena: self.arena)
15751561
}
15761562
}
@@ -1601,9 +1587,11 @@ extension Parser {
16011587

16021588
let indices = self.parseParameterClause(for: .indices)
16031589

1590+
let unexpectedBeforeArrow = RawUnexpectedNodesSyntax(parseAllEffectsSpecifiers(), arena: self.arena)
1591+
16041592
let result: RawReturnClauseSyntax
1605-
if self.at(.arrow) {
1606-
result = self.parseFunctionReturnClause().returnClause
1593+
if let handle = self.canRecoverTo(.arrow) {
1594+
result = self.parseReturnClause(handle).returnClause
16071595
} else {
16081596
result = RawReturnClauseSyntax(
16091597
arrow: RawTokenSyntax(missing: .arrow, arena: self.arena),
@@ -1635,6 +1623,7 @@ extension Parser {
16351623
subscriptKeyword: subscriptKeyword,
16361624
genericParameterClause: genericParameterClause,
16371625
indices: indices,
1626+
unexpectedBeforeArrow,
16381627
result: result,
16391628
genericWhereClause: genericWhereClause,
16401629
accessor: accessor,
@@ -1767,41 +1756,6 @@ extension Parser {
17671756
attributes: attrs, modifier: modifier, kind: kind, token: introducer)
17681757
}
17691758

1770-
@_spi(RawSyntax)
1771-
public mutating func parseEffectsSpecifier() -> RawTokenSyntax? {
1772-
// 'async'
1773-
if let async = self.consumeIfContextualKeyword("async") {
1774-
return async
1775-
}
1776-
1777-
// 'reasync'
1778-
if let reasync = self.consumeIfContextualKeyword("reasync") {
1779-
return reasync
1780-
}
1781-
1782-
// 'throws'/'rethrows'
1783-
if let throwsRethrows = self.consume(ifAny: [.throwsKeyword, .rethrowsKeyword]) {
1784-
return throwsRethrows
1785-
}
1786-
1787-
// diagnose 'throw'/'try'.
1788-
if let throwTry = self.consume(ifAny: [.throwKeyword, .tryKeyword], where: { !$0.isAtStartOfLine }) {
1789-
return throwTry
1790-
}
1791-
1792-
return nil
1793-
}
1794-
1795-
@_spi(RawSyntax)
1796-
public mutating func parseEffectsSpecifiers() -> [RawTokenSyntax] {
1797-
var specifiers = [RawTokenSyntax]()
1798-
var loopProgress = LoopProgressCondition()
1799-
while let specifier = self.parseEffectsSpecifier(), loopProgress.evaluate(currentToken) {
1800-
specifiers.append(specifier)
1801-
}
1802-
return specifiers
1803-
}
1804-
18051759
/// Parse the body of a variable declaration. This can include explicit
18061760
/// getters, setters, and observers, or the body of a computed property.
18071761
///
@@ -1884,14 +1838,15 @@ extension Parser {
18841838

18851839
// Next, parse effects specifiers. While it's only valid to have them
18861840
// on 'get' accessors, we also emit diagnostics if they show up on others.
1887-
let asyncKeyword: RawTokenSyntax?
1888-
let throwsKeyword: RawTokenSyntax?
1889-
if self.at(anyIn: EffectsSpecifier.self) != nil {
1890-
asyncKeyword = self.parseEffectsSpecifier()
1891-
throwsKeyword = self.parseEffectsSpecifier()
1841+
var asyncOrReasync: RawTokenSyntax? = nil
1842+
var throwsOrRethrows: RawTokenSyntax? = nil
1843+
let unexpectedAfterThrows: [SpecifierWithToken]
1844+
if introducer.kind == .get {
1845+
(asyncOrReasync, throwsOrRethrows, unexpectedAfterThrows) = parseEffectsSpecifiers(atDecl: true)
18921846
} else {
1893-
asyncKeyword = nil
1894-
throwsKeyword = nil
1847+
asyncOrReasync = nil
1848+
throwsOrRethrows = nil
1849+
unexpectedAfterThrows = parseAllEffectsSpecifiers()
18951850
}
18961851

18971852
let body = self.parseOptionalCodeBlock()
@@ -1901,8 +1856,9 @@ extension Parser {
19011856
modifier: introducer.modifier,
19021857
accessorKind: introducer.token,
19031858
parameter: parameter,
1904-
asyncKeyword: asyncKeyword,
1905-
throwsKeyword: throwsKeyword,
1859+
asyncKeyword: asyncOrReasync,
1860+
throwsKeyword: throwsOrRethrows,
1861+
RawUnexpectedNodesSyntax(unexpectedAfterThrows, arena: self.arena),
19061862
body: body,
19071863
arena: self.arena))
19081864
}
@@ -2233,3 +2189,59 @@ extension Parser {
22332189
}
22342190
}
22352191
}
2192+
2193+
extension Parser {
2194+
mutating func parseAllEffectsSpecifiers() -> [SpecifierWithToken] {
2195+
var specifiers = [SpecifierWithToken]()
2196+
var loopProgress = LoopProgressCondition()
2197+
while let (specifier, handle) = self.canRecoverTo(anyIn: EffectsSpecifier.self), loopProgress.evaluate(currentToken) {
2198+
let (unexpected, token) = self.eatKeepUnexpected(handle)
2199+
specifiers.append(contentsOf: unexpected.map { SpecifierWithToken(specifier: nil, token: $0) })
2200+
specifiers.append(SpecifierWithToken(specifier: specifier, token: token))
2201+
}
2202+
return specifiers
2203+
}
2204+
2205+
func addMissingEffectsSpecifiers(atDecl: Bool, from unexpectedSpecifiers: [SpecifierWithToken], asyncOrReasync: inout RawTokenSyntax?, throwsOrRethrows: inout RawTokenSyntax?) {
2206+
for unexpected in unexpectedSpecifiers {
2207+
guard asyncOrReasync == nil || throwsOrRethrows == nil else {
2208+
break
2209+
}
2210+
2211+
if asyncOrReasync == nil, unexpected.specifier == .asyncContextualKeyword || (atDecl && unexpected.specifier == .reasyncContextualKeyword) {
2212+
asyncOrReasync = RawTokenSyntax(missing: unexpected.token.tokenKind, text: unexpected.token.tokenText, arena: self.arena)
2213+
}
2214+
2215+
if throwsOrRethrows == nil, unexpected.specifier == .throwKeyword || (atDecl && unexpected.specifier == .rethrowsKeyword) {
2216+
throwsOrRethrows = RawTokenSyntax(missing: unexpected.token.tokenKind, arena: self.arena)
2217+
}
2218+
}
2219+
}
2220+
2221+
mutating func parseEffectsSpecifiers(atDecl: Bool) -> (asyncOrReasync: RawTokenSyntax?,
2222+
throwsOrRethrows: RawTokenSyntax?,
2223+
specifiersAfterThrows: [SpecifierWithToken]){
2224+
var initialSpecifiers = parseAllEffectsSpecifiers()
2225+
2226+
var asyncOrReasync: RawTokenSyntax?
2227+
var throwsOrRethrows: RawTokenSyntax?
2228+
if !initialSpecifiers.isEmpty {
2229+
let first = initialSpecifiers.removeFirst()
2230+
if first.specifier == .asyncContextualKeyword || atDecl && first.specifier == .reasyncContextualKeyword {
2231+
asyncOrReasync = first.token
2232+
2233+
if !initialSpecifiers.isEmpty {
2234+
let second = initialSpecifiers.removeFirst()
2235+
if second.specifier == .throwsKeyword || atDecl && second.specifier == .rethrowsKeyword {
2236+
throwsOrRethrows = second.token
2237+
}
2238+
}
2239+
} else if first.specifier == .throwsKeyword || atDecl && first.specifier == .rethrowsKeyword {
2240+
throwsOrRethrows = first.token
2241+
}
2242+
addMissingEffectsSpecifiers(atDecl: atDecl, from: initialSpecifiers, asyncOrReasync: &asyncOrReasync, throwsOrRethrows: &throwsOrRethrows)
2243+
}
2244+
2245+
return (asyncOrReasync, throwsOrRethrows, initialSpecifiers)
2246+
}
2247+
}

Sources/SwiftParser/Expressions.swift

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,8 +2066,9 @@ extension Parser {
20662066
}
20672067

20682068
var input: RawSyntax?
2069-
var asyncKeyword: RawTokenSyntax? = nil
2069+
var asyncTok: RawTokenSyntax? = nil
20702070
var throwsTok: RawTokenSyntax? = nil
2071+
var specifiersAfterThrows = [SpecifierWithToken]()
20712072
var output: RawReturnClauseSyntax? = nil
20722073
if !self.at(.inKeyword) {
20732074
if self.at(.leftParen) {
@@ -2097,32 +2098,30 @@ extension Parser {
20972098
input = RawSyntax(RawClosureParamListSyntax(elements: params, arena: self.arena))
20982099
}
20992100

2100-
asyncKeyword = self.parseEffectsSpecifier()
2101-
throwsTok = self.parseEffectsSpecifier()
2101+
(asyncTok, throwsTok, specifiersAfterThrows) = parseEffectsSpecifiers(atDecl: false)
21022102

2103-
// Parse the optional explicit return type.
2104-
if let arrow = self.consume(if: .arrow) {
2105-
// Parse the type.
2106-
let returnTy = self.parseType()
2107-
2108-
output = RawReturnClauseSyntax(
2109-
arrow: arrow,
2110-
returnType: returnTy,
2111-
arena: self.arena
2112-
)
2103+
if let handle = self.canRecoverTo(.arrow) {
2104+
let (returnClause, misplacedSpecifiers) = parseReturnClause(handle)
2105+
addMissingEffectsSpecifiers(atDecl: false, from: misplacedSpecifiers, asyncOrReasync: &asyncTok, throwsOrRethrows: &throwsTok)
2106+
output = returnClause
21132107
}
21142108
}
21152109

2110+
var specifiersBeforeIn: [SpecifierWithToken] = parseAllEffectsSpecifiers()
2111+
addMissingEffectsSpecifiers(atDecl: false, from: specifiersBeforeIn, asyncOrReasync: &asyncTok, throwsOrRethrows: &throwsTok)
2112+
21162113
// Parse the 'in'.
2117-
let (unexpectedBeforeInTok, inTok) = self.expect(.inKeyword)
2114+
let (unexpectedBeforeInTok, inTok) = self.expectKeepUnexpected(.inKeyword)
2115+
21182116
return RawClosureSignatureSyntax(
21192117
attributes: attrs,
21202118
capture: captures,
21212119
input: input,
2122-
asyncKeyword: asyncKeyword,
2120+
asyncKeyword: asyncTok,
21232121
throwsTok: throwsTok,
2122+
RawUnexpectedNodesSyntax(specifiersAfterThrows, arena: self.arena),
21242123
output: output,
2125-
unexpectedBeforeInTok,
2124+
RawUnexpectedNodesSyntax(specifiersBeforeIn.map { $0.token } + unexpectedBeforeInTok, arena: self.arena),
21262125
inTok: inTok,
21272126
arena: self.arena)
21282127
}

Sources/SwiftParser/Lookahead.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ extension Parser {
5151
public func lookahead() -> Lookahead {
5252
return Lookahead(cloning: self)
5353
}
54+
55+
public func lookahead<T>(_ body: (_: inout Lookahead) -> T) -> T {
56+
var lookahead = lookahead()
57+
return body(&lookahead)
58+
}
5459
}
5560

5661
extension Parser.Lookahead {

Sources/SwiftParser/Parser.swift

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -281,40 +281,76 @@ extension Parser {
281281
}
282282

283283
/// Eat a token that we know we are currently positioned at, based on `canRecoverTo(anyIn:)`.
284-
mutating func eat(_ handle: RecoveryConsumptionHandle) -> (RawUnexpectedNodesSyntax?, Token) {
285-
let unexpectedNodes: RawUnexpectedNodesSyntax?
284+
mutating func eatKeepUnexpected(_ handle: RecoveryConsumptionHandle) -> (unexpected: [RawTokenSyntax], token: Token) {
285+
var unexpectedTokens = [RawTokenSyntax]()
286286
if handle.unexpectedTokens > 0 {
287-
var unexpectedTokens = [RawSyntax]()
288287
for _ in 0..<handle.unexpectedTokens {
289-
unexpectedTokens.append(RawSyntax(self.consumeAnyToken()))
288+
unexpectedTokens.append(self.consumeAnyToken())
290289
}
291-
unexpectedNodes = RawUnexpectedNodesSyntax(elements: unexpectedTokens, arena: self.arena)
292-
} else {
293-
unexpectedNodes = nil
294290
}
295291
let token = self.eat(handle.tokenConsumptionHandle)
296-
return (unexpectedNodes, token)
292+
return (unexpectedTokens, token)
293+
}
294+
295+
/// Eat a token that we know we are currently positioned at, based on `canRecoverTo(anyIn:)`.
296+
mutating func eat(_ handle: RecoveryConsumptionHandle) -> (unexpected: RawUnexpectedNodesSyntax?, token: Token) {
297+
let (unexpected, token) = eatKeepUnexpected(handle)
298+
return (RawUnexpectedNodesSyntax(elements: unexpected.map(RawSyntax.init), arena: self.arena), token)
297299
}
298300
}
299301

300302
// MARK: Expecting Tokens with Recovery
301303

302304
extension Parser {
303305
/// Implements the paradigm shared across all `expect` methods.
304-
private mutating func expectImpl(
306+
private mutating func expectImplKeepUnexpected(
305307
consume: (inout Parser) -> RawTokenSyntax?,
306308
canRecoverTo: (inout Lookahead) -> RecoveryConsumptionHandle?,
307309
makeMissing: (inout Parser) -> RawTokenSyntax
308-
) -> (unexpected: RawUnexpectedNodesSyntax?, token: RawTokenSyntax) {
310+
) -> (unexpected: [RawTokenSyntax], token: RawTokenSyntax) {
309311
if let tok = consume(&self) {
310-
return (nil, tok)
312+
return ([], tok)
311313
}
312314
var lookahead = self.lookahead()
313315
if let handle = canRecoverTo(&lookahead) {
314-
let (unexpectedTokens, token) = self.eat(handle)
316+
let (unexpectedTokens, token) = self.eatKeepUnexpected(handle)
315317
return (unexpectedTokens, token)
316318
}
317-
return (nil, makeMissing(&self))
319+
return ([], makeMissing(&self))
320+
}
321+
322+
/// Implements the paradigm shared across all `expect` methods.
323+
private mutating func expectImpl(
324+
consume: (inout Parser) -> RawTokenSyntax?,
325+
canRecoverTo: (inout Lookahead) -> RecoveryConsumptionHandle?,
326+
makeMissing: (inout Parser) -> RawTokenSyntax
327+
) -> (unexpected: RawUnexpectedNodesSyntax?, token: RawTokenSyntax) {
328+
let (unexpected, token) = expectImplKeepUnexpected(consume: consume, canRecoverTo: canRecoverTo, makeMissing: makeMissing)
329+
return (RawUnexpectedNodesSyntax(unexpected, arena: self.arena), token)
330+
}
331+
332+
/// Attempts to consume a token of the given kind.
333+
/// If it cannot be found, the parser tries
334+
/// 1. To eat unexpected tokens that have lower ``TokenPrecedence`` than the
335+
/// expected token and see if the token occurs after that unexpected.
336+
/// 2. If the token couldn't be found after skipping unexpected, it synthesizes
337+
/// a missing token of the requested kind.
338+
@_spi(RawSyntax)
339+
public mutating func expectKeepUnexpected(
340+
_ kind: RawTokenKind,
341+
remapping: RawTokenKind? = nil
342+
) -> (unexpected: [RawTokenSyntax], token: RawTokenSyntax) {
343+
return expectImplKeepUnexpected(
344+
consume: { $0.consume(if: kind, remapping: remapping) },
345+
canRecoverTo: { $0.canRecoverTo([kind]) },
346+
makeMissing: {
347+
if let remapping = remapping {
348+
return $0.missingToken(remapping, text: kind.defaultText)
349+
} else {
350+
return $0.missingToken(kind, text: nil)
351+
}
352+
}
353+
)
318354
}
319355

320356
/// Attempts to consume a token of the given kind.

0 commit comments

Comments
 (0)