diff --git a/Sources/MMIOMacros/SwiftSyntaxExtensions/ArgumentParsing/ParsableMacro.swift b/Sources/MMIOMacros/SwiftSyntaxExtensions/ArgumentParsing/ParsableMacro.swift index bb09cf8a..41f2005d 100644 --- a/Sources/MMIOMacros/SwiftSyntaxExtensions/ArgumentParsing/ParsableMacro.swift +++ b/Sources/MMIOMacros/SwiftSyntaxExtensions/ArgumentParsing/ParsableMacro.swift @@ -49,11 +49,11 @@ extension ParsableMacro { var attributeWithPlaceholders = AttributeSyntax( atSign: .atSignToken(), attributeName: TypeSyntax(stringLiteral: baseName)) - var arguments: LabeledExprListSyntax? = nil + var arguments = LabeledExprListSyntax() for child in Mirror(reflecting: Self()).children { guard let child = child.value as? any ArgumentProtocol else { continue } - if arguments == nil { + if arguments.isEmpty { signature += "(" attributeWithPlaceholders.leftParen = .leftParenToken() arguments = .init() @@ -67,10 +67,15 @@ extension ParsableMacro { expression: EditorPlaceholderExprSyntax( placeholder: .identifier("<#\(child.typePlaceholder)#>"))) - arguments?.append(argument) + if !arguments.isEmpty { + let lastIndex = arguments.index(before: arguments.endIndex) + arguments[lastIndex].trailingComma = .commaToken(trailingTrivia: .space) + } + + arguments.append(argument) } - if let arguments = arguments { + if !arguments.isEmpty { signature += ")" attributeWithPlaceholders.arguments = .argumentList(arguments) attributeWithPlaceholders.rightParen = .rightParenToken() diff --git a/Tests/MMIOMacrosTests/SwiftSyntaxExtensions/ArgumentParsingTests/ArgumentParsing/ParsableMacroTests.swift b/Tests/MMIOMacrosTests/SwiftSyntaxExtensions/ArgumentParsingTests/ArgumentParsing/ParsableMacroTests.swift index 664c8339..08fe54fb 100644 --- a/Tests/MMIOMacrosTests/SwiftSyntaxExtensions/ArgumentParsingTests/ArgumentParsing/ParsableMacroTests.swift +++ b/Tests/MMIOMacrosTests/SwiftSyntaxExtensions/ArgumentParsingTests/ArgumentParsing/ParsableMacroTests.swift @@ -19,24 +19,25 @@ import XCTest protocol MMIOArgumentParsingMacro: MMIOMemberMacro {} extension MMIOArgumentParsingMacro { static var memberMacroSuppressParsingDiagnostics: Bool { false } + mutating func expansion( of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: MacroContext ) throws -> [DeclSyntax] { [] } + + mutating func update( + label: String, + from expression: ExprSyntax, + in context: MacroContext + ) throws { + fatalError() + } } final class ParsableMacroTests: XCTestCase { func test_noArguments_parse() { - struct A: MMIOArgumentParsingMacro { - mutating func update( - label: String, - from expression: ExprSyntax, - in context: MacroContext - ) throws { - fatalError() - } - } + struct A: MMIOArgumentParsingMacro {} // Good... assertMacroExpansion( @@ -526,4 +527,45 @@ final class ParsableMacroTests: XCTestCase { ], macros: ["A": A.self]) } + + func test_many_signatures() { + struct A: MMIOArgumentParsingMacro {} + struct B: MMIOArgumentParsingMacro { + @Argument(label: "foo") + var foo: Int + } + struct C: MMIOArgumentParsingMacro { + @Argument(label: "foo") + var foo: Int + + @Argument(label: "bar") + var bar: Int + } + struct D: MMIOArgumentParsingMacro { + @Argument(label: "foo") + var foo: Int + } + struct E: MMIOArgumentParsingMacro { + @Argument(label: "foo") + var foo: Int? + } + struct F: MMIOArgumentParsingMacro { + @Argument(label: "foo") + var foo: [Int] + } + + XCTAssertEqual(A.signature, "@A") + XCTAssertEqual(B.signature, "@B(foo:)") + XCTAssertEqual(C.signature, "@C(foo:bar:)") + XCTAssertEqual(D.signature, "@D(foo:)") + XCTAssertEqual(E.signature, "@E(foo:)") + XCTAssertEqual(F.signature, "@F(foo:)") + + XCTAssertEqual("\(A.attributeWithPlaceholders)", "@A") + XCTAssertEqual("\(B.attributeWithPlaceholders)", "@B(foo: <#Int#>)") + XCTAssertEqual("\(C.attributeWithPlaceholders)", "@C(foo: <#Int#>, bar: <#Int#>)") + XCTAssertEqual("\(D.attributeWithPlaceholders)", "@D(foo: <#Int#>)") + XCTAssertEqual("\(E.attributeWithPlaceholders)", "@E(foo: <#Int#>)") + XCTAssertEqual("\(F.attributeWithPlaceholders)", "@F(foo: <#Int#>)") + } }