Scala -Ywarn播放路线文件上未使用的导入触发

Scala -Ywarn播放路线文件上未使用的导入触发,scala,playframework,sbt,scalac,Scala,Playframework,Sbt,Scalac,我希望能够使用-Xfatal warnings和-Ywarn unused import,问题是编译器在包含我的应用程序播放路径的文件上触发错误: [error] /path/to/app/conf/routes: Unused import [error] /path/to/app/conf/routes: Unused import [error] /path/to/app/conf/routes:1: Unused import [error] GET /document/:

我希望能够使用
-Xfatal warnings
-Ywarn unused import
,问题是编译器在包含我的应用程序播放路径的文件上触发错误:

[error] /path/to/app/conf/routes: Unused import
[error] /path/to/app/conf/routes: Unused import
[error] /path/to/app/conf/routes:1: Unused import
[error] GET        /document/:id        my.app.controllers.MyController.getById(id: Int)
其他路线也是如此

是否可以告诉scalac忽略一个文件

Scala版本是
2.11.8

一个可怕的“解决方案”可能是在生成路由之后但在编译任务运行之前删除那些未使用的导入。这是一张草图:

lazy val optimizeRoutesImports = taskKey[Unit]("Remove unused imports from generated routes sources.")

optimizeRoutesImports := {

  def removeUnusedImports(targetFiles: (File) => PathFinder, linesToRemove: Set[String], linesToReplace: Map[String, String]) = {
    val files = targetFiles(crossTarget.value).get
    files foreach { file =>
      val lines = sbt.IO.readLines(file)
      val updatedLines = lines map { line =>
        linesToReplace.getOrElse(line, line)
      } filterNot { line =>
        linesToRemove.contains(line.trim)
      }
      sbt.IO.writeLines(file, updatedLines, append = false)
    }
  }

  removeUnusedImports(
    _ / "routes" / "main" / "controllers" / "ReverseRoutes.scala",
    Set("import ReverseRouteContext.empty"),
    Map(
      "import play.api.mvc.{ QueryStringBindable, PathBindable, Call, JavascriptLiteral }" ->
        "import play.api.mvc.{ QueryStringBindable, PathBindable, Call }",
      "import play.core.routing.{ HandlerDef, ReverseRouteContext, queryString, dynamicString }" ->
        "import play.core.routing.{ ReverseRouteContext, queryString, dynamicString }"
    )
  )

  removeUnusedImports(
    _ / "routes" / "main" / "controllers" / "javascript" / "JavaScriptReverseRoutes.scala",
    Set(
      "import play.core.routing.{ HandlerDef, ReverseRouteContext, queryString, dynamicString }",
      "import ReverseRouteContext.empty"
    ),
    Map(
      "import play.api.mvc.{ QueryStringBindable, PathBindable, Call, JavascriptLiteral }" ->
        "import play.api.mvc.{ QueryStringBindable, PathBindable }"
    )
  )

  removeUnusedImports(
    _ / "routes" / "main" / "router" / "Routes.scala",
    Set("import play.core.j._"),
    Map())
}
然后,您将需要对任务依赖项进行排序:

// Our optimize routes imports task depends on the routes task.
optimizeRoutesImports := (optimizeRoutesImports dependsOn (play.sbt.routes.RoutesKeys.routes in Compile)).value

// And compilation depends on the unused routes having been removed.
compile := ((compile in Compile) dependsOn optimizeRoutesImports).value
在启用
-Ywarn unused import
之前,您可能还需要将
旋转键.templateImports
设置为保守列表。类似于此,具体取决于视图中使用的类型:

TwirlKeys.templateImports := Seq("play.api.mvc._", "play.api.i18n.Messages", "controllers.routes")
我还不得不将未使用的
TemplateMagic
导入从Twirl模板(YMMV)中删除:

如果这样做,请确保任务依赖关系设置正确


这对我有用。Scala 2.11.8,播放2.5.10,启用了
-Xfatal警告
-Ywarn未使用导入
。这很可怕,但它可以工作。

我刚刚在Scala 2.12和Play 2.6(您可能正在使用)中遇到了同样的问题

一个名为消音器的Scala编译器插件对其进行了分类:

将以下依赖项添加到build.sbt中:

val silencerVersion = "1.2.1"

libraryDependencies ++= Seq(
    compilerPlugin("com.github.ghik" %% "silencer-plugin" % silencerVersion),
    "com.github.ghik" %% "silencer-lib" % silencerVersion % Provided
)
scalacOptions += "-P:silencer:globalFilters=Unused import"
然后添加(也在build.sbt中):

globalFilters=
后面的文本是编译器警告静音的正则表达式匹配列表,可以用逗号分隔。您可能需要根据自己的情况调整正则表达式,但我发现上面的示例运行良好

这确实意味着它会消除任何“未使用的导入”警告,但是如果您习惯于自动格式化代码(Intellij中的
ctrl+alt+L
),包括整理未使用的导入,那么它应该不会导致问题。

