如何解决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 举报,一经查实,本站将立刻删除。