-Ywarn-unused-import triggering on play routes file
I want to be able to use -Xfatal-warnings
and -Ywarn-unused-import
, the problem is that the compiler is triggering an error on the file which contains the play routes for my application:
[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)
same goes for other routes.
Is it possible maybe to tell scalac to ignore a file?
Scala version is 2.11.8
.
I just encountered the same problem with Scala 2.12 and Play 2.6 (which you're probably now using).
A Scala compiler plugin called Silencer sorts it out: https://github.com/ghik/silencer
Add the following dependency into build.sbt:
val silencerVersion = "1.2.1"
libraryDependencies ++= Seq(
compilerPlugin("com.github.ghik" %% "silencer-plugin" % silencerVersion),
"com.github.ghik" %% "silencer-lib" % silencerVersion % Provided
)
Then add (also in build.sbt):
scalacOptions += "-P:silencer:globalFilters=Unused import"
The text after the globalFilters=
is a list of regex matches for compiler warnings to silence, can be comma-separated. You might need to tweak the regex for your own case, but I found the above example worked fine.
It does mean that it silences any "Unused import" warnings, but if you're in the habit of auto-formatting your code (ctrl+alt+L
in Intellij), including tidying up unused imports, then it shouldn't cause a problem.
A horrible "solution" could be to remove those unused imports after the routes are generated but before the compile task runs. Here's a sketch:
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())
}
You'll then want to sort out the task dependencies:
// 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
You'll also likely need to set TwirlKeys.templateImports
to a conservative list before enabling -Ywarn-unused-import
. Something like this, depending on what types are used in your views:
TwirlKeys.templateImports := Seq("play.api.mvc._", "play.api.i18n.Messages", "controllers.routes")
I also had to knock unused TemplateMagic
imports out of Twirl templates (YMMV):
removeUnusedImports(
_ / "twirl" ** "*.template.scala",
Set("import play.twirl.api.TemplateMagic._"),
Map())
If you do that, make sure the task dependencies are set up appropriately.
This works for me. Scala 2.11.8, Play 2.5.10, both -Xfatal-warnings
and -Ywarn-unused-import
enabled. It's hideous, but it works.
I've come up with a working solution for Scala 2.13.7 (no need for any plugin) for Play 2.8.11. Take a look on those examples and adjust to your needs:
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
If you want to understand more, take a look on this documentation and add verbose information on the compiler messages output
scalacOptions += "-Wconf:any:wv",
Pro tip: fail compilation of unused only within 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")
},