Java Null Pointer Static Analysis in Eclipse – JDT vs CheckerFramework – False-Positive comparison

We’re talking about this and this. For those of us who program in Eclipse using Java, what is the best tool(s) for checking null pointer exceptions.

There have been several attempts before, but nothing has made its way into the official language. Eclipse does provide good null pointer checking out of the box, but there is only so much it can do. To improve the power of the checkers, we need to add annotations.

Note: In the following, I am using JDK 1.8.

1) We have Eclipse itself, using the JDT annotations Eclipse help.
2) We have the standard checker framework.
3) We have FindBugs, which I believe is used by sonarcube.

There is great power to have something built into the environment, so the standard eclipse system has a strong advantage. Unless it has serious problems, it should be part of the standard development environment.

Unfortunately, it does have serious problems. Take a look at the following program:

public class TestNullCode {

    @org.eclipse.jdt.annotation.Nullable
    private String testJdt;

    public void testJdt() {
        if (testJdt == null) {
            testJdt = "not null";
        }
        // this line causes a false-positive warning
        // Potential null pointer access: The field testJdt is specified as @Nullable
        if (testJdt.equals("not null")) {
            System.out.println("will work because testJdt is not null");
        }
    }
}

This example shows a false-positive that is provably incorrect. The compiler issues a warning (or error, depending on your settings) on line 12, that is untrue.

Frankly, this is pretty sad. The reasons are justifiable, but the result is sad, nonetheless.

For non-final @Nullable fields the simplest strategy for avoiding any nullness related risks is to pessimistically assume potential null at every read. This means for strict checking no flow analysis should be applied to @Nullable fields..

How does the checkerframework do? Answer: No problems at all:

public class TestNullCode {

    @org.checkerframework.checker.nullness.qual.Nullable
    private String testCheckerFramework;

    public void testCheckerFramework() {
        if (testCheckerFramework == null) {
            testCheckerFramework = "not null";
        }
        // this line is ok
        if (testCheckerFramework.equals("not null")) {
            System.out.println("will work because testCheckerFramework is not null");
        }

        testCheckerFramework = null;
        // following line is flagged by checker framework
        // dereference of possibly-null reference testCheckerFramework
        System.out.println("will fail because testCheckerFramework is not null" + testCheckerFramework.toString());
    }
}

How does FindBugs do? Answer: No problems at all. Same as checkerframework , so I won’t post the identical code.

Eclipse has a workaround that costs a line of extra code:

While this appears to be a very drastic restriction, the remedy is quite easy: before dereferencing a @Nullable field it has to be assigned to a local variable. Flow analysis is then safely applied to the local variable with no risk of side effects, aliasing nor concurrency, since local variables are not shared with any code locations that would be outside the scope of the analysis. I.e., the flow analysis can see everything it needs to consider regarding local variables.

public class TestNullCode {

    @org.eclipse.jdt.annotation.Nullable
    private String testJdt;

    public void testJdtCopy() {
    	String copy = testJdt;
        if (copy == null) {
        	copy = "not null";
        }
        //This is fine
        if (copy.equals("not null")) {
            System.out.println("will work because copyis not null");
        }
    }
}

I consider this “solution” ugly. I get it, but I don’t buy it.
At some point, I would expect the Eclipse engine to get better.

So at this point, we still need the other tools.

You can download my example eclipse project here.