Skip to content

Commit e63f6b7

Browse files
committed
Handle DeinitializerDeclSyntax errors in a single diagnostic
1 parent e0fa85f commit e63f6b7

File tree

4 files changed

+129
-92
lines changed

4 files changed

+129
-92
lines changed

Sources/SwiftParserDiagnostics/MissingNodesError.swift

+17-5
Original file line numberDiff line numberDiff line change
@@ -161,15 +161,27 @@ func nodesDescriptionAndCommonParent(_ nodes: [some SyntaxProtocol], format: Boo
161161
}
162162

163163
let partDescriptions = NodesDescriptionPart.descriptionParts(for: missingSyntaxNodes).map({ $0.description(format: format) ?? "syntax" })
164-
switch partDescriptions.count {
164+
165+
return (nil, formatDescriptions(partDescriptions))
166+
}
167+
168+
/// Formats an array of descriptions into a single string.
169+
///
170+
/// This function takes an array of descriptions and formats them into a single string. Depending on the number
171+
/// of descriptions in the array, it returns different formatted strings.
172+
///
173+
/// - Parameter descriptions: An array of descriptions to be formatted.
174+
/// - Returns: A formatted string representing the descriptions.
175+
func formatDescriptions(_ descriptions: [String]) -> String {
176+
switch descriptions.count {
165177
case 0:
166-
return (nil, "syntax")
178+
return "syntax"
167179
case 1:
168-
return (nil, "\(partDescriptions.first!)")
180+
return descriptions.first!
169181
case 2:
170-
return (nil, "\(partDescriptions.first!) and \(partDescriptions.last!)")
182+
return "\(descriptions.first!) and \(descriptions.last!)"
171183
default:
172-
return (nil, "\(partDescriptions[0..<partDescriptions.count - 1].joined(separator: ", ")), and \(partDescriptions.last!)")
184+
return "\(descriptions[0..<descriptions.count - 1].joined(separator: ", ")), and \(descriptions.last!)"
173185
}
174186
}
175187

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

+27-31
Original file line numberDiff line numberDiff line change
@@ -788,40 +788,36 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
788788
if shouldSkip(node) {
789789
return .skipChildren
790790
}
791-
if let unexpected = node.unexpectedBetweenDeinitKeywordAndEffectSpecifiers,
792-
let name = unexpected.presentTokens(satisfying: { $0.tokenKind.isIdentifier == true }).only
793-
{
794-
addDiagnostic(
795-
name,
796-
.deinitCannotHaveName,
797-
fixIts: [
798-
FixIt(message: RemoveNodesFixIt(name), changes: .makeMissing(name))
799-
],
800-
handledNodes: [name.id]
801-
)
802-
}
803-
if let unexpected = node.unexpectedBetweenDeinitKeywordAndEffectSpecifiers,
804-
let params = unexpected.compactMap({ $0.as(FunctionParameterClauseSyntax.self) }).only
805-
{
806-
addDiagnostic(
807-
params,
808-
.deinitCannotHaveParameters,
809-
fixIts: [
810-
FixIt(message: RemoveNodesFixIt(params), changes: .makeMissing(params))
811-
],
812-
handledNodes: [params.id]
813-
)
814-
}
815-
if let unexpected = node.unexpectedBetweenEffectSpecifiersAndBody,
816-
let returnType = unexpected.compactMap({ $0.as(ReturnClauseSyntax.self) }).only
817-
{
791+
792+
let name: TokenSyntax? = node
793+
.unexpectedBetweenDeinitKeywordAndEffectSpecifiers?
794+
.presentTokens(satisfying: \.tokenKind.isIdentifier)
795+
.only
796+
797+
let params: FunctionParameterClauseSyntax? = node
798+
.unexpectedBetweenDeinitKeywordAndEffectSpecifiers?
799+
.compactMap({ $0.as(FunctionParameterClauseSyntax.self) })
800+
.only
801+
802+
let returnType: ReturnClauseSyntax? = node
803+
.unexpectedBetweenEffectSpecifiersAndBody?
804+
.compactMap({ $0.as(ReturnClauseSyntax.self) })
805+
.only
806+
807+
let nodes = [Syntax(name), Syntax(params), Syntax(returnType)].compactMap { $0 }
808+
809+
if !nodes.isEmpty {
818810
addDiagnostic(
819-
returnType,
820-
.deinitCannotHaveReturnType,
811+
nodes.first!,
812+
DeinitializerSignatureError(name: name, params: params, returnClause: returnType),
813+
highlights: nodes,
821814
fixIts: [
822-
FixIt(message: RemoveNodesFixIt(returnType), changes: .makeMissing(returnType))
815+
FixIt(
816+
message: RemoveNodesFixIt(nodes),
817+
changes: nodes.map { .makeMissing($0) }
818+
)
823819
],
824-
handledNodes: [returnType.id]
820+
handledNodes: nodes.map { $0.id }
825821
)
826822
}
827823

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

+30-9
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,9 @@ extension DiagnosticMessage where Self == StaticParserError {
120120
public static var defaultOutsideOfSwitch: Self {
121121
.init("'default' label can only appear inside a 'switch' statement")
122122
}
123-
public static var deinitCannotHaveName: Self {
124-
.init("deinitializers cannot have a name")
125-
}
126-
public static var deinitCannotHaveParameters: Self {
127-
.init("deinitializers cannot have parameters")
128-
}
129123
public static var deinitCannotThrow: Self {
130124
.init("deinitializers cannot throw")
131125
}
132-
public static var deinitCannotHaveReturnType: Self {
133-
.init("deinitializers cannot have return type")
134-
}
135126
public static var editorPlaceholderInSourceFile: Self {
136127
.init("editor placeholder in source file")
137128
}
@@ -305,6 +296,36 @@ public struct CannotParseVersionTuple: ParserError {
305296
}
306297
}
307298

299+
public struct DeinitializerSignatureError: ParserError {
300+
public let name: TokenSyntax?
301+
public let params: FunctionParameterClauseSyntax?
302+
public let returnClause: ReturnClauseSyntax?
303+
304+
public var message: String {
305+
var descriptions: [String] = []
306+
307+
if name != nil {
308+
descriptions.append("a name")
309+
}
310+
311+
if params != nil {
312+
descriptions.append("parameters")
313+
}
314+
315+
if returnClause != nil {
316+
var message = "return clause"
317+
318+
if descriptions.isEmpty {
319+
message = "a " + message
320+
}
321+
322+
descriptions.append(message)
323+
}
324+
325+
return "deinitializers cannot have \(formatDescriptions(descriptions))"
326+
}
327+
}
328+
308329
public struct DuplicateEffectSpecifiers: ParserError {
309330
public let correctSpecifier: TokenSyntax
310331
public let unexpectedSpecifier: TokenSyntax

0 commit comments

Comments
 (0)