这里有另一个选项(可能不如danielnixon的选项)

我将以下内容添加到
build.sbt

import CustomGenerator._

import play.sbt.routes.RoutesKeys
RoutesKeys.routesImport := Seq.empty
routesGenerator := ModifiedInjectedRoutesGenerator
然后将其添加到
project/CustomGenerator.scala
(始终为顶级
project/
):


Play sbt插件生成路由代码(可在
target/scala-2.11/routes
下查看)。此代码段删除或内联所有未使用的导入。您可能需要根据自己的路线进行定制。

什么是scala版本?不久前,我对隐式搜索相关的警告进行了修复。我会尝试一个样本项目,如果你能做到这一点。没有忽略文件的机制,只不过在2.11中,您可以提供一个执行任何操作的报告程序。@som snytt I“我正在使用scala
2.11.8
,只需要编译就可以了。有关于这个主题的更新吗?模板确实会在路由编译时插入额外的导入。可以从scalac任务中排除路由类,然后使用自定义任务进行编译;或者运行一个格式化程序来清理导入;或者让路由编译器更智能。我还没有试过。@som snytt我实际上看到在
Routes.scala
文件中生成了一些导入的文件,但是编译器直接指向
Routes
文件,我不知道为什么或者它是否重要。关于格式化程序,scalariform不支持导入优化,可能可以在Intellij中设置。关于scalac,我甚至不知道该如何开始,谷歌也没有帮助。这看起来很有希望,我会在接下来的几天里尝试!这只会抑制对的所有警告me@Pavel感谢您的反馈,我已经更新了答案以更正正则表达式。@MattStephens现在不会抑制所有“未使用的导入”警告吗?无论如何,对于我来说,带有任何globalFilters键的消音器插件只会抑制所有警告(我尝试了不同的regexp、不同的键、不同的代码),以确保消音器中存在错误。@Pavel yep这是正确的,正如我在上一段中指出的。问题的严重程度可能取决于个人偏好&您的团队是否习惯于自动整理导入内容。不能对消音器发表评论,因为它在这里工作得很好;改变正则表达式,我可以将它从工作状态切换到不工作状态,然后再切换回来。可能有不同的Scala/JVM/sbt版本?
import CustomGenerator._

import play.sbt.routes.RoutesKeys
RoutesKeys.routesImport := Seq.empty
routesGenerator := ModifiedInjectedRoutesGenerator
object CustomGenerator {
  object ModifiedInjectedRoutesGenerator extends play.routes.compiler.RoutesGenerator {
    import play.routes.compiler._
    import play.routes.compiler.RoutesCompiler.RoutesCompilerTask

    def generate(task: RoutesCompilerTask, namespace: Option[String], rules: List[Rule]): Seq[(String, String)] = {
      play.routes.compiler.InjectedRoutesGenerator.generate(task, namespace, rules) map { case(key, value) =>
        var v = value
        if(key.endsWith("/ReverseRoutes.scala")) {
          v = v.replace("import ReverseRouteContext.empty", "implicit val empty = ReverseRouteContext(Map())")
          v = v.replace("import play.core.routing.{ HandlerDef, ReverseRouteContext, queryString, dynamicString }", "import play.core.routing.{ ReverseRouteContext, queryString }")
          v = v.replace("import play.api.mvc.{ QueryStringBindable, PathBindable, Call, JavascriptLiteral }", "import play.api.mvc.{ QueryStringBindable, Call }")
        }
        if(key.endsWith("migrations/ReverseRoutes.scala")) {
          v = v.replace("import play.api.mvc.{ QueryStringBindable, Call }", "import play.api.mvc.{ Call }")
          v = v.replace("import play.core.routing.{ ReverseRouteContext, queryString }", "import play.core.routing.{ ReverseRouteContext }")
        }
        if(key.endsWith("/JavaScriptReverseRoutes.scala")) {
          v = v.replace("import ReverseRouteContext.empty", "")
          v = v.replace("import play.api.mvc.{ QueryStringBindable, PathBindable, Call, JavascriptLiteral }", "import play.api.mvc.{ QueryStringBindable, JavascriptLiteral }")
          v = v.replace("import play.core.routing.{ HandlerDef, ReverseRouteContext, queryString, dynamicString }", "")
        }
        if(key.endsWith("migrations/javascript/JavaScriptReverseRoutes.scala")) {
          v = v.replace("import play.api.mvc.{ QueryStringBindable, JavascriptLiteral }", "")
        }
        if(key.endsWith("/Routes.scala")) {
          v = v.replace("import play.core.routing.HandlerInvokerFactory._", "")
          v = v.replace("import play.core.j._", "")
          v = v.replace("import ReverseRouteContext.empty", "implicit val empty = ReverseRouteContext(Map())")
        }
        (key, v)
      }
    }

    def id: String = "injected+"
  }
}