Checker BasicNullness   as of Julia version 2.5.0 (built on 4 Jul 2018)

belongs to group Basic

Identify simple nullness bugs


If the null value gets dereferenced, Java runs into a NullPointerException. For this reason, programmers must ensure that the content of expressions dereferenced in their programs is never null. Solving this problem is in general hard. This checker provides a coverage of the most frequent scenarios when null might end up being dereferenced. For a sound alternative to this checker, that covers all possible situations, see the Nullness checker. However, BasicNullness is much faster than Nullness and issues a more restricted set of false alarms, hence it is often the best solution for a rapid identification of the most frequent null-pointer errors in a program.

Action: Check if the warning corresponds to a situation where null might actually be dereferenced at runtime. If that is the case, add a nullness check for the value being dereferenced, or change the logic of the code. Sometimes, a warning of this checker corresponds to a spurious nullness check, that can be removed.

Examples


Consider the following program:

public class Test {
  String f = null;

  public void test1(String s) {
    if ((System.nanoTime() % 2) == 0 || f != null)
      System.out.println(f.length());

    if (s != null)
      System.out.println("s01 is null");

    System.out.println(s.length());
  }

  public void test2(String par) {
    String s = System.currentTimeMillis() % 2 == 0 ? null : "ciao";
    TestSupport ts = new TestSupport();
    int n01 = ts.m01(s);
    int n02 = ts.m02(s);
		
    if (s != null)
      ts.m01(s);

    ts.m01(par);

    System.out.println(n01 + n02);
  }

  public void test3(String par) {
    if (par != null)
      System.out.println("is non-null");

    new TestSupport().m01(par);
  }
}

where

public class TestSupport {

  public int m01(String s) {
    return s.length();
  }

  public int m02(String s) {
    if (s != null)
      return s.length();

    return -1;
  }
}

This checker issues the following warnings:

Test.java:6: [BasicNullness: CallOnNullWarning] the receiver of "length" seems always null here
Test.java:11: [BasicNullness: CallOnNullWarning] is the receiver of "length" non-null? It seems to be compared against null at line 8
Test.java:17: [BasicNullness: ActualNullWarning] is parameter "s" of "m01" non-null? An explicit null constant seems to be passed for it
Test.java:17: [BasicNullness: ActualNullWarning] is parameter "s" of "m01" non-null? It seems to be compared against null at line 20
Test.java:32: [BasicNullness: ActualNullWarning] is parameter "s" of "m01" non-null? It seems to be compared against null at line 29

At line 6 of Test.java, field f holds always null when it is dereferenced, since it is never initialized otherwise in the program. At line 11, the expression s.length() will dereference the value of s, that is checked against null at line 8. This checker finds this suspicious, since either the dereference will run into a NullPointerException or s will never hold null at runtime, but then the check at line 8 is useless and could be removed. At line 17, this checker finds two reasons why there might be a NullPointerException there. The first is that there is an explicit null literal stored into s at line 15; the second is that s is later compared against null at line 20. Note that null cannot be passed as parameter to the method m01(), since that method dereferences its parameter, at line 4 of TestSupport.java. The situation is different for method m2(), that explicitly checks for nullness of its parameter, and this is why no warning is issued at line 18 of Test.java. At line 32, par is passed to m1() and consequently cannot be null. However, it was previously checked against null at line 29, which is suspicious.

Checks on Methods that Return java.lang.Boolean

Methods with return type java.lang.Boolean should never return null, since they are often used in Boolean conditions, through the automatic unboxing mechanism into the primitive type boolean. Unboxing corresponds to a call to method booleanValue() of java.lang.Boolean, that will consequently run into a NullPointerException. Consider for instance the following code:

public class TestReturnNullForBoolean {

  public Boolean returnNullForBoolean1() {
    if (System.currentTimeMillis() % 2 == 0)
      return null;
    else
      return Boolean.TRUE;
  }

  public Boolean returnNullForBoolean2() {
    if (System.currentTimeMillis() % 2 == 0)
      return null;
    else
      return Boolean.FALSE;
  }

  public Boolean returnNullForBoolean3() {
    if (System.currentTimeMillis() % 2 == 0)
      return null;
    else
      return Boolean.TRUE;
  }

  public void test1() {
    boolean b = test3();
    System.out.println(b);
  }

  public void test2() {
    Boolean b = returnNullForBoolean3();
    if (b != null) {
      boolean bb = b;
      System.out.println(bb);
    }
  }

  public Boolean test3() {
    return returnNullForBoolean2();
  }
}

This checker will issue the following warnings:

TestReturnNullForBoolean.java:5: [BasicNullness: ReturningNullForBooleanWarning] Returning null for java.lang.Boolean: automatic unboxing will throw an exception
TestReturnNullForBoolean.java:12: [BasicNullness: ReturningNullForBooleanWarning] Returning null for java.lang.Boolean, which is later dereferenced at line 25
TestReturnNullForBoolean.java:19: [BasicNullness: ReturningNullForBooleanWarning] Returning null for java.lang.Boolean: automatic unboxing will throw an exception

Note, in the previous example, that this checker can identify where the null value returned at line 12 might be unboxed in the program: namely, this might happen at line 25. Moreover, note that the explicit nullness check at line 31 avoids a NullPointerException at line 32, which is why the warning at line 19 does not refer to a possible dereference of null at line 32.