确定scala编译器插件

时间:2015-07-26 16:36:53

标签: scala

如何在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守卫就抓住了这个!但不是其他的。

0 个答案:

没有答案