【问题标题】:-Ywarn-unused-import triggering on play routes file-Ywarn-unused-import 触发播放路径文件
【发布时间】:2016-09-21 15:14:31
【问题描述】:

我希望能够使用-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

【问题讨论】:

  • 什么scala版本?前段时间,我为与隐式搜索相关的警告提供了修复。如果你能做到,我会尝试一个示例项目。没有忽略文件的机制,除了在 2.11 中您可以提供一个报告器,它可以做任何事情。
  • @som-snytt 我正在使用 scala 2.11.8, here you can find an example project,只需要编译。
  • 关于该主题的任何更新?
  • 模板确实在路由编译时注入了额外的导入。可以从 scalac 任务中排除路由类,然后使用自定义任务进行编译;或运行清理导入的格式化程序;或使路由编译器更智能。我还没试过。
  • @som-snytt 我实际上看到在Routes.scala 文件中生成了一些导入,但编译器直接指向routes 文件,我不确定为什么或是否重要。关于格式化程序,scalariform 不支持导入优化,也许可以在 Intellij 中设置。关于我什至不知道要开始的 scalac,谷歌搜索也没有帮助。

标签: scala playframework sbt scalac


【解决方案1】:

我刚刚在 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),包括整理未使用的导入,那么它不应该导致一个问题。

【讨论】:

  • 这看起来很有希望,我会在接下来的几天里尝试一下!
  • 这只是为我禁止了所有警告
  • @Pavel 感谢您的反馈,我已更新答案以更正正则表达式。
  • @MattStephens 现在不会抑制所有“未使用的导入”警告吗?无论如何,对我来说,带有任何 globalFilters 键的消音器插件只会抑制所有警告(我尝试了不同的正则表达式、不同的键、不同的代码),确保消音器中存在错误。
  • @Pavel 是的,这是正确的,正如我在上一段中指出的那样。有多少问题可能取决于个人喜好以及您的团队是否习惯性地自动整理他们的导入。不能真正评论消音器,因为它在这里工作得很好;更改正则表达式我可以将其从工作切换到不工作,然后返回。也许不同的 Scala/JVM/sbt 版本?
【解决方案2】:

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

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 模板 (YMMV) 中剔除未使用的 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 均已启用。这很可怕,但它有效。

【讨论】:

    【解决方案3】:

    我为 Play 2.8.11 的 Scala 2.13.7(不需要任何插件)提出了一个可行的解决方案。查看这些示例并根据您的需要进行调整:

    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
    

    如果您想了解更多信息,请使用look on this documentation 并在编译器消息输出中添加详细信息

    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")
    },
    

    【讨论】:

      【解决方案4】:

      这是另一种选择(可能不如 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 下看到)。此代码 sn-p 删除或内联所有未使用的导入。您可能需要为您的路线定制它。

      【讨论】:

        【解决方案5】:

        也有这个问题。基本上 Scala 2.12.13 或 2.13.x 将 Silencer 代码合并到它们的 -Wconf 编译器标志 see here

        例子:

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

        可以将此规则限制为某些源文件,例如路由 (see doc),但我尝试时没有运气。

        对于以前版本的 Scala,您应该使用 Silencer 插件:

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

        【讨论】:

          猜你喜欢
          • 2017-08-31
          • 2016-05-11
          • 1970-01-01
          • 2013-05-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多