Skip to content

Commit 1b57308

Browse files
committed
Add diagnostic if deinits have names and parameters
1 parent a09125e commit 1b57308

File tree

7 files changed

+138
-109
lines changed

7 files changed

+138
-109
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,12 +1302,18 @@ extension Parser {
13021302
_ handle: RecoveryConsumptionHandle
13031303
) -> RawDeinitializerDeclSyntax {
13041304
let (unexpectedBeforeDeinitKeyword, deinitKeyword) = self.eat(handle)
1305+
var unexpectedNameAndSignature: [RawSyntax?] = []
1306+
unexpectedNameAndSignature.append(self.consume(if: .identifier).map(RawSyntax.init))
1307+
if self.at(.leftParen) {
1308+
unexpectedNameAndSignature.append(RawSyntax(parseFunctionSignature()))
1309+
}
13051310
let items = self.parseOptionalCodeBlock()
13061311
return RawDeinitializerDeclSyntax(
13071312
attributes: attrs.attributes,
13081313
modifiers: attrs.modifiers,
13091314
unexpectedBeforeDeinitKeyword,
13101315
deinitKeyword: deinitKeyword,
1316+
RawUnexpectedNodesSyntax(unexpectedNameAndSignature, arena: self.arena),
13111317
body: items,
13121318
arena: self.arena
13131319
)

Sources/SwiftParser/Diagnostics/DiagnosticExtensions.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ extension FixIt.Changes {
5252
return FixIt.Changes(changes: changes)
5353
}
5454

55+
static func makeMissing<SyntaxType: SyntaxProtocol>(node: SyntaxType) -> Self {
56+
return FixIt.Changes(changes: [
57+
.replace(oldNode: Syntax(node), newNode: MissingMaker().visit(Syntax(node)))
58+
])
59+
}
60+
5561
/// Remove the nodes in `unexpected`.
5662
static func remove(unexpected: UnexpectedNodesSyntax) -> Self {
5763
var changes: [FixIt.Change] = [

Sources/SwiftParser/Diagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
181181
if shouldSkip(node) {
182182
return .skipChildren
183183
}
184+
if node.allSatisfy({ handledNodes.contains($0.id) }) {
185+
return .skipChildren
186+
}
184187
if let tryKeyword = node.onlyToken(where: { $0.tokenKind == .tryKeyword }),
185188
let nextToken = tryKeyword.nextToken(viewMode: .sourceAccurate),
186189
nextToken.tokenKind.isKeyword {
@@ -446,6 +449,26 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
446449
return .visitChildren
447450
}
448451

452+
public override func visit(_ node: DeinitializerDeclSyntax) -> SyntaxVisitorContinueKind {
453+
if shouldSkip(node) {
454+
return .skipChildren
455+
}
456+
if let unexpected = node.unexpectedBetweenDeinitKeywordAndBody,
457+
let name = unexpected.filter({ $0.as(TokenSyntax.self)?.tokenKind.isIdentifier == true }).only?.as(TokenSyntax.self) {
458+
addDiagnostic(name, .deinitCannotHaveName, fixIts: [
459+
FixIt(message: RemoveNodesFixIt(name), changes: .makeMissing(token: name))
460+
], handledNodes: [name.id])
461+
}
462+
if let unexpected = node.unexpectedBetweenDeinitKeywordAndBody,
463+
let signature = unexpected.compactMap({ $0.as(FunctionSignatureSyntax.self) }).only {
464+
addDiagnostic(signature, .deinitCannotHaveParameters, fixIts: [
465+
FixIt(message: RemoveNodesFixIt(signature), changes: .makeMissing(node: signature))
466+
], handledNodes: [signature.id])
467+
}
468+
469+
return .visitChildren
470+
}
471+
449472
public override func visit(_ node: ForInStmtSyntax) -> SyntaxVisitorContinueKind {
450473
if shouldSkip(node) {
451474
return .skipChildren

Sources/SwiftParser/Diagnostics/ParserDiagnosticMessages.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public enum StaticParserError: String, DiagnosticMessage {
7373
case cStyleForLoop = "C-style for statement has been removed in Swift 3"
7474
case defaultCannotBeUsedWithWhere = "'default' cannot be used with a 'where' guard expression"
7575
case defaultOutsideOfSwitch = "'default' label can only appear inside a 'switch' statement"
76+
case deinitCannotHaveName = "deinitializers cannot have a name"
77+
case deinitCannotHaveParameters = "deinitializers cannot have parameters"
7678
case editorPlaceholderInSourceFile = "editor placeholder in source file"
7779
case expectedExpressionAfterTry = "expected expression after 'try'"
7880
case invalidFlagAfterPrecedenceGroupAssignment = "expected 'true' or 'false' after 'assignment'"

Sources/SwiftParser/Diagnostics/PresenceUtils.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,12 @@ class PresentMaker: SyntaxRewriter {
124124
))
125125
}
126126
}
127+
128+
class MissingMaker: SyntaxRewriter {
129+
override func visit(_ node: TokenSyntax) -> Syntax {
130+
guard node.presence == .present else {
131+
return Syntax(node)
132+
}
133+
return Syntax(TokenSyntax(node.tokenKind, presence: .missing))
134+
}
135+
}

Tests/SwiftParserTest/translated/AsyncTests.swift

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ final class AsyncTests: XCTestCase {
112112
"""
113113
class X {
114114
init() async { }
115-
deinit1️⃣ async 2️⃣{ }
115+
deinit 1️⃣async { }
116116
func f() async { }
117-
subscript(x: Int) 4️⃣async -> Int {
117+
subscript(x: Int) 2️⃣async -> Int {
118118
get {
119119
return 0
120120
}
@@ -124,15 +124,8 @@ final class AsyncTests: XCTestCase {
124124
}
125125
""",
126126
diagnostics: [
127-
// TODO: Old parser expected error on line 3: deinitializers cannot have a name
128-
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive declarations on a line must be separated by ';'"),
129-
DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code '{ }' in function"),
130-
// TODO: Old parser expected error on line 5: expected '->' for subscript element type
131-
// TODO: Old parser expected error on line 5: single argument function types require parentheses
132-
// TODO: Old parser expected error on line 5: cannot find type 'async' in scope
133-
// TODO: Old parser expected note on line 5: cannot use module 'async' as a type
134-
DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code 'async' in subscript"),
135-
// TODO: Old parser expected error on line 9: 'set' accessor cannot have specifier 'async'
127+
DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have a name"),
128+
DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code 'async' in subscript"),
136129
]
137130
)
138131
}

0 commit comments

Comments
 (0)