Skip to content

Commit 50f0688

Browse files
Applied review comments
1 parent 7cef632 commit 50f0688

File tree

4 files changed

+93
-21
lines changed

4 files changed

+93
-21
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,9 @@ extension Parser {
11671167

11681168
extension Parser {
11691169
/// 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.
1170-
mutating func parseFunctionReturnClause(effectSpecifiers: inout (some RawEffectSpecifiersTrait)?, allowNamedOpaqueResultType: Bool) -> RawReturnClauseSyntax {
1170+
mutating func parseFunctionReturnClause(effectSpecifiers: inout (some RawMisplacedEffectSpecifiersTrait)?, allowNamedOpaqueResultType: Bool)
1171+
-> RawReturnClauseSyntax
1172+
{
11711173
let (unexpectedBeforeArrow, arrow) = self.expect(.arrow)
11721174
let unexpectedBeforeReturnType = self.parseMisplacedEffectSpecifiers(&effectSpecifiers)
11731175
let result: RawTypeSyntax
@@ -1251,7 +1253,7 @@ extension Parser {
12511253

12521254
/// Only allow recovery to the arrow with exprKeyword precedence so we only
12531255
/// skip over misplaced identifiers and don't e.g. recover to an arrow in a 'where' clause.
1254-
if self.at(.arrow) || self.canRecoverTo(TokenSpec(.arrow, recoveryPrecedence: .exprKeyword)) != nil {
1256+
if self.canRecoverTo(TokenSpec(.arrow, recoveryPrecedence: .exprKeyword)) != nil {
12551257
output = self.parseFunctionReturnClause(effectSpecifiers: &effectSpecifiers, allowNamedOpaqueResultType: true)
12561258
} else {
12571259
output = nil

Sources/SwiftParser/Recovery.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ extension Parser.Lookahead {
130130
#endif
131131
let initialTokensConsumed = self.tokensConsumed
132132

133-
precondition(!specSet.allCases.isEmpty, "SpecSet must have at least one case")
133+
if specSet.allCases.isEmpty {
134+
return nil
135+
}
136+
134137
let recoveryPrecedence =
135138
overrideRecoveryPrecedence ?? specSet.allCases.map({
136139
return $0.spec.recoveryPrecedence

Sources/SwiftParser/Specifiers.swift

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public enum EffectSpecifier: TokenSpecSet {
134134

135135
/// Raw syntax nodes don't have traits (because usually we don't need them).
136136
/// Specify the effect specifiers trait manually as a one off.
137-
protocol RawEffectSpecifiersTrait {
137+
protocol RawMisplacedEffectSpecifiersTrait {
138138
/// The token kinds that should be consumed as misspelled `asyncSpecifier`.
139139
/// Should be a subset of ``AsyncEffectSpecifier``.
140140
associatedtype MisspelledAsyncTokenKinds: TokenSpecSet
@@ -151,10 +151,21 @@ protocol RawEffectSpecifiersTrait {
151151
/// Should be a subset of ``ThrowsEffectSpecifier``.
152152
associatedtype CorrectThrowsTokenKinds: TokenSpecSet
153153

154-
var unexpectedBeforeAsyncSpecifier: RawUnexpectedNodesSyntax? { get }
155154
var asyncSpecifier: RawTokenSyntax? { get }
156-
var unexpectedBetweenAsyncSpecifierAndThrowsSpecifier: RawUnexpectedNodesSyntax? { get }
157155
var throwsSpecifier: RawTokenSyntax? { get }
156+
157+
init(
158+
asyncSpecifier: RawTokenSyntax?,
159+
throwsSpecifier: RawTokenSyntax?,
160+
arena: __shared SyntaxArena
161+
)
162+
163+
func withMisplaced(async misplacedAsyncKeyword: RawTokenSyntax?, throws misplacedThrowsKeyword: RawTokenSyntax?, arena: __shared SyntaxArena) -> Self
164+
}
165+
166+
protocol RawEffectSpecifiersTrait: RawMisplacedEffectSpecifiersTrait {
167+
var unexpectedBeforeAsyncSpecifier: RawUnexpectedNodesSyntax? { get }
168+
var unexpectedBetweenAsyncSpecifierAndThrowsSpecifier: RawUnexpectedNodesSyntax? { get }
158169
var unexpectedAfterThrowsSpecifier: RawUnexpectedNodesSyntax? { get }
159170

160171
init(
@@ -168,6 +179,21 @@ protocol RawEffectSpecifiersTrait {
168179
}
169180

170181
extension RawEffectSpecifiersTrait {
182+
init(
183+
asyncSpecifier: RawTokenSyntax?,
184+
throwsSpecifier: RawTokenSyntax?,
185+
arena: __shared SyntaxArena
186+
) {
187+
self.init(
188+
nil,
189+
asyncSpecifier: asyncSpecifier,
190+
nil,
191+
throwsSpecifier: throwsSpecifier,
192+
nil,
193+
arena: arena
194+
)
195+
}
196+
171197
func withMisplaced(async misplacedAsyncKeyword: RawTokenSyntax?, throws misplacedThrowsKeyword: RawTokenSyntax?, arena: __shared SyntaxArena) -> Self {
172198
return Self.init(
173199
self.unexpectedBeforeAsyncSpecifier,
@@ -417,8 +443,7 @@ extension RawAccessorEffectSpecifiersSyntax: RawEffectSpecifiersTrait {
417443
}
418444
}
419445

420-
// This conformance exists solely to parse misplaced specifiers in parseFunctionReturnClause()
421-
extension RawDeinitEffectSpecifiersSyntax: RawEffectSpecifiersTrait {
446+
extension RawDeinitEffectSpecifiersSyntax: RawMisplacedEffectSpecifiersTrait {
422447
enum MisspelledAsyncTokenKinds: TokenSpecSet {
423448
case await
424449
case reasync
@@ -495,25 +520,32 @@ extension RawDeinitEffectSpecifiersSyntax: RawEffectSpecifiersTrait {
495520
}
496521
}
497522

498-
var unexpectedBetweenAsyncSpecifierAndThrowsSpecifier: RawUnexpectedNodesSyntax? { unexpectedAfterAsyncSpecifier }
499523
var throwsSpecifier: RawTokenSyntax? { nil }
500-
var unexpectedAfterThrowsSpecifier: RawUnexpectedNodesSyntax? { nil }
501524

502525
init(
503-
_ unexpectedBeforeAsyncSpecifier: RawUnexpectedNodesSyntax?,
504526
asyncSpecifier: RawTokenSyntax?,
505-
_ unexpectedBetweenAsyncSpecifierAndThrowsSpecifier: RawUnexpectedNodesSyntax?,
506527
throwsSpecifier: RawTokenSyntax?,
507-
_ unexpectedAfterThrowsSpecifier: RawUnexpectedNodesSyntax?,
508528
arena: __shared SwiftSyntax.SyntaxArena
509529
) {
510530
// Inserted missing throws is discarded
511-
assert(throwsSpecifier?.isMissing ?? true)
512-
assert(unexpectedAfterThrowsSpecifier == nil)
531+
precondition(throwsSpecifier?.isMissing ?? true)
513532
self.init(
514-
unexpectedBeforeAsyncSpecifier,
533+
nil,
515534
asyncSpecifier: asyncSpecifier,
516-
unexpectedBetweenAsyncSpecifierAndThrowsSpecifier,
535+
nil,
536+
arena: arena
537+
)
538+
}
539+
540+
func withMisplaced(async misplacedAsyncKeyword: RawTokenSyntax?, throws misplacedThrowsKeyword: RawTokenSyntax?, arena: SyntaxArena)
541+
-> RawDeinitEffectSpecifiersSyntax
542+
{
543+
// Inserted missing throws is discarded
544+
precondition(throwsSpecifier?.isMissing ?? true)
545+
return Self.init(
546+
self.unexpectedBeforeAsyncSpecifier,
547+
asyncSpecifier: self.asyncSpecifier ?? misplacedAsyncKeyword,
548+
self.unexpectedAfterAsyncSpecifier,
517549
arena: arena
518550
)
519551
}
@@ -672,7 +704,7 @@ extension Parser {
672704
/// When a misplaced effect specifier is consumed and `effectSpecifiers`
673705
/// doesn't have an effect specifier of that kind, modify `effectSpecifiers`
674706
/// to have a missing specifier of that kind.
675-
mutating func parseMisplacedEffectSpecifiers<S: RawEffectSpecifiersTrait>(_ effectSpecifiers: inout S?) -> RawUnexpectedNodesSyntax? {
707+
mutating func parseMisplacedEffectSpecifiers<S: RawMisplacedEffectSpecifiersTrait>(_ effectSpecifiers: inout S?) -> RawUnexpectedNodesSyntax? {
676708
var synthesizedAsync: RawTokenSyntax? = nil
677709
var synthesizedThrows: RawTokenSyntax? = nil
678710
var unexpected: [RawTokenSyntax] = []
@@ -707,11 +739,8 @@ extension Parser {
707739
effectSpecifiers = specifiers.withMisplaced(async: synthesizedAsync, throws: synthesizedThrows, arena: self.arena)
708740
} else {
709741
effectSpecifiers = S(
710-
nil,
711742
asyncSpecifier: synthesizedAsync,
712-
nil,
713743
throwsSpecifier: synthesizedThrows,
714-
nil,
715744
arena: self.arena
716745
)
717746
}

Tests/SwiftParserTest/translated/InitDeinitTests.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,44 @@ final class InitDeinitTests: XCTestCase {
972972
)
973973
}
974974

975+
func testDeinitOutputAsyncThrows() {
976+
assertParse(
977+
"""
978+
class FooClassDeinitializerA {
979+
deinit 1️⃣-> async throws Void {}
980+
}
981+
""",
982+
diagnostics: [
983+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'async' in effect specifiers", fixIts: ["insert 'async'"]),
984+
DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'async throws', and return type"]),
985+
],
986+
fixedSource: """
987+
class FooClassDeinitializerA {
988+
deinit async {}
989+
}
990+
"""
991+
)
992+
}
993+
994+
func testDeinitOutputThrowsAsync() {
995+
assertParse(
996+
"""
997+
class FooClassDeinitializerA {
998+
deinit 1️⃣-> throws async Void {}
999+
}
1000+
""",
1001+
diagnostics: [
1002+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'async' in effect specifiers", fixIts: ["insert 'async'"]),
1003+
DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'throws async', and return type"]),
1004+
],
1005+
fixedSource: """
1006+
class FooClassDeinitializerA {
1007+
deinit async {}
1008+
}
1009+
"""
1010+
)
1011+
}
1012+
9751013
func testAsyncDeinit() {
9761014
// This is expected for now.
9771015
// `async` is parsed as a modifier like `public` because you can have an `async var x: Int`.

0 commit comments

Comments
 (0)