# Life with fatal warnings
Scala programmers are well known for their desire to pass as much work to the compiler as possible. The compiler, in turn, gladly takes care of programs' correctness thanks to many built-in linting rules. It can emit warnings whenever some code construct is suspicious and is likely to be a mistake, and it is a commonly accepted good practice to treat those warnings as errors. This post discusses how you can achieve this without going crazy because of constant compilation errors.
# Making warnings fatal
The easiest way to make every warning seen is to make them fail the build. If you are lucky enough to be able to use
Scala 2.13.2 or later, add -Wconf:any:error
to your scalac
options. For older Scala versions -Xfatal-warnings
works. The list of the checks that the Scala compiler can perform is well documented on the
Scala Compiler Options (opens new window) page, and
if you use sbt as a build tool there are plugins that configure scalac
for you, such as
sbt-tpolecat (opens new window).
The stricter your compiler settings are, however, the higher the chances that they will get in the way of the normal development process. For example, even though you don't want to have unused variables, you very often have some just because the code that uses them is yet to be written. Or you comment out a line of code to see if a test covers some corner case, but instead, you learn that now there is an unused import that fails the compilation.
# Making warnings non-fatal
Tired of fighting with the compiler, you may decide that you want different compiler options for the CI and the local
development. The former should be strict to ensure that no bad code gets into production, while the latter can be more
relaxed. With sbt you can achieve this by checking the insideCI
value:
scalacOptions ++= {
if (insideCI.value) Seq("-Wconf:any:error")
else Seq("-Wconf:any:warning")
}
Now the compiler will keep telling you about potential problems in the code, but it will not fail the build, so you will still be able to run the tests or the application itself.
# Making warnings shown
The solution of keeping warnings as warnings has one shortcoming: you will only see them for the files that have been re-compiled in the current incremental compilation run. If missed, you will not know about a problem until it fails the CI build.
Moreover, sbt 1.4.0-M1 adds experimental cached compilation support, where the compiler can reuse compilation results from another machine. Regarding scalac warnings, it means that you may miss some of them because someone else has already compiled this code and there was no need to re-run the compilation on your machine.
There is a printWarnings
sbt task that can show all compilation warnings, but you need to remember to run it, and
since it calls compile
first you may see some warnings twice. It would be nice if you just always saw the warnings
regardless of the incremental compilation details, and here we come to
sbt-rewarn (opens new window).
Add the following line to your plugins.sbt
(either of a particular project, or the global one at
~/.sbt/1.0/plugins/plugins.sbt
)
addSbtPlugin("com.timushev.sbt" % "sbt-rewarn" % "0.1.0")
and sbt will start showing scalac
warnings whenever you run tasks like compile
or test
, even if there are no
changes in sources:
sbt:demo> compile
[info] Compiling 1 Scala source to ...
// ...
// here a warning has been printed but you did not pay attention
// ...
// without sbt-rewarn
sbt:demo> test
[info] + DemoSpec
[info] + an important test
[info] Ran 1 test in 500 ms: 1 succeeded, 0 ignored, 0 failed
[success] Total time: 1 s, completed Jul 25, 2020 5:00:00 PM
// with sbt-rewarn
sbt:demo> test
[warn] /demo/A.scala:2:15: private val a in object A is never used
[warn] private val a: Int = 1
[warn] ^
[warn] one warning found
[info] + DemoSpec
[info] + an important test
[info] Ran 1 test in 500 ms: 1 succeeded, 0 ignored, 0 failed
[success] Total time: 1 s, completed Jul 25, 2020 5:00:00 PM
# Summary
To get the most out of the built-int Scala compiler linting options:
- Enable compiler warnings either manually (opens new window) or with sbt-tpolecat (opens new window).
- Make warnings fatal on CI and non-fatal locally.
- Add sbt-rewarn (opens new window) to your build.