Scala编译器插件-确定方法是否被重写



我正在编写一个Scala编译器插件,其中包含一个在";jvm";需要知道方法被重写的阶段。以下是我正在处理的一个测试用例的提炼版本:

abstract class GenericController[T] {
    def getPath():String
    def save(entity:T):String = "parent"
}
class StatusController extends GenericController[String] {
    override def getPath():String = "/statuses"
    override def save(entity:String):String = "child"
}

在遍历AST时,我对类和方法进行迭代,确保只处理未被重写的方法。应用于StatusController.getPath方法的符号的scala.reflect.internal.Symbols.MethodSymbol.overrides()方法将包括用于GenericController.getPath的方法。

然而,MethodSymbol.overrides()未能返回用于StatusController.save符号的GenericController.save方法。似乎泛型类型使这种行为复杂化了,这应该在另一个编译器阶段之后完成吗?

让我们定义测试编译器插件。

core/src/main/scala/main.scala

abstract class GenericController[T] {
  def getPath():String
  def save(entity:T):String = "parent"
}
class StatusController extends GenericController[String] {
  override def getPath():String = "/statuses"
  override def save(entity:String):String = "child"
}

plugin/src/main/resources/scalac plugin.xml

<plugin>
    <name>MyCompilerPlugin</name>
    <classname>compilerPlugin.MyCompilerPlugin</classname>
</plugin>

plugin/src/main/scala/compilerPlugin/MyCompilerPlugin.scala

package compilerPlugin
import scala.tools.nsc.{Global, Phase}
import scala.tools.nsc.plugins.{Plugin, PluginComponent}
class MyCompilerPlugin(val global: Global) extends Plugin {
  import global._
  val name = "MyCompilerPlugin"
  val description = "My compiler plugin"
  val components: List[PluginComponent] = List[PluginComponent](MyComponent)
  private object MyComponent extends PluginComponent {
    val global: MyCompilerPlugin.this.global.type = MyCompilerPlugin.this.global
    val runsAfter: List[String] = List[String](/*"typer"*/"jvm")
    val phaseName: String = MyCompilerPlugin.this.name
    def newPhase(_prev: Phase) = new MyPhase(_prev)
    class MyPhase(prev: Phase) extends StdPhase(prev) {
      override def name: String = MyCompilerPlugin.this.name
      def apply(unit: CompilationUnit): Unit = {
        for (tree@q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" <- unit.body) {
          println(s"tree=$tree, symbol=${tree.symbol.fullName}, symbol.overrides=${tree.symbol.overrides.map(_.fullName)}")
        }
      }
    }
  }
}

build.sbt

ThisBuild / name := "compiler-plugin-demo"
lazy val commonSettings = Seq(
  scalaVersion := "2.13.3",
  version := "1.0",
)
lazy val plugin = project
  .settings(
    commonSettings,
    libraryDependencies ++= Seq(
      scalaOrganization.value % "scala-reflect" % scalaVersion.value,
      scalaOrganization.value % "scala-compiler" % scalaVersion.value,
    )
  )
lazy val core = project
  .settings(
    commonSettings,
    scalacOptions ++= Seq(
      "-Xplugin:plugin/target/scala-2.13/plugin_2.13-1.0.jar",
      "-Xplugin-require:MyCompilerPlugin"
    )
  )

如果你在typer阶段之后运行(sbt reload; clean; plugin/package; core/compile(这个编译器插件,它会打印

tree=def <init>(): GenericController[T] = {
  GenericController.super.<init>();
  ()
}, symbol=GenericController.<init>, symbol.overrides=List()
tree=def getPath(): String, symbol=GenericController.getPath, symbol.overrides=List()
tree=def save(entity: T): String = "parent", symbol=GenericController.save, symbol.overrides=List()
tree=def <init>(): StatusController = {
  StatusController.super.<init>();
  ()
}, symbol=StatusController.<init>, symbol.overrides=List()
tree=override def getPath(): String = "/statuses", symbol=StatusController.getPath, symbol.overrides=List(GenericController.getPath)
tree=override def save(entity: String): String = "child", symbol=StatusController.save, symbol.overrides=List(GenericController.save)

但如果你在jvm阶段之后运行它,它会打印

tree=def getPath(): String, symbol=GenericController.getPath, symbol.overrides=List()
tree=def save(entity: Object): String = "parent", symbol=GenericController.save, symbol.overrides=List()
tree=def <init>(): GenericController = {
  GenericController.super.<init>();
  ()
}, symbol=GenericController.<init>, symbol.overrides=List()
tree=override def getPath(): String = "/statuses", symbol=StatusController.getPath, symbol.overrides=List(GenericController.getPath)
tree=override def save(entity: String): String = "child", symbol=StatusController.save, symbol.overrides=List()
tree=override <bridge> <artifact> def save(entity: Object): String = StatusController.this.save(entity.$asInstanceOf[String]()), symbol=StatusController.save, symbol.overrides=List(GenericController.save)
tree=def <init>(): StatusController = {
  StatusController.super.<init>();
  ()
}, symbol=StatusController.<init>, symbol.overrides=List()

如您所见,在typer阶段之后,方法符号StatusController.getPath覆盖方法符号GenericController.getPathStatusController.save覆盖GenericController.save。在CCD_ 15之后,相位CCD_ 16再次覆盖CCD_ 17,但覆盖GenericController.save的是桥CCD_ 18而不是普通的CCD_ 19。所以我猜你们把桥牌法的符号和普通法的符号混淆了。

相关内容

  • 没有找到相关文章

最新更新