Treat Warnings as Errors in Java
Compiler warnings identify real problems, indicate likely programming mistakes or code smells. Warnings also indicate other unusual conditions in your code that might indicate a problem, although compilation can proceed successfully. Since these compiler warnings are unlikely to be fatal they are likely to be ignored. Once they are ignored they build up and become a Frankenstein Monster who is difficult to treat and un-reasonable to plan and eliminate.
One of the famous examples of the build-up of errors in an open-source world is Mozilla Firefox builds which are existing for 19 years and getting resolved even today, do check this bug created in the Mozilla bug tracker
Eliminating these warnings was such a huge task, and a big transformation challenge for firefox. A computer scientist from Mozilla Mr Nicholas Nethercote who was working for Firefox has shared the story in a nice beautiful blog post on how Mozilla started treating compiler warnings fatal.
Some ages back Mozilla took a herculean transformation step, which is to treat all compiler warnings as errors.
So how do we Java developers treat warnings as errors in the applications that we build?
JAVAC compiler which comes as part of JDK provides an option to lint your code for compiler warnings related to various scenarios. Below is the list of compiler warning scenarios detected by JAVAC. Below command lists man pages of JAVAC.
javac -help -X
Below are the Xlint options for linting your code for compiler warnings.
-Xlint Enable recommended warnings
-Xlint:<key>(,<key>)*
Warnings to enable or disable, separated by comma.
Precede a key by - to disable the specified warning.
Supported keys are:
all Enable all warnings
auxiliaryclass Warn about an auxiliary class that is hidden in a source file, and is used from other files.
cast Warn about use of unnecessary casts.
classfile Warn about issues related to classfile contents.
deprecation Warn about use of deprecated items.
dep-ann Warn about items marked as deprecated in JavaDoc but not using the @Deprecated annotation.
divzero Warn about division by constant integer 0.
empty Warn about empty statement after if.
exports Warn about issues regarding module exports.
fallthrough Warn about falling through from one case of a switch statement to the next.
finally Warn about finally clauses that do not terminate normally.
module Warn about module system related issues.
opens Warn about issues regarding module opens.
options Warn about issues relating to use of command line options.
overloads Warn about issues regarding method overloads.
overrides Warn about issues regarding method overrides.
path Warn about invalid path elements on the command line.
processing Warn about issues regarding annotation processing.
rawtypes Warn about use of raw types.
removal Warn about use of API that has been marked for removal.
requires-automatic Warn about use of automatic modules in the requires clauses.
requires-transitive-automatic Warn about automatic modules in requires transitive.
serial Warn about Serializable classes that do not provide a serial version ID.
Also warn about access to non-public members from a serializable element.
static Warn about accessing a static member using an instance.
try Warn about issues relating to use of try blocks (i.e. try-with-resources).
unchecked Warn about unchecked operations.
varargs Warn about potentially unsafe vararg methods
preview Warn about use of preview language features
none Disable all warnings
To run JAVAC using Xlint option use the below pattern
javac -Xlint:option_name -classpath if-exists path-to-java-file.java
eg.. command
javac -Xlint:finally Example.java
Just to play around with compiler warnings here is one of my favorite examples which Warn about finally
clauses that do not terminate normally.
public static int horribleMethod(int a, int b) {
try {
return b / a;
} catch (AClassException e) {
System.err.println("Caught AClassException.");
return 1;
} finally {
return 0;
}
}
The above code throws a warning if you build with Xlint:finally
. Since
A finally
block always executes when the try block exits. In this example,
if control is transferred to the catch
block, then the method exits.
However, the finally
block must be executed,
so it is executed, even though control has already been transferred outside the method.
If you are building your application using some build automation tool like Maven or Gradle, you have plugins and options to treat warnings as errors
In case of Gradle, to treat warnings as errors we get Compile options using which you can configure compiler options to javac task just like below.
tasks.withType(JavaCompile) {
configure(options) {
options.compilerArgs << '-Xlint:all' << -Werror
}
}
In case of Maven, to treat compiler warnings as errors we can use Maven compiler plugin which could be configured in your POM like below
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<fork>true</fork>
<compilerArgs>
<arg>-verbose</arg>
<arg>-Werror</arg>
<arg>-Xlint:all</arg>
</compilerArgs>
</configuration>
</plugin>
If you are building A spring Boot application, I have seen some difficulties in treating warnings as errors. Please do visit my another post on treating warnings as errors in Spring Boot where I explain it in detail.