From 124c757890849bab9806cfa9c16f5398cf9aaeb1 Mon Sep 17 00:00:00 2001 From: MahdiBM Date: Fri, 12 Jul 2024 23:57:28 +0330 Subject: [PATCH] better code, more tests --- .../EnumeratorMacroType.swift | 36 ++- .../EnumeratorMacroTests.swift | 231 ++++++++++++++++-- 2 files changed, 239 insertions(+), 28 deletions(-) diff --git a/Sources/EnumeratorMacroImpl/EnumeratorMacroType.swift b/Sources/EnumeratorMacroImpl/EnumeratorMacroType.swift index 497ec82..2f760f1 100644 --- a/Sources/EnumeratorMacroImpl/EnumeratorMacroType.swift +++ b/Sources/EnumeratorMacroImpl/EnumeratorMacroType.swift @@ -2,6 +2,7 @@ import SwiftDiagnostics import SwiftSyntax import SwiftSyntaxMacros import SwiftParser +import SwiftParserDiagnostics import Mustache import Foundation @@ -89,23 +90,34 @@ extension EnumeratorMacroType: MemberMacro { let decls = SourceFileSyntax.parse( from: &parser ).statements.compactMap { statement -> DeclSyntax? in - if statement.hasError { - context.diagnose( - Diagnostic( + let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: statement) + let hasError = diagnostics.contains(where: { $0.diagMessage.severity == .error }) + if hasError { + context.diagnose(.init( + node: codeSyntax, + message: MacroError.renderedSyntaxContainsErrors(statement.description) + )) + } + for diagnostic in diagnostics { + if diagnostic.diagMessage.severity == .error { + context.diagnose(.init( node: codeSyntax, - message: MacroError.renderedSyntaxContainsErrors(statement.description) - ) - ) + position: diagnostic.position, + message: diagnostic.diagMessage, + highlights: diagnostic.highlights, + notes: diagnostic.notes, + fixIts: diagnostic.fixIts + )) + } else if let /*fixIt*/_ = diagnostic.fixIts.first { + /// TODO: Apply the fixit + } + } + if hasError { return nil } switch DeclSyntax(statement.item) { case let .some(declSyntax): - if declSyntax.hasWarning { - return declSyntax - // TODO: try to fix the warnings using SwiftSyntax-provided functions - } else { - return declSyntax - } + return declSyntax case .none: context.diagnose( Diagnostic( diff --git a/Tests/EnumeratorMacroTests/EnumeratorMacroTests.swift b/Tests/EnumeratorMacroTests/EnumeratorMacroTests.swift index 48c4551..938a592 100644 --- a/Tests/EnumeratorMacroTests/EnumeratorMacroTests.swift +++ b/Tests/EnumeratorMacroTests/EnumeratorMacroTests.swift @@ -277,6 +277,93 @@ final class EnumeratorMacroTests: XCTestCase { ) } + func testDiagnosesNoArguments() throws { + assertMacroExpansion( + #""" + @Enumerator + enum TestEnum { + case a + } + """#, + expandedSource: #""" + enum TestEnum { + case a + } + """#, + diagnostics: [.init( + id: .init( + domain: "EnumeratorMacro.MacroError", + id: "macroDeclarationHasNoArguments" + ), + message: """ + The macro declaration needs to have at least 1 String-Literal argument + """, + line: 1, + column: 1, + severity: .error + )], + macros: EnumeratorMacroEntryPoint.macros + ) + } + + func testDiagnosesEmptyArguments() throws { + assertMacroExpansion( + #""" + @Enumerator + enum TestEnum { + case a + } + """#, + expandedSource: #""" + enum TestEnum { + case a + } + """#, + diagnostics: [.init( + id: .init( + domain: "EnumeratorMacro.MacroError", + id: "macroDeclarationHasNoArguments" + ), + message: """ + The macro declaration needs to have at least 1 String-Literal argument + """, + line: 1, + column: 1, + severity: .error + )], + macros: EnumeratorMacroEntryPoint.macros + ) + } + + func testDiagnosesUnacceptableArguments() throws { + assertMacroExpansion( + #""" + @Enumerator(myVariable) + enum TestEnum { + case a + } + """#, + expandedSource: #""" + enum TestEnum { + case a + } + """#, + diagnostics: [.init( + id: .init( + domain: "EnumeratorMacro.MacroError", + id: "allArgumentsMustBeStringLiterals" + ), + message: """ + All arguments must be string literals, but found: myVariable + """, + line: 1, + column: 1, + severity: .error + )], + macros: EnumeratorMacroEntryPoint.macros + ) + } + func testDiagnosesBadMustacheTemplate() throws { assertMacroExpansion( #""" @@ -339,26 +426,138 @@ final class EnumeratorMacroTests: XCTestCase { case testCase(testValue: String) } """#, - diagnostics: [.init( - id: .init( - domain: "EnumeratorMacro.MacroError", - id: "renderedSyntaxContainsErrors" + diagnostics: [ + .init( + id: .init( + domain: "EnumeratorMacro.MacroError", + id: "renderedSyntaxContainsErrors" + ), + message: """ + Rendered syntax contains errors: + enum Subtype: String { + case "a" + case "b" + case "testCase" + } + """, + line: 1, + column: 13, + severity: .error ), - message: """ - Rendered syntax contains errors: - enum Subtype: String { - case "a" - case "b" - case "testCase" - } - """, - line: 1, - column: 13, - severity: .error - )], + .init( + id: .init( + domain: "SwiftParser", + id: "MissingNodesError" + ), + message: """ + expected identifier in enum case + """, + line: 2, + column: 17, + severity: .error, + fixIts: [.init( + message: "insert identifier" + )] + ), + .init( + id: .init( + domain: "SwiftParser", + id: "UnexpectedNodesError" + ), + message: """ + unexpected code '"a"' before enum case + """, + line: 2, + column: 17, + severity: .error + ), + .init( + id: .init( + domain: "SwiftParser", + id: "MissingNodesError" + ), + message: """ + expected identifier in enum case + """, + line: 3, + column: 7, + severity: .error, + fixIts: [.init( + message: "insert identifier" + )] + ), + .init( + id: .init( + domain: "SwiftParser", + id: "UnexpectedNodesError" + ), + message: """ + unexpected code '"b"' before enum case + """, + line: 3, + column: 7, + severity: .error + ), + .init( + id: .init( + domain: "SwiftParser", + id: "MissingNodesError" + ), + message: """ + expected identifier in enum case + """, + line: 4, + column: 5, + severity: .error, + fixIts: [.init( + message: "insert identifier" + )] + ), + .init( + id: .init( + domain: "SwiftParser", + id: "UnexpectedNodesError" + ), + message: """ + unexpected code '"testCase"' in enum + """, + line: 4, + column: 5, + severity: .error + ), + ], macros: EnumeratorMacroEntryPoint.macros ) } + +// func testAppliesFixIts() throws { +// assertMacroExpansion( +// #""" +// @Enumerator(""" +// var value: Int { +// a 1️⃣\u{a0}+ 2 +// } +// """) +// enum TestEnum { +// case a(val1: String, Int) +// case b +// case testCase(testValue: String) +// } +// """#, +// expandedSource: #""" +// enum TestEnum { +// case a(val1: String, Int) +// case b +// case testCase(testValue: String) +// +// var value: Int { +// a + 2 +// } +// } +// """#, +// macros: EnumeratorMacroEntryPoint.macros +// ) +// } } @Enumerator("""