diff --git a/Sources/SwiftParserDiagnostics/MissingNodesError.swift b/Sources/SwiftParserDiagnostics/MissingNodesError.swift index 74f73c0d63f..0c5ad2ca6ca 100644 --- a/Sources/SwiftParserDiagnostics/MissingNodesError.swift +++ b/Sources/SwiftParserDiagnostics/MissingNodesError.swift @@ -161,15 +161,27 @@ func nodesDescriptionAndCommonParent(_ nodes: [some SyntaxProtocol], format: Boo } let partDescriptions = NodesDescriptionPart.descriptionParts(for: missingSyntaxNodes).map({ $0.description(format: format) ?? "syntax" }) - switch partDescriptions.count { + + return (nil, formatDescriptions(partDescriptions)) +} + +/// Formats an array of descriptions into a single string. +/// +/// This function takes an array of descriptions and formats them into a single string. Depending on the number +/// of descriptions in the array, it returns different formatted strings. +/// +/// - Parameter descriptions: An array of descriptions to be formatted. +/// - Returns: A formatted string representing the descriptions. +func formatDescriptions(_ descriptions: [String]) -> String { + switch descriptions.count { case 0: - return (nil, "syntax") + return "syntax" case 1: - return (nil, "\(partDescriptions.first!)") + return descriptions.first! case 2: - return (nil, "\(partDescriptions.first!) and \(partDescriptions.last!)") + return "\(descriptions.first!) and \(descriptions.last!)" default: - return (nil, "\(partDescriptions[0..' and return type"]) + DiagnosticSpec(message: "deinitializers cannot have a return clause", fixIts: ["remove '->' and return type"]) ], fixedSource: """ class FooClassDeinitializerA { @@ -806,12 +829,15 @@ final class InitDeinitTests: ParserTestCase { assertParse( """ class FooClassDeinitializerA { - deinit 1️⃣x 2️⃣-> Void {} + deinit 1️⃣x -> Void {} } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have a name", fixIts: ["remove 'x'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->' and return type"]), + DiagnosticSpec( + message: "deinitializers cannot have a name and return clause", + highlight: "x -> Void", + fixIts: ["remove 'x', '->', and return type"] + ) ], fixedSource: """ class FooClassDeinitializerA { @@ -825,12 +851,15 @@ final class InitDeinitTests: ParserTestCase { assertParse( """ class FooClassDeinitializerA { - deinit1️⃣() 2️⃣-> Void {} + deinit1️⃣() -> Void {} } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have parameters", fixIts: ["remove parameter clause"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->' and return type"]), + DiagnosticSpec( + message: "deinitializers cannot have parameters and return clause", + highlight: "() -> Void", + fixIts: ["remove parameter clause, '->', and return type"] + ) ], fixedSource: """ class FooClassDeinitializerA { @@ -844,13 +873,15 @@ final class InitDeinitTests: ParserTestCase { assertParse( """ class FooClassDeinitializerA { - deinit 1️⃣x2️⃣() 3️⃣-> Void {} + deinit 1️⃣x() -> Void {} } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have a name", fixIts: ["remove 'x'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "deinitializers cannot have parameters", fixIts: ["remove parameter clause"]), - DiagnosticSpec(locationMarker: "3️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->' and return type"]), + DiagnosticSpec( + message: "deinitializers cannot have a name, parameters, and return clause", + highlight: "x() -> Void", + fixIts: ["remove 'x', parameter clause, '->', and return type"] + ) ], fixedSource: """ class FooClassDeinitializerA { @@ -868,8 +899,8 @@ final class InitDeinitTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'async' in effect specifiers", fixIts: ["insert 'async'"]), - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'async', and return type"]), + DiagnosticSpec(message: "expected 'async' in effect specifiers", fixIts: ["insert 'async'"]), + DiagnosticSpec(message: "deinitializers cannot have a return clause", fixIts: ["remove '->', 'async', and return type"]), ], fixedSource: """ class FooClassDeinitializerA { @@ -887,7 +918,7 @@ final class InitDeinitTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'async', and return type"]) + DiagnosticSpec(message: "deinitializers cannot have a return clause", fixIts: ["remove '->', 'async', and return type"]) ], fixedSource: """ class FooClassDeinitializerA { @@ -906,7 +937,7 @@ final class InitDeinitTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected async specifier; did you mean 'async'?", fixIts: ["replace 'await' with 'async'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'async', and return type"]), + DiagnosticSpec(locationMarker: "2️⃣", message: "deinitializers cannot have a return clause", fixIts: ["remove '->', 'async', and return type"]), ], fixedSource: """ class FooClassDeinitializerA { @@ -924,7 +955,7 @@ final class InitDeinitTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'await', and return type"]) + DiagnosticSpec(message: "deinitializers cannot have a return clause", fixIts: ["remove '->', 'await', and return type"]) ], fixedSource: """ class FooClassDeinitializerA { @@ -942,7 +973,7 @@ final class InitDeinitTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'throws', and return type"]) + DiagnosticSpec(message: "deinitializers cannot have a return clause", fixIts: ["remove '->', 'throws', and return type"]) ], fixedSource: """ class FooClassDeinitializerA { @@ -961,7 +992,7 @@ final class InitDeinitTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot throw", fixIts: ["remove 'throws'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'throws', and return type"]), + DiagnosticSpec(locationMarker: "2️⃣", message: "deinitializers cannot have a return clause", fixIts: ["remove '->', 'throws', and return type"]), ], fixedSource: """ class FooClassDeinitializerA { @@ -979,8 +1010,8 @@ final class InitDeinitTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'async' in effect specifiers", fixIts: ["insert 'async'"]), - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'async throws', and return type"]), + DiagnosticSpec(message: "expected 'async' in effect specifiers", fixIts: ["insert 'async'"]), + DiagnosticSpec(message: "deinitializers cannot have a return clause", fixIts: ["remove '->', 'async throws', and return type"]), ], fixedSource: """ class FooClassDeinitializerA { @@ -998,8 +1029,8 @@ final class InitDeinitTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'async' in effect specifiers", fixIts: ["insert 'async'"]), - DiagnosticSpec(locationMarker: "1️⃣", message: "deinitializers cannot have return type", fixIts: ["remove '->', 'throws async', and return type"]), + DiagnosticSpec(message: "expected 'async' in effect specifiers", fixIts: ["insert 'async'"]), + DiagnosticSpec(message: "deinitializers cannot have a return clause", fixIts: ["remove '->', 'throws async', and return type"]), ], fixedSource: """ class FooClassDeinitializerA {