Swift compiler doesn't Synthesize Equatable
and Hashable
conformance for classes due to the complexity of class inheritance hierarchies. 0185-synthesize-equatable-hashable
However, there are still cases where the generated code is helpful especially for a root class. Therefore, I have tried to implement this using Swift Macro. However, I have encountered issues like below:
In short, I can generate necessary code below using macro, but it doesn't compile.
However, the same code will work if it's not generated from macro.
Could someone help here? Many thanks. @Douglas_Gregor
The macro code is below:
public struct Equatable: ConformanceMacro, MemberMacro {
public static func expansion<Declaration: DeclGroupSyntax,
Context: MacroExpansionContext>(of node: AttributeSyntax,
providingConformancesOf declaration: Declaration,
in context: Context) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
guard [SwiftSyntax.SyntaxKind.classDecl, .actorDecl].contains(declaration.kind) else {
throw MacroDiagnostics.errorMacroUsage(message: "Can only be applied to a class or actor")
}
// TODO: inheritance check for classes
return [("Equatable", nil) ]
}
public static func expansion<Declaration: DeclGroupSyntax,
Context: MacroExpansionContext>(of node: AttributeSyntax,
providingMembersOf declaration: Declaration,
in context: Context) throws -> [DeclSyntax] {
guard declaration.as(ClassDeclSyntax.self)?.inheritanceClause == nil else {
throw MacroDiagnostics.errorMacroUsage(message: "Can only be applied to a root class")
}
guard let identifier = declaration.as(ClassDeclSyntax.self)?.identifier.text ?? declaration.as(ActorDeclSyntax.self)?.identifier.text else {
return []
}
let variables = declaration.memberBlock.members.compactMap({ $0.decl.as(VariableDeclSyntax.self)?.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier.text }).map {
"lhs.\($0) == rhs.\($0)"
}.joined(separator: "\n&& ")
return [
DeclSyntax(
"""
static func == (lhs: \(raw: identifier), rhs: \(raw: identifier)) -> Bool {
\(raw: variables)
}
"""
)
]
}
}