Scala 3编译器插件添加混入特质后方法未生效的解决方法
问题:编译器阶段混入特质后方法无法生效的解决办法
问题代码
class ExpPhase extends PluginPhase { import tpd._ val phaseName = "expPhase" override val runsAfter = Set("typer") override def transformTypeDef(tree: TypeDef)(implicit ctx: Context): Tree = if tree.isClassDef && !tree.rhs.symbol.isStatic then // only look at classes & traits, not objects val c2 = QuotesCache.init(ctx.fresh) implicit val quotes = scala.quoted.runtime.impl.QuotesImpl()(using c2) import quotes.reflect.* // Add new parent, SJ_Serialize, so class Person becomes class Person extends SJ_Serialize val cd = ClassDef.copy(classDef)( name = classDef.name, constr = classDef.constructor, parents = classDef.parents :+ TypeTree.of[SJ_Serialize], // SJ_Serialize is a trait having methods selfOpt = classDef.self, body = classDef.body ) val newCd = cd.asInstanceOf[dotty.tools.dotc.ast.tpd.Tree] println(QuotesImpl.showDecompiledTree(newCd)) // see if class modified successfully newCd else tree }
问题描述
上述代码在编译器阶段尝试给目标类混入SJ_Serialize特质,打印结果显示语法树已修改成功,但后续流程中该特质的实现方法未被复制到目标类,导致无法调用这些方法。
解决办法
- 更新类符号的父类信息:仅修改语法树不足以让编译器识别新增的特质父类,必须同步更新类符号的父类列表。可以通过类符号的
addParent方法将SJ_Serialize的符号添加进去,示例代码如下(需适配Dotty的符号API):val serializeSymbol = ctx.definitions.getClass("SJ_Serialize").symbol tree.symbol.addParent(serializeSymbol) - 触发特质方法的混入逻辑:Dotty中特质的方法合成是在
mixin阶段完成的,你需要确保修改后的类能被该阶段处理。可以在修改语法树后,手动触发符号的成员计算,或者确保你的阶段运行在mixin阶段之前(调整runsAfter或runsBefore),让mixin阶段自动处理新增的特质父类,将特质方法复制到目标类中。 - 避免混用Quotes与Dotty内部API:当前代码混用了Quotes的语法树操作和Dotty的
tpd.Tree,容易导致语法树结构不兼容。建议直接使用Dotty内部的ClassDef节点操作来修改父类,比如:val cd = tree.asInstanceOf[ClassDef] val newParents = cd.parents :+ TypeTree.of[SJ_Serialize](using ctx) val newCd = cd.copy(parents = newParents) - 验证阶段执行顺序:确保你的
expPhase运行在typer之后、mixin之前。可以通过override val runsBefore = Set("mixin")来明确阶段顺序,保证修改后的语法树能被后续的混入逻辑处理。
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

