Reputation: 15783
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
.
Upvotes: 53
Views: 3925
Reputation: 2355
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")
},
Upvotes: 8
Reputation: 4055
Had this problem too. Basically Scala 2.12.13 or 2.13.x incorporates the Silencer code into their -Wconf compiler flag see here.
Example:
scalacOptions += "-Wconf:cat=unused-imports:s"
It is possible to restrict this rule to certain source file eg routes (see doc) but I had no luck when I tried.
For previous versions of Scala, then you should use the Silencer plugin with:
scalacOptions += "-P:silencer:pathFilters=views;routes",
Upvotes: 0
Reputation: 51
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.
Upvotes: 5
Reputation: 336
Here's another option (possibly inferior to that of danielnixon)
I added the following to build.sbt
:
import CustomGenerator._
import play.sbt.routes.RoutesKeys
RoutesKeys.routesImport := Seq.empty
routesGenerator := ModifiedInjectedRoutesGenerator
Then added this to project/CustomGenerator.scala
(always top level 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 plugin generates routes code (can be seen under target/scala-2.11/routes
). This code snippet removes or inlines all unused imports. You might need to tailor it for your routes.
Upvotes: 0
Reputation: 4268
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.
Upvotes: 4