December 6, 2023

Error Prone

I discovered error-prone some years ago, but didn’t take a chance to use it, but this week I was reminded about it by someone on Twitter and decided now it was time.

What is error prone?

Error Prone is a static analysis tool for Java, developed by Google, designed to identify and correct bugs in Java code at compile-time.

Key features include:

  • Bug Pattern Detection: Scans for common mistakes based on experienced developers’ insights.
  • Compile-Time Integration: Operates during compilation, catching errors earlier than later.
  • Customizable: Comes with default checks but can be extended for project-specific needs, for example with the picnic support library.
  • Automated Fixes: Can apply fixes, called refaster templates.

Use it with Maven

At work, we use Maven and Java 17, so I will explain here how to use Error Prone with these two. We also use Lombok and MapStruct that have their own annotation processors.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>17</source>
                <target>17</target>
                <encoding>UTF-8</encoding>
                <!-- Display warnings in the logs, otherwise you won't know about some 
                     things discovered by Error Prone -->
                <showWarnings>true</showWarnings>
                <!-- Whether you want to fail the build if any warning is found,
                     I don't, because I prefer to lift a rule to level ERROR for specific rules -->
                <failOnWarning>false</failOnWarning>
                <compilerArgs>
                    <!-- this is for JDK 16+ -->
                    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
                    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
                    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
                    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
                    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
                    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
                    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
                    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
                    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
                    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
                    <!-- this is for Error Prone -->
                    <arg>-XDcompilePolicy=simple</arg>
                    <arg>-Xplugin:ErrorProne -XepExcludedPaths:.*/target/.* -Xep:BigDecimalEquals:ERROR -Xep:BigDecimalLiteralDouble:ERROR</arg>
                    <!-- here comes your own javac parameters -->
                    <arg>-parameters</arg>
                    <arg>-Amapstruct.defaultComponentModel=spring</arg>
                </compilerArgs>
                <annotationProcessorPaths>
                    <path>
                        <groupId>com.google.errorprone</groupId>
                        <artifactId>error_prone_core</artifactId>
                        <version>2.23.0</version>
                    </path>
                    <path>
                        <groupId>tech.picninc.error-prone-support</groupId>
                        <artifactId>error-prone-contrib</artifactId>
                        <version>0.14.0</version>
                    </path>
                    <!-- .. your other processors: mapstruct, lombok, jpamodelgen, ... -->
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

Some difficulties that I encountered while trying to install it based on the installation guide, are:

  • The .mvn/.jvmconfig file works well with Maven, and for IntelliJ only if your project is a simple maven module, if you have a multi-module then it is apparently not supported by IntelliJ yet, this is why I decided to pass the parameters to javac as <arg> in the maven config
  • You enable Error Prone using -Xplugin:ErrorProne as an <arg> value
  • You can (and should) discard some directories like target/ because it may contain generated code that you don’t have control on (for example OpenAPI generated source code).
  • You can lift the level of the bug patterns, for example above I decided that we should not use equals to compare BigDecimal, this is checked by the bug pattern named BigDecimalEquals, but this pattern has a default level of WARNING, so you can lift it to ERROR using -Xep:BigDecimalEquals:ERROR.

What is more important to know is that if you want to change the level of multiple patterns, they all have to be put in the same <arg> XML tag.

Using the panic contrib

You can see above that I included Error Prone Support from Picnic which contains a set of additional bug patterns and refaster rules.

Build time

Just know that it adds a significant amount of time to the build, at work I am working on multiple projects, the main one is taking 35 seconds to compile, but when Error Prone is enabled it will take almost 1min30, this is why I moved this behind a maven profile named error-prone so that we can use it or not depending on the case.

Until next time!

Alexandre Grison - //grison.me - @algrison