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.
ahoppen
(Alex Hoppen)
2
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.
la-pieuvre
(La Pieuvre)
3
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.