Swift Macro and diagnostic

Hi everyone,

I'm currently working on a Macro to generate DTO type.
While doing so and to provide a good user experience I try to use Swift-diagnostic to provide error messages and fixit actions.

One of the fixit I'm trying to provide is to ensure that 2 types are identical and if not replace one by the other.

more specifically I use an attached macro of this form :

@ConvertDTOType(from: Int, to: Amount, convert: {Amount(withCents: $0)})
    var amount: Amount

Where "to: TYPE" must be identical to 'amount: TYPE'.

I wrote this fixit :

                 fixIt = [FixIt(
                    message: FixDestTypeMessage(
                        attributeType: destTypeNode.description,
                        propertyType: propertyType.description
                    ),
                    changes: [
                        FixIt.Change.replace(
                            oldNode: Syntax(destTypeNode),
                            newNode: Syntax(propertyType)
                        )
                    ]
                )]

where destTypeNode is the node of the 'to: TYPE' type in the @attribute and propertyType is the type of the property (amount in the previous example).

My problème is that this fixit is greyed out and therefore doesn't work.
However if I switch the 2 values of oldNode and newNode
Then the fixit works be replacing the property type with the attribute type (the opposite of what I want)

Am I missing something here, note that I'm very new with swiftSyntax framework and even newer with swift diagnostic so any help will be appreciated.

Your code looks correct in general. Do you have an example project which you can share that reproduces the problem? If so, I can take a look.

Thanks for you answer.

The project is open source so you can find the full code here : GitHub - OctoPoulpeStudio/DTOMacro: A Swift Macro that generate DTO object.

However here is a simplified version of the problem, without intermediary function call it's a bit messy because there is few things happening

public struct ConvertDTOMacro: PeerMacro {
    public static func expansion(of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
        guard let variable = declaration.as(VariableDeclSyntax.self) else {return []}
        var result: [Diagnostic] = []
        
        if let convertAttribute = variable.attributes?.filter({filter(attributeNamed: "ConvertDTOType", element: $0)}).first?.as(AttributeSyntax.self)
        {
            if let arguments = convertAttribute.arguments?.as(LabeledExprListSyntax.self),
               let destinationTypeOrigin =  arguments.first(where: { tupleExprElementSyntax in tupleExprElementSyntax.label?.text == "to" })?.expression
            {
                let destinationType = TypeAnnotationSyntax(type: TypeSyntax("\(raw: destinationTypeOrigin)"))
                let propertyType = variable.bindings.first?.typeAnnotation?.as(TypeAnnotationSyntax.self)
                
                if !SNC.compare(propertyType , destinationType) {
                    if let propertyType = variable.bindings.first?.typeAnnotation?.as(TypeAnnotationSyntax.self) {
                        
                        let fixIt = [FixIt(
                            message: FixDestTypeMessage(
                                attributeType: destinationType.type.description,
                                propertyType: propertyType.type.description
                            ),
                            changes: [
                                FixIt.Change.replace(
                                    oldNode: Syntax(destinationType),
                                    newNode: Syntax(propertyType.type)
                                )
                            ]
                        )]
                        
                        let diag = Diagnostic(
                            node: variable,
                            message: DTODiagnostic.DTOConvertFromTypeDontMatchError,
                            fixIts: fixIt
                        )
                        result.append(diag)
                    }
                }
                for diag in result {
                    context.diagnose(diag)
                }
                
            }
        }
        return []
    }
    
    internal static func filter(attributeNamed name:String, element: AttributeListSyntax.Element) -> Bool {
        guard let attribute = element.as(AttributeSyntax.self) else { return false}
        return attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text == name
    }
}

And here is the line with error in a struct :

    @ConvertDTOType(from: Int, to: Amount, convert: {Amount(withCents: $0)})
    var status: State

I have the felling that this is because destinationType is kind of a new node created out of the data from the attribute

let destinationType = TypeAnnotationSyntax(type: TypeSyntax("\(raw: destinationTypeOrigin)"))

However I tried with the orignal node and I had the same problem...

And here is the error message with the greyed out replacement fixit

Thanks a lot for your help.