如何在scalac(2.11)插件中确定Tree对象是否为Option[_]
的子类型?
示例:
class OptionIfPhase(prev: Phase) extends StdPhase(prev) {
override def name = OptionIf.this.name
override def apply(unit: CompilationUnit) {
for (tree @ Apply(TypeApply(Select(rcvr, TermName("map")), typetree), List(param)) <- unit.body;
if rcvr.tpe <:< definitions.OptionClass.tpe)
{
reporter.echo(tree.pos, s"found a map on ${rcvr} with type ${rcvr.tpe} and typelist ${typetree} and arg $param")
}
}
不幸的是,这个if-guard始终是假的。如果我完全删除它,它匹配所有“类型”地图(如预期的那样)。以下是我想要匹配的几个表达式示例:
// All of these maps
val x = Some(4).map(_ * 2)
val xo: Option[Int] = x
xo.map(_ * 3)
None.map(i => "foo")
// Not this one
List(1, 3, 5).map(_ + 1) // removing the if guard completely also matches this
// The inner one
List(Some(1), None, Some(3)).flatMap(_.map(_ * -1))
// And from methods:
def foo: Option[Int] = Some(3)
foo.map(_ + 1)
我试过了:
if rcvr.tpe <:< typeOf[Option[_]]
哪个在REPL中工作(在import scala.reflect.runtime.universe._
之后),但在插件中它会引发异常:“scala.ScalaReflectionException:找不到编译器镜像中的类net.obrg.optionif.OptionIf。”
在for循环中过滤掉已确定为Option [_]类型的表达式上的任何“map”的if条件是什么?
背景:我正在尝试构建一个scala插件,将所有x.map(f)
个表达式(其中x的类型为Option)转换为(if (x.isEmpty) None else Some(f(x.get)))
。
到目前为止,周围的插件样板:
package net.obrg.optionif
import scala.tools.nsc.{Global, Phase}
import scala.tools.nsc.plugins.{Plugin, PluginComponent}
class OptionIf(val global: Global) extends Plugin {
import global._
override val name = "optionif"
override val description = "flattens map operations on options to if statements"
override val components = List[PluginComponent](Component)
private object Component extends PluginComponent {
override val global: OptionIf.this.global.type = OptionIf.this.global
// I'm not 100% on these constraints, but afaik these are appropriate outer bounds.
// The typer phase is, obviously, necessary to determine which objects are of type Option, to begin with.
override val runsAfter = List("typer")
// The refchecks phase seems to do some if-related optimizations ("Eliminate branches in a conditional if the
// condition is a constant"). So put it before this, at least.
override val runsBefore = List("refchecks")
override val phaseName = OptionIf.this.name
override def newPhase(_prev: Phase) = new OptionIfPhase(_prev)
class OptionIfPhase(prev: Phase) extends StdPhase(prev) {
override def name = OptionIf.this.name
override def apply(unit: CompilationUnit) {
for (tree @ Apply(TypeApply(Select(rcvr, TermName("map")), typetree), List(param)) <- unit.body;
if rcvr.tpe <:< definitions.OptionClass.tpe)
{
reporter.echo(tree.pos, s"found a map on ${rcvr} with type ${rcvr.tpe} and typelist ${typetree} and arg $param")
}
}
}
}
}
改编自http://www.scala-lang.org/old/node/140
编辑:我刚刚添加了None.map(x =&gt;“foo”)的测试,而实际上 的if守卫就抓住了这个!但不是其他的。