如何将调用与Wart Remover中的特定方法匹配?

时间:2015-07-15 18:11:49

标签: scala scala-reflect

我正在写一个Wart Remover插件。我想检测对可能返回null的某些方法的调用,例如System.getProperty。但是,我试图匹配它们的所有方法都失败了。这是我最近的尝试:

import org.brianmckenna.wartremover.{WartTraverser, WartUniverse}
import scala.language.{implicitConversions,reflectiveCalls}

object LegacyJavaAPIs extends WartTraverser {

  def apply(u: WartUniverse): u.Traverser = {
    import u.universe._

    @SuppressWarnings(Array("org.brianmckenna.wartremover.warts.AsInstanceOf",
      "com.example.wart.LegacyJavaAPIs"))
    val getProperty = reify(System.getProperty("x")).tree match {
      case Apply(a,b) => a.toString
    }

    new u.Traverser {
      override def traverse(tree: Tree): Unit = {
        if(tree.toString == getProperty)
          u.error(tree.pos,
            """|Do not use System.getProperty - it may return null.
               |Use sys.props instead.""".stripMargin)
        else
          super.traverse(tree)
      }
    }
  }
}

我尝试的大多数方法都会导致应该出错的行没有错误 - 尽管当我尝试.symbol而不是.toString时,顶级树开始在任何地方匹配。我猜这些情境中.symbolnull ......

我还尝试使用showRaw并复制并粘贴相关的构造函数,但这不起作用,因为它不是有效的Scala代码 - java.lang出现了showRaw输出中的标识符,而不是包含在它实际包含的任何构造函数中。

1 个答案:

答案 0 :(得分:0)

好吧,我终于设法找到了一个没有被破坏的scala反射API的一部分:

object LegacyJavaAPIs extends WartTraverser {

  def apply(u: WartUniverse): u.Traverser = {
    import u.universe._

    @SuppressWarnings(Array("org.brianmckenna.wartremover.warts.AsInstanceOf"))
    val systemStatic = rootMirror.typeOf[System].companion

    val getProperty = systemStatic.decl(TermName("getProperty")).alternatives

    new u.Traverser {
      override def traverse(tree: Tree): Unit =
        if(getProperty.contains(tree.symbol))
          u.error(tree.pos,
            """|Do not use System.getProperty - it may return null.
               |Use sys.props.get instead.""".stripMargin)
        else
          super.traverse(tree)
    }
  }

我们需要致电companion(即使java.lang.System没有伴侣对象,因为它是Java)因为scala-reflect没有认识到静态方法,并将它们视为伴随对象上的方法。我们还需要致电alternatives,因为getProperty已超载。

如果感兴趣的类型是scala.Predef,则需要稍微调整一下:

val predef = rootMirror.typeOf[scala.Predef.type]