使用 Scala 编译器插件在特征中注入对基类方法的调用

问题描述 投票:0回答:1

我正在尝试编写一个编译器插件,它将在 Scala 的特征中注入对基类方法的调用。对于以下输入源:

class Component {
  def valCallback[T](ref: T, name: String): T = ???
}
trait MyTrait {
  this: Component =>
  val v = 5
}

我希望转换结果看起来像:

trait MyTrait {
  this: Component =>
  val v = valCallback(5, "v")
}

这是我到目前为止所得到的:

object ValCallbackTransformer extends Transformer {
  override def transform(tree: global.Tree): global.Tree = {
    val transformed = super.transform(tree)
    transformed match {
      case cd: ClassDef if cd.mods.hasFlag(Flag.TRAIT) =>
        // ... check of self type omitted ...
        val clazz = cd.impl.self.tpt.tpe.parents.find(_.toString == "Component").get.typeSymbol
        val func = clazz.tpe.members.find(_.name.toString == "valCallback").get
        val body = cd.impl.body.map {
          case vd: ValDef if !vd.mods.isParamAccessor && vd.rhs.nonEmpty =>
            val lit = Literal(Constant(vd.getterName.toString))
            val thiz = This(clazz)
            val sel = Select(thiz, func)
            val appl = Apply(sel, List(vd.rhs, lit))
            
            thiz.tpe = clazz.tpe
            sel.tpe = func.tpe
            appl.tpe = definitions.UnitTpe
            lit.setType(definitions.StringTpe)
            treeCopy.ValDef(vd, vd.mods, vd.name, vd.tpt, appl)
          case e => e
        }
        val impl = treeCopy.Template(cd.impl, cd.impl.parents, cd.impl.self, body)
        treeCopy.ClassDef(cd, cd.mods, cd.name, cd.tparams, impl)
      case other => transformed
    }
  }
}

但是,编译器会抱怨以下内容:

Internal error: unable to find the outer accessor symbol of trait MyTrait
[error]   val v = 5

什么是特征的“外部访问器”?为什么编译器找不到它?我应该如何正确构造Apply

更新

:我设法让它工作(参见发布的答案),但是有点令人困惑,为什么我必须从特征中调用 this 指针上的方法。

scala abstract-syntax-tree scala-compiler
1个回答
0
投票
Select

This(<trait type>)
的方法。但是,由于该函数本身不存在于特征类中,因此我需要在 self 类型中单独找到它。工作版本:
val clazz = cd.impl.symbol.owner
val valCallbackClazz = cd.impl.self.tpt.tpe.parents.find(_.toString == "Component").get.typeSymbol
val func = valCallbackClazz.tpe.members.find(_.name.toString == "valCallback").get
val body = cd.impl.body.map {
  // ....
  val thiz = This(clazz)
  val sel = Select(thiz, func)
  // ...
}

© www.soinside.com 2019 - 2024. All rights reserved.