微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Scala编译器插件-确定方法是否被覆盖

如何解决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时,我遍历类和方法,以确保仅处理未被覆盖的方法。应用于scala.reflect.internal.Symbols.MethodSymbol.overrides()方法的符号的StatusController.getPath方法包括GenericController.getPath方法

但是,MethodSymbol.overrides()无法返回GenericController.save符号的StatusController.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"
    )
  )

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

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.overrides=List()
tree=def save(entity: Object): String = "parent",symbol.overrides=List()
tree=def <init>(): GenericController = {
  GenericController.super.<init>();
  ()
},symbol.overrides=List()
tree=override <bridge> <artifact> def save(entity: Object): String = StatusController.this.save(entity.$asInstanceOf[String]()),symbol.overrides=List(GenericController.save)
tree=def <init>(): StatusController = {
  StatusController.super.<init>();
  ()
},symbol.overrides=List()

如您所见,在typer阶段之后,方法符号StatusController.getPath覆盖了方法符号GenericController.getPath,而StatusController.save覆盖了GenericController.save。在jvm阶段之后,StatusController.getPath再次覆盖GenericController.getPath,但是它是桥StatusController.save而不是覆盖StatusController.save的普通GenericController.save。因此,我想您将桥接方法的符号与普通方法的符号混淆了。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。