Skip to content

Commit 53a73f4

Browse files
committed
[Typed throws] Parse typed throws in effect specifiers
1 parent b0cfa0a commit 53a73f4

File tree

5 files changed

+90
-15
lines changed

5 files changed

+90
-15
lines changed

Sources/SwiftParser/Specifiers.swift

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ public enum EffectSpecifier: TokenSpecSet {
128128
case .throwsSpecifier(let underlyingKind): return underlyingKind.spec
129129
}
130130
}
131+
132+
var isThrowsSpecifier: Bool {
133+
switch self {
134+
case .asyncSpecifier: return false
135+
case .throwsSpecifier: return true
136+
}
137+
}
131138
}
132139

133140
// MARK: - EffectSpecifiersTrait
@@ -153,10 +160,12 @@ protocol RawMisplacedEffectSpecifiersTrait {
153160

154161
var asyncSpecifier: RawTokenSyntax? { get }
155162
var throwsSpecifier: RawTokenSyntax? { get }
163+
var thrownType: RawThrownTypeSyntax? { get }
156164

157165
init(
158166
asyncSpecifier: RawTokenSyntax?,
159167
throwsSpecifier: RawTokenSyntax?,
168+
thrownType: RawThrownTypeSyntax?,
160169
arena: __shared SyntaxArena
161170
)
162171

@@ -166,14 +175,17 @@ protocol RawMisplacedEffectSpecifiersTrait {
166175
protocol RawEffectSpecifiersTrait: RawMisplacedEffectSpecifiersTrait {
167176
var unexpectedBeforeAsyncSpecifier: RawUnexpectedNodesSyntax? { get }
168177
var unexpectedBetweenAsyncSpecifierAndThrowsSpecifier: RawUnexpectedNodesSyntax? { get }
169-
var unexpectedAfterThrowsSpecifier: RawUnexpectedNodesSyntax? { get }
170-
178+
var unexpectedBetweenThrowsSpecifierAndThrownType: RawUnexpectedNodesSyntax? { get }
179+
var thrownType: RawThrownTypeSyntax? { get }
180+
var unexpectedAfterThrownType: RawUnexpectedNodesSyntax? { get }
171181
init(
172182
_ unexpectedBeforeAsyncSpecifier: RawUnexpectedNodesSyntax?,
173183
asyncSpecifier: RawTokenSyntax?,
174184
_ unexpectedBetweenAsyncSpecifierAndThrowsSpecifier: RawUnexpectedNodesSyntax?,
175185
throwsSpecifier: RawTokenSyntax?,
176-
_ unexpectedAfterThrowsSpecifier: RawUnexpectedNodesSyntax?,
186+
_ unexpectedBetweenThrowsSpecifierAndThrownType: RawUnexpectedNodesSyntax?,
187+
thrownType: RawThrownTypeSyntax?,
188+
_ unexpectedAfterThrownType: RawUnexpectedNodesSyntax?,
177189
arena: __shared SyntaxArena
178190
)
179191
}
@@ -182,6 +194,7 @@ extension RawEffectSpecifiersTrait {
182194
init(
183195
asyncSpecifier: RawTokenSyntax?,
184196
throwsSpecifier: RawTokenSyntax?,
197+
thrownType: RawThrownTypeSyntax?,
185198
arena: __shared SyntaxArena
186199
) {
187200
self.init(
@@ -190,6 +203,8 @@ extension RawEffectSpecifiersTrait {
190203
nil,
191204
throwsSpecifier: throwsSpecifier,
192205
nil,
206+
thrownType: thrownType,
207+
nil,
193208
arena: arena
194209
)
195210
}
@@ -200,7 +215,9 @@ extension RawEffectSpecifiersTrait {
200215
asyncSpecifier: self.asyncSpecifier ?? misplacedAsyncKeyword,
201216
self.unexpectedBetweenAsyncSpecifierAndThrowsSpecifier,
202217
throwsSpecifier: self.throwsSpecifier ?? misplacedThrowsKeyword,
203-
self.unexpectedAfterThrowsSpecifier,
218+
self.unexpectedBetweenThrowsSpecifierAndThrownType,
219+
thrownType: thrownType,
220+
self.unexpectedAfterThrownType,
204221
arena: arena
205222
)
206223
}
@@ -521,10 +538,12 @@ extension RawDeinitializerEffectSpecifiersSyntax: RawMisplacedEffectSpecifiersTr
521538
}
522539

523540
var throwsSpecifier: RawTokenSyntax? { nil }
541+
var thrownType: RawThrownTypeSyntax? { nil }
524542

525543
init(
526544
asyncSpecifier: RawTokenSyntax?,
527545
throwsSpecifier: RawTokenSyntax?,
546+
thrownType: RawThrownTypeSyntax?,
528547
arena: __shared SwiftSyntax.SyntaxArena
529548
) {
530549
// `throwsSpecifier` should never be present because `parseMisplacedEffectSpecifiers()` only creates missing tokens
@@ -577,12 +596,28 @@ extension TokenConsumer {
577596
// MARK: - Parsing effect specifiers
578597

579598
extension Parser {
599+
private mutating func parseThrownType() -> RawThrownTypeSyntax {
600+
let (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen)
601+
let type = self.parseType()
602+
let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen)
603+
return RawThrownTypeSyntax(
604+
unexpectedBeforeLeftParen,
605+
leftParen: leftParen,
606+
type: type,
607+
unexpectedBeforeRightParen,
608+
rightParen: rightParen,
609+
arena: self.arena
610+
)
611+
}
612+
580613
private mutating func parseEffectSpecifiers<S: RawEffectSpecifiersTrait>(_: S.Type) -> S? {
581614
var unexpectedBeforeAsync: [RawSyntax] = []
582615
var asyncKeyword: RawTokenSyntax? = nil
583616
var unexpectedBeforeThrows: [RawSyntax] = []
584617
var throwsKeyword: RawTokenSyntax?
585-
var unexpectedAfterThrows: [RawSyntax] = []
618+
var thrownType: RawThrownTypeSyntax?
619+
var unexpectedAfterThrownType: [RawSyntax] = []
620+
586621
while let misspelledAsync = self.consume(ifAnyIn: S.MisspelledAsyncTokenKinds.self) {
587622
unexpectedBeforeAsync.append(RawSyntax(misspelledAsync))
588623
if asyncKeyword == nil {
@@ -619,24 +654,28 @@ extension Parser {
619654
throwsKeyword = throwsKw
620655
}
621656

622-
var unexpectedAfterThrowsLoopProgress = LoopProgressCondition()
623-
while self.hasProgressed(&unexpectedAfterThrowsLoopProgress) {
657+
if throwsKeyword != nil && self.at(.leftParen) {
658+
thrownType = parseThrownType()
659+
}
660+
661+
var unexpectedAfterThrownTypeLoopProgress = LoopProgressCondition()
662+
while self.hasProgressed(&unexpectedAfterThrownTypeLoopProgress) {
624663
if let (_, handle, _) = self.at(anyIn: S.MisspelledAsyncTokenKinds.self, or: S.CorrectAsyncTokenKinds.self) {
625664
let misspelledAsync = self.eat(handle)
626-
unexpectedAfterThrows.append(RawSyntax(misspelledAsync))
665+
unexpectedAfterThrownType.append(RawSyntax(misspelledAsync))
627666
if asyncKeyword == nil {
628667
// Handle `async` after `throws`
629668
asyncKeyword = missingToken(.keyword(.async))
630669
}
631670
} else if let (_, handle, _) = self.at(anyIn: S.MisspelledThrowsTokenKinds.self, or: S.CorrectThrowsTokenKinds.self) {
632671
let misspelledThrows = self.eat(handle)
633-
unexpectedAfterThrows.append(RawSyntax(misspelledThrows))
672+
unexpectedAfterThrownType.append(RawSyntax(misspelledThrows))
634673
} else {
635674
break
636675
}
637676
}
638677

639-
if unexpectedBeforeAsync.isEmpty && asyncKeyword == nil && unexpectedBeforeThrows.isEmpty && throwsKeyword == nil && unexpectedAfterThrows.isEmpty {
678+
if unexpectedBeforeAsync.isEmpty && asyncKeyword == nil && unexpectedBeforeThrows.isEmpty && throwsKeyword == nil && thrownType == nil && unexpectedAfterThrownType.isEmpty {
640679
return nil
641680
}
642681

@@ -645,7 +684,9 @@ extension Parser {
645684
asyncSpecifier: asyncKeyword,
646685
RawUnexpectedNodesSyntax(unexpectedBeforeThrows, arena: self.arena),
647686
throwsSpecifier: throwsKeyword,
648-
RawUnexpectedNodesSyntax(unexpectedAfterThrows, arena: self.arena),
687+
nil,
688+
thrownType: thrownType,
689+
RawUnexpectedNodesSyntax(unexpectedAfterThrownType, arena: self.arena),
649690
arena: self.arena
650691
)
651692
}
@@ -749,6 +790,7 @@ extension Parser {
749790
effectSpecifiers = S(
750791
asyncSpecifier: synthesizedAsync,
751792
throwsSpecifier: synthesizedThrows,
793+
thrownType: nil,
752794
arena: self.arena
753795
)
754796
}

Sources/SwiftParser/Types.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,11 +793,18 @@ extension Parser.Lookahead {
793793
return true
794794
}
795795

796-
if self.at(anyIn: EffectSpecifier.self) != nil {
796+
if let effect = self.at(anyIn: EffectSpecifier.self) {
797797
if self.peek().rawTokenKind == .arrow {
798798
return true
799799
}
800800

801+
if effect.spec.isThrowsSpecifier && self.peek().rawTokenKind == .leftParen {
802+
var backtrack = self.lookahead()
803+
backtrack.consumeAnyToken()
804+
backtrack.skipSingle()
805+
return backtrack.atFunctionTypeArrow()
806+
}
807+
801808
if peek(isAtAnyIn: EffectSpecifier.self) != nil {
802809
var backtrack = self.lookahead()
803810
backtrack.consumeAnyToken()

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
258258
(node.throwsSpecifier, { ThrowsEffectSpecifier(token: $0) != nil }, StaticParserError.misspelledThrows),
259259
]
260260

261-
let unexpectedNodes = [node.unexpectedBeforeAsyncSpecifier, node.unexpectedBetweenAsyncSpecifierAndThrowsSpecifier, node.unexpectedAfterThrowsSpecifier]
261+
let unexpectedNodes = [node.unexpectedBeforeAsyncSpecifier, node.unexpectedBetweenAsyncSpecifierAndThrowsSpecifier,node.unexpectedBetweenThrowsSpecifierAndThrownType, node.unexpectedAfterThrownType]
262262

263263
// Diagnostics that are emitted later silence previous diagnostics, so check
264264
// for the most contextual (and thus helpful) diagnostics last.
@@ -279,9 +279,9 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
279279
}
280280
}
281281

282-
if let throwsSpecifier = node.throwsSpecifier {
282+
if let throwsSpecifier = node.throwsSpecifier, node.thrownType == nil {
283283
exchangeTokens(
284-
unexpected: node.unexpectedAfterThrowsSpecifier,
284+
unexpected: node.unexpectedAfterThrownType,
285285
unexpectedTokenCondition: { AsyncEffectSpecifier(token: $0) != nil },
286286
correctTokens: [node.asyncSpecifier],
287287
message: { AsyncMustPrecedeThrows(asyncKeywords: $0, throwsKeyword: throwsSpecifier) },

Tests/SwiftParserTest/DeclarationTests.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,20 @@ final class DeclarationTests: ParserTestCase {
891891
)
892892
}
893893

894+
func testTypedThrows() {
895+
assertParse(
896+
"func test() throws(any Error) -> Int { }"
897+
)
898+
899+
assertParse(
900+
"""
901+
struct X {
902+
init() throws(any Error) { }
903+
}
904+
"""
905+
)
906+
}
907+
894908
func testExtraneousRightBraceRecovery() {
895909
assertParse(
896910
"""

Tests/SwiftParserTest/TypeTests.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,4 +304,16 @@ final class TypeTests: ParserTestCase {
304304
]
305305
)
306306
}
307+
308+
func testTypedThrows() {
309+
assertParse(
310+
"""
311+
{ () throws(PosixError) -> Void in }
312+
"""
313+
)
314+
315+
assertParse("typealias T = () throws(PosixError) -> Void")
316+
317+
assertParse("[() throws(PosixError) -> Void]()")
318+
}
307319
}

0 commit comments

Comments
 (0)