如何用Swift宏从函数参数初始化带关联值的枚举?
从FunctionDeclSyntax提取参数生成Mock枚举关联值与调用记录
核心思路
解决这个问题的关键是利用SwiftSyntax的FunctionDeclSyntax和ParameterSyntax相关API,精准提取函数的内部参数名与类型,分别用于生成枚举关联值的定义,以及calls.append调用里的参数传递逻辑。
分步实现
获取函数参数列表
从FunctionDeclSyntax实例中,通过signature.parameterClause.parameters拿到所有参数的ParameterSyntax数组,每个实例包含了参数的外部名、内部名和类型信息。提取参数的内部名与类型
对于每个ParameterSyntax:
- 内部名取
secondName(这是方法实现中实际可用的变量名) - 参数类型直接取
type属性
比如处理func myFunction(_ name: String, count: Int)时,会得到两组数据:
- 第一个参数:内部名
name,类型String - 第二个参数:内部名
count,类型Int
- 生成枚举Case的关联值定义
将提取到的参数名和类型拼接成枚举case的关联值格式,示例代码如下:
func generateCallEnumCase(from function: FunctionDeclSyntax) -> EnumCaseElementSyntax { let parameterList = function.signature.parameterClause.parameters let associatedValues = parameterList.compactMap { param in guard let paramName = param.secondName?.text, let paramType = param.type.as(IdentifierTypeSyntax.self) else { return nil } return TupleTypeElementSyntax( name: TokenSyntax.identifier(paramName), colon: .colonToken(), type: paramType ) } return EnumCaseElementSyntax( name: TokenSyntax.identifier(function.name.text), associatedValue: TupleTypeSyntax(elements: TupleTypeElementListSyntax(associatedValues)) ) }
这段代码会为上面的示例函数生成:case myFunction(name: String, count: Int)
- 生成calls.append的调用语句
复用参数的内部名,拼接成调用枚举case的代码,示例代码如下:
func generateCallAppendStatement(from function: FunctionDeclSyntax) -> FunctionCallExprSyntax { let parameterList = function.signature.parameterClause.parameters let callArguments = parameterList.compactMap { param in guard let paramName = param.secondName?.text else { return nil } return FunctionCallArgumentSyntax( label: TokenSyntax.identifier(paramName), colon: .colonToken(), expression: IdentifierExprSyntax(identifier: TokenSyntax.identifier(paramName)) ) } return FunctionCallExprSyntax( calledExpression: MemberAccessExprSyntax( base: IdentifierExprSyntax(identifier: TokenSyntax.identifier("calls")), name: TokenSyntax.identifier("append") ), leftParen: .leftParenToken(), arguments: FunctionCallArgumentListSyntax(callArguments), rightParen: .rightParenToken() ) }
这段代码会为示例函数生成:calls.append(.myFunction(name: name, count: count))
整合到Mock类生成流程
遍历ProtocolDeclSyntax的成员时,筛选出所有FunctionDeclSyntax实例,分别用上述方法生成枚举Case和方法内的调用语句,再将这些片段组合成完整的Mock类代码即可。
内容的提问来源于stack exchange,提问作者Daniel Amarante
相关产品推荐
相关产品推荐

