如果您只需要一个额外的初始值设定项,MemberMacro
可以生成:
enum CountedMacro: MemberMacro {
static func expansion(of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
let initialisers = declaration.memberBlock.members.compactMap { member in
member.decl.as(InitializerDeclSyntax.self)
}
guard initialisers.count > 0 else {
context.diagnose(...)
return []
}
let newInitialisers = initialisers.map(generateNewInitialiser).map(DeclSyntax.init)
return ["var count = 0"] + newInitialisers
}
static func generateNewInitialiser(from initialiser: InitializerDeclSyntax) -> InitializerDeclSyntax {
var newInitialiser = initialiser
// add parameter
let newParameterList = FunctionParameterListSyntax {
newInitialiser.signature.parameterClause.parameters
"count: Int"
}
newInitialiser.signature.parameterClause.parameters = newParameterList
// add statement initialising count
newInitialiser.body?.statements.append("self.count = count")
return newInitialiser
}
}
如果要在现有的初始化器中添加一个新的count
参数,宏目前不能这样做.一种解决方法是要求宏的用户将初始化器标记为私有.然后,宏可以 for each 专用初始化器生成新的public
个初始化器.这实际上"隐藏"了现有的初始值设定项.
// in generateNewInitialiser...
let modifiersToBeRemoved: [TokenSyntax] = ["public", "private", "internal", "fileprivate", "required"]
newInitialiser.modifiers = newInitialiser.modifiers.filter { m in modifiersToBeRemoved.contains { m.name == $0 } }
newInitialiser.modifiers.insert(DeclModifierSyntax(name: "public"), at: newInitialiser.modifiers.startIndex)
另一种更灵活的设计是将其拆分为three个宏:
@Counted
是一个成员宏,它添加var count = 0
以及附加的member attribute macro that adds
@CountedInitialiserto all initialisers without a
@CountedIgnored`宏.
@CountedInitialiser
是使用generateNewInitialiser
生成公共初始值设定项的对等宏.
@CountedIgnored
是不执行任何操作的对等宏.它仅用于告诉@Counted
忽略初始值设定项
这允许宏的用户准确地 Select 他们想要增加的初始值设定项.例如:
@Counted
struct Foo {
let name: String
@CountedIgnored
init() { name = "Default" }
// @CountedInitialiser will be added to this when @Counted expands
private init(name: String) { self.name = name }
}
根据SWIFT数据,@Observable
个(@ObservationTracked
/@ObservationIgnored
)和@Model
个(@_PersistedProperty
/@Transient
)可以看到类似的技术.