Enhanced Java compilation errors with the static analysis tool ErrorProne

Enhanced Java compilation errors with the static analysis tool ErrorProne

What is ErrorProne?

** ErrorProne ** is a Java static analysis tool made by Google. The big difference from other static analysis tools (such as FindBugs) is that the check NG ** will result in a compilation error **.

Below, quoted from Official Site

It’s common for even the best programmers to make simple mistakes. And sometimes a refactoring which seems safe can leave behind code which will never do what’s intended.

We’re used to getting help from the compiler, but it doesn’t do much beyond static type checking. Using Error Prone to augment the compiler’s type analysis, you can catch more mistakes before they cost you time, or end up as bugs in production. We use Error Prone in Google’s Java build system to eliminate classes of serious bugs from entering our code, and we’ve open-sourced it, so you can too!

It is said that Google also uses this for static analysis. The bug patterns to be checked are described in here.

Environment

This time, we built the following verification environment.

Created IntelliJ Gradle project.

image.png

In addition, looking at the official website, it seems that it corresponds to the following.

--Build tools: Bazel, Maven, Gradle, Ant

Note that ErrorProne requires JDK9 or higher.

Gradle settings

The following build files are prepared to run ErrorProne.

build.gradle


plugins {
    id 'java'
    id "net.ltgt.errorprone" version "0.8.1"
}

group 'errorprone-sample2'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    errorprone("com.google.errorprone:error_prone_core:2.+")
}

def defaultEncoding = 'UTF-8'
tasks.withType(AbstractCompile) each { it.options.encoding = defaultEncoding }

compileTestJava {
    options.encoding = defaultEncoding
}
//tasks.withType(JavaCompile) {
//    options.errorprone {
//        disableAllChecks = true //Disable all checks
//        enable("CollectionIncompatibleType", "ArrayToString") //Enable the check specified earlier
//    }
//}

Verification

We will verify the bug pattern check by ErrorProne. CollectionIncompatibleType Incompatible type as argument to Object-accepting Java collections method

Executable file

public class CollectionIncompatibleTypeSample {
    public static void main(String... args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "One");
        map.put(2, "Two");

        map.get("1"); //Different type from generics
        map.remove("2"); //Different type from generics
    }
}

Error Prone check result

src\main\java\CollectionIncompatibleTypeSample.java:10:error: [CollectionIncompatibleType] Argument '"1"' should not be passed to t
his method; its type String is not compatible with its collection's type argument Integer
        map.get("1"); //Different type from generics
               ^
    (see https://errorprone.info/bugpattern/CollectionIncompatibleType)
src\main\java\CollectionIncompatibleTypeSample.java:11:error: [CollectionIncompatibleType] Argument '"2"' should not be passed to t
his method; its type String is not compatible with its collection's type argument Integer
        map.remove("2"); //Different type from generics
                  ^
    (see https://errorprone.info/bugpattern/CollectionIncompatibleType)


In java.util.Map, the argument type of the above method is ʻObject`, so the above usually does not cause a compile error. However, since this is an implementation error, ErrorProne will result in a compilation error. image.png image.png

ArrayToString Calling toString on an array does not provide useful information

Executable file

public class ArrayToStringSample {
    public static void main(String... args) {
        int[] ary = {1, 2, 3};
        System.out.println(ary); // => [I@3ac3fd8b
        System.out.println(Arrays.toString(ary)); // => [1, 2, 3]
    }
}

Error Prone check result

src\main\java\ArrayToStringSample.java:6:error: [ArrayToString] Calling toString on an array does not provide useful information
        System.out.println(ary); // => [I@3ac3fd8b
                           ^
    (see https://errorprone.info/bugpattern/ArrayToString)
  Did you mean 'System.out.println(Arrays.toString(ary));'?

Using the toString () method of an array is often incorrect, so ErrorProne will result in a compilation error.

FormatString Invalid printf-style format string

Executable file

public class FormatStringSample {
    public static void main(String... args) {
        String text1 = String.format("arg1: %d. arg2: %s", 1, "a");
        System.out.println(text1); // => arg1: 1. arg2: a
        String text2 = String.format("arg1: %d. arg2: %s", "2", "b");
        System.out.println(text2); // => java.util.IllegalFormatConversionException: d != java.lang.String
    }
}

text2 throws ʻIllegalFormatConversionException because the format string of String.format () `and the argument type are different.

Error Prone check result

src\main\java\FormatStringSample.java:5:error: [FormatString] illegal format conversion: 'java.lang.String' cannot be formatted usi
ng '%d'
        String text2 = String.format("arg1: %d. arg2: %s", "2", "b");
                                    ^
    (see https://errorprone.info/bugpattern/FormatString)

If the format string and the argument type are different, an exception is thrown at runtime without a compile error, but ErrorProne causes a compile error.

If you want to enable only some checks

You may want to enable only some checks in the following cases.

--Many check NGs with existing code --Intentionally written code becomes check NG

In that case, only some checks can be enabled by setting as follows.

tasks.withType(JavaCompile) {
    options.errorprone {
        disableAllChecks = true //Disable all checks
        enable("CollectionIncompatibleType", "ArrayToString") //Enable the check specified earlier
    }
}

Impressions

InteliJ will issue a warning for the above part, so I think that there is basically no problem if it corresponds to the IDE warning, but Some people in the world ignore IDE warnings, so if there is a compile error, it will be enforced, so it looks good.

Also, since you can notice the error at the time of compilation, I thought it would be less likely to waste time like sending back a test.

Remarks

-ErrorProne official website

Recommended Posts

Enhanced Java compilation errors with the static analysis tool ErrorProne
Static code analysis with Checkstyle in Java + Gradle
[Java] JavaConfig with Static InnerClass
Static analysis tool that can be used on GitHub [Java version]
Morphological analysis in Java with Kuromoji
Follow the link with Selenium (Java)
Try using the Wii remote with Java
[Java] Get the date with the LocalDateTime class
[Rails] Gem Rubocop Static code analysis tool