-Ywarn-unused-import触发play路由文件的问题

53

我希望能够使用-Xfatal-warnings-Ywarn-unused-import,但问题是编译器正在文件中触发我的应用程序的Play路由的错误:

[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


什么Scala版本?我之前为与隐式搜索相关的警告做出了修复贡献。如果您可以尝试一个示例项目,我会尝试一下。没有忽略文件的机制,除非在2.11中您可以提供自己想要的报告器。 - som-snytt
@som-snytt 我正在使用 Scala 2.11.8在这里你可以找到一个示例项目,只需要编译即可。 - Ende Neu
有关这个问题有任何更新吗? - Louis F.
模板确实在路由编译过程中注入了额外的导入。可能有办法将路由类从scalac任务中排除,然后使用自定义任务进行编译;或者运行一个清理导入的格式化程序;或者让路由编译器更智能。我还没有尝试过。 - som-snytt
我有完全相同的问题,使用Scala 2.11.8和Play 2.5.10。最终我注释掉了warn-unused-import。你找到解决方法了吗? - Abhijit Sarkar
显示剩余2条评论
5个回答

7

我已经想出了Scala 2.13.7的工作解决方案(无需任何插件)适用于Play 2.8.11。查看这些示例并根据您的需求进行调整:

scalacOptions ++= Seq(
    "-Wconf:cat=unused-imports&site=.*views.html.*:s", // Silence import warnings in Play html files
    "-Wconf:cat=unused-imports&site=<empty>:s", // Silence import warnings on Play `routes` files
    "-Wconf:cat=unused-imports&site=router:s", // Silence import warnings on Play `routes` files
    "-Wconf:cat=unused-imports&site=v1:s", // Silence import warnings on Play `v1.routes` files
    "-Wconf:cat=unused-imports&site=v2:s", // Silence import warnings on Play `v2.routes` files
    "-Wconf:cat=unused-imports&site=views.v1:s", // Silence import warnings on Play `views.v1.routes` files
    "-Wconf:cat=deprecation&site=controllers\\.v1.*&origin=scala.util.Either.right:s", // Silence deprecations in generated Controller classes
    "-Wconf:cat=deprecation&site=.*v1.Routes.*&origin=scala.util.Either.right:s"
  ) // Silence deprecations in generated Controller classes

如果你想了解更多,可以查看这份文档,并在编译器输出的消息中添加详细信息。文档链接:https://www.scala-lang.org/2021/01/12/configuring-and-suppressing-warnings.html
scalacOptions += "-Wconf:any:wv",

提示:仅在CI状态下运行未使用的失败编译。
scalacOptions ++= {
  // Mark unused errors as info for local development (due to -Werror usage)
  if (insideCI.value) Seq.empty else Seq("-Wconf:cat=unused:i")
},

在我的情况下,我使用以下内容来消除与html和路由相关的警告:"-Wconf:cat=unused-imports&src=.*routes.*:s""-Wconf:cat=unused-imports&src=.*html.*:s" - Alperen

5

我刚遇到了 Scala 2.12 和 Play 2.6 的相同问题(你现在可能正在使用它们)。

一个名为 Silencer 的 Scala 编译器插件可以解决这个问题:https://github.com/ghik/silencer

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

val silencerVersion = "1.2.1"

libraryDependencies ++= Seq(
    compilerPlugin("com.github.ghik" %% "silencer-plugin" % silencerVersion),
    "com.github.ghik" %% "silencer-lib" % silencerVersion % Provided
)

然后在build.sbt文件中添加以下内容:
scalacOptions += "-P:silencer:globalFilters=Unused import"
globalFilters=后面的文本是一组用于禁止编译器警告的正则表达式匹配项,可以使用逗号分隔。您可能需要根据自己的情况调整正则表达式,但我发现上述示例效果很好。这意味着它会停止任何"未使用的导入"警告,但如果您有习惯自动格式化代码(在Intellij中是ctrl+alt+L),包括清理未使用的导入,则不应该会造成问题。

这看起来很有前途,我会在接下来的几天里试一试! - Ende Neu
这只是为我压制所有警告。 - Pavel
@Pavel 谢谢你的反馈,我已经更新了答案来修正正则表达式。 - Matt Stephens
@MattStephens 现在它不会抑制所有“未使用的导入”警告吗?无论如何,对我来说,带有任何globalFilters键的silencer-plugin都会抑制所有警告(我尝试了不同的正则表达式、不同的键、不同的代码),肯定是silencer中存在一个错误。 - Pavel
@Pavel 是的,就像我在最后一段中提到的那样。这可能会成为多大的问题,取决于个人喜好和您的团队是否习惯自动整理导入。对于silencer,我无法发表评论,因为它在这里运行良好;更改正则表达式后,我可以将其从工作状态切换到非工作状态,然后再切换回来。也许是由于不同的Scala/JVM/sbt版本? - Matt Stephens

4
一个可怕的“解决方案”是在路由生成之后但编译任务运行之前删除那些未使用的导入项。以下是一个简要概述:
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前,您还可能需要将TwirlKeys.templateImports设置为一个保守的列表。 根据您的视图中使用的类型,可以这样做:

TwirlKeys.templateImports := Seq("play.api.mvc._", "play.api.i18n.Messages", "controllers.routes")

我还需要从Twirl模板中删除未使用的TemplateMagic导入(可能因人而异):

  removeUnusedImports(
    _ / "twirl" ** "*.template.scala",
    Set("import play.twirl.api.TemplateMagic._"),
    Map())

如果您这样做,请确保任务依赖关系设置得当。
对我来说有效。Scala 2.11.8,Play 2.5.10,启用-Xfatal-warnings-Ywarn-unused-import。它很丑陋,但是它有效。

0

我也遇到了这个问题。基本上 Scala 2.12.13 或 2.13.x 将 Silencer 代码合并到他们的 -Wconf 编译器标志中 请参见此处

例如:

scalacOptions += "-Wconf:cat=unused-imports:s"

可以将此规则限制为某些源文件,例如路由(请参阅文档),但我尝试时没有成功。

对于早期版本的Scala,则应使用Silencer插件:

scalacOptions += "-P:silencer:pathFilters=views;routes",

0

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

我在build.sbt中添加了以下内容:

import CustomGenerator._

import play.sbt.routes.RoutesKeys
RoutesKeys.routesImport := Seq.empty
routesGenerator := ModifiedInjectedRoutesGenerator

然后将以下内容添加到project/CustomGenerator.scala文件中(始终是顶级project/):
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+"
  }
}

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接