Есть ли способ предотвратить использование определенной функции, предоставляемой зависимостью?

Мое приложение зависит от компиляции библиотеки A. В этой библиотеке есть определенная функция, которая, как я знаю, вызывает проблемы с производительностью. Но он не является устаревшим, и если у вас нет этой предварительной информации, вы можете подумать, что его можно безопасно использовать. Я ищу способ как-то отказаться от этой функции или предотвратить ее прямой вызов.

Можно ли добиться этого с помощью инструментов статического анализа или встроенных флагов компилятора?

2 ответа

Решение

Я проверил scalafix, но его было слишком сложно настроить только для такого типа линтинга. В итоге я использовал средство для удаления бородавок и добавил собственную бородавку.

Пользовательские бородавки находятся в подпроекте, как показано в этом примере.

lazy val myWarts = project.in(file("my-warts")).settings(
  commonSettings,
  libraryDependencies ++= Seq(
  "org.wartremover" % "wartremover" % wartremover.Wart.PluginVersion cross CrossVersion.full
  )
)

lazy val main = project.in(file("main")).settings(
  commonSettings,
  wartremoverWarnings += Wart.custom("mywarts.ExtractOrElse"),
  wartremoverClasspaths ++= {
    (fullClasspath in (myWarts, Compile)).value.map(_.data.toURI.toString)
  }
)

Я создал собственную бородавку, просто изменив встроенную бородавку EitherProjectionPartial.

object ExtractOrElse extends WartTraverser {
  def apply(u: WartUniverse): u.Traverser = {
    import u.universe._

    val extractableJsonAstNode = rootMirror.staticClass("org.json4s.ExtractableJsonAstNode")
    new u.Traverser {
      override def traverse(tree: Tree): Unit = {
        tree match {
          // Ignore trees marked by SuppressWarnings
          case t if hasWartAnnotation(u)(t) =>
          case Select(left, TermName("extractOrElse")) if left.tpe.baseType(extractableJsonAstNode) != NoType =>
            error(u)(tree.pos, "extractOrElse is deprecated - use toOption.map or extract[Option[A]] instead")
            super.traverse(tree)
          case _ => super.traverse(tree)
        }
      }
    }
  }
}

Вы можете написать правило для Scalafix

https://scalacenter.github.io/scalafix/docs/developers/setup.html

Например, давайте не рекомендуем scala.Predef.println

class DeprecateFunction extends SemanticRule("DeprecateFunction") {

  case class Deprecation(position: Position) extends Diagnostic {
    override def message = "Use loggers instead of println"
    override def severity = LintSeverity.Warning
  }

  val deprecatedFunction = SymbolMatcher.normalized("scala.Predef.println")

  override def fix(implicit doc: SemanticDocument): Patch = {
    doc.tree.collect {
      case deprecatedFunction(t: Name) =>
        Patch.lint(Deprecation(t.pos))
    }.asPatch
  }
}

Пример:

object Scalafixdemo {
  println(1)
}

Выход:

[IJ]sbt:scalafix> scalafix --rules=file:rules/src/main/scala/fix/DeprecateFunction.scala
[info] Running scalafix on 1 Scala sources
[warn] .../scalafix/input/src/main/scala/fix/Scalafixdemo.scala:8:3: warning: [DeprecateFunction] Use loggers instead of println
[warn]   println(1)
[warn]   ^^^^^^^
[success] Total time: 2 s, completed 27.05.2020 21:12:13
Другие вопросы по тегам