swiftswift-macro

How to generate an enum with associated values using swift macros?


When trying to generate an enum with associated values, the generated enum is missing comma separating it's values.

This is the my expansion function (I'm using a PeermMacro)

public enum MyMacro: PeerMacro {

    public static func expansion(
        of _: AttributeSyntax,
        providingPeersOf _: some DeclSyntaxProtocol,
        in _: some MacroExpansionContext
    ) throws -> [DeclSyntax] {
        [
            DeclSyntax(
                EnumDeclSyntax(name: "MyEnum") {
                    EnumCaseDeclSyntax {
                        EnumCaseElementSyntax(
                            name: "myCase",
                            parameterClause: EnumCaseParameterClauseSyntax(
                                parameters: EnumCaseParameterListSyntax(
                                    [
                                        EnumCaseParameterSyntax(type: TypeSyntax("Int")),
                                        EnumCaseParameterSyntax(type: TypeSyntax("String"))
                                    ]
                                )
                            )
                        )
                    }
                }
            )
        ]
    }
}

The code above generates the following code, which doesn't compile because there's a comma missing between the associated values:

enum MyEnum {
    case myCase(Int String)
}

How can I update my expansion code so that the generated code contains comma between my associated values?


Solution

  • In the same way that you passed a result builder closure to EnumDeclSyntax and EnumCaseDeclSyntax, you can do that to EnumCaseParameterListSyntax too.

    EnumCaseParameterListSyntax {
        EnumCaseParameterSyntax(type: TypeSyntax("Int")),
        EnumCaseParameterSyntax(type: TypeSyntax("String"))
    )
    

    Many of the AST nodes conform to ExpressibleByStringInterpolation, so you can further simplify this to

    EnumCaseParameterListSyntax {
        "Int"
        "String"
    )
    

    In fact, given a list of enum case parameters, you can generate the whole enum declaration using an interpolated string literal:

    static func enumDeclWithParameters(_ list: [EnumCaseParameterSyntax]) -> DeclSyntax {
        let parameterList = EnumCaseParameterListSyntax {
            for elem in list {
                elem
            }
        }
        return """
        enum MyEnum {
            case myCase(\(parameterList)
        }
        """
    }