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

How to match calls to particular methods in Wart Remover?

我正在写一个 Wart Remover 插件。我想检测对某些可能 return 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 时,顶级树开始到处匹配。我猜 .symbol 在这些情况下是 null...

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

好吧,我终于设法找到了 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]