Checker BasicNullness

belongs to group Basic
Identify simple nullness bugs

Frameworks supported by this checker

  • java up to 11
  • android up to API level 28
  • dotnet

Warnings generated by this checker

  • ActualNullReflectionWarning: an actual parameter passed to a method might be null [ CWE476 ]
  • ActualNullWarning: an actual parameter passed to a method might be null [ CWE476 ]
  • ArrayLengthOfNullWarning: the length of a possibly null array is computed [ CWE476 ]
  • ArrayLoadFromNullWarning: an element of a possibly null array is read [ CWE476 ]
  • ArrayStoreIntoNullWarning: an element of a possibly null array is written [ CWE476 ]
  • CallOnNullWarning: a method call might occur on a null receiver [ CWE476 ]
  • FormalNullWarning: a formal parameter of a method or constructor might hold null [ CWE476 ]
  • GetFieldFromNullWarning: a field is read from a possibly null receiver [ CWE476 ]
  • LambdaParameterMightBeNullWarning: a parameter of a lambda expression might be null [ CWE456 ]
  • MethodShouldNotReturnNullWarning: a method returns null but is normally assumed to return a non-null value [ CWE227 ]
  • MissingNullnessCheckOfReturnedValueWarning: the return value of a method is checked against null, but not here [ CWE252 ]
  • PutFieldIntoNullWarning: a field is written into a possibly null receiver [ CWE476 ]
  • ReturningNullForArrayWarning: a method returns null instead of an empty array [ CWE476 ]
  • ReturningNullForBooleanWarning: a method returns null instead of a java.lang.Boolean [ CWE476 ]
  • ReturningNullForOptionalWarning: a method returns null instead of a java.util.Optional [ CWE476 ]
  • SynchronizationOnNullWarning: a synchronization might occur on null [ CWE476 ]
  • ThrowOfNullWarning: a throw command might throw null [ CWE476 ]
  • VariableCanOnlyBeNullWarning: a variable that can only hold null is dereferenced [ CWE456 ]

Options accepted by this checker

  • warnIfExpected: issues null-pointer warnings also where the exception might be expected
    The programmer sometime expects a NullPointerException, for instance if the code catches that exception explicitly or if a JUnit test is annotated as expecting that exception to be thrown. By disabling this option, a warning is not generated in those cases

Annotations understood by this checker

  • @com.juliasoft.julia.checkers.nullness.NonNull
  • @com.juliasoft.julia.checkers.basicNullness.NullnessShouldBeChecked


Description

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.

If the null value gets dereferenced, .Net runs into a NullReferenceException. 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.

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.

Checks nullness on Method/Field Reflection java.lang.Boolean

In Java, the classes java.lang.reflect.Method and java.lang.reflect.Field have some methods which throw a NullPointerException, if a specified argument is null and the method/field (used through reflection) is an instance method/field. When this instructions are detected, Julia tries to statically resolve the reflection, in order to check if it is a valid reflection or if it leads to a NullPointerException. For performance reasons, the resolver checks only the constants inside the method where the reflection instruction is called.

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class ReflectionExample {

	public static void Method() throws NoSuchFieldException, SecurityException, ClassNotFoundException,
									InstantiationException , IllegalAccessException , IllegalArgumentException, InvocationTargetException {

			
			Class c = Class.forName(ClassUsedByReflection.class.getName());
			
			Field success1 = c.getField("ok");
			success1.get(nil());
			
			Field success2 = c.getField("hi");
			success2.get(new Object()); // arg0 is not null
			
			Field fail1 = c.getField("hi");
			fail1.get(nil());
			
			Field[] fail2 = c.getFields();
			for(Field f : fail2)
				f.get(nil()); // in the ClassUsedByReflection class exists at least an instance field		

	}

	private static class ClassUsedByReflection{
		
		public String hi = "Hello World!"; // it is an instance
		
		public static String ok = "I am ok!"; // it is static then it is not an instance
		
		public ClassUsedByReflection(){	}
		
	}	
	
	public static String nil() {
		 return null;
	}

}

This checker will issue the following warnings:

ReflectionExample.java:19: [BasicNullness: ActualNullReflectionWarning] Is the actual parameter "arg 0" passed to method "get" non-null? An explicit null constant from line 40 seems to be passed for it, and this will lead to an exception when accessing field "com.juliasoft.julia.tests.checks.BasicNullness.Example$ClassUsedByReflection.hi" through reflection.
ReflectionExample.java:23: [BasicNullness: ActualNullReflectionWarning] Is the actual parameter "arg 0" passed to method "get" non-null? An explicit null constant from line 40 seems to be passed for it, and this could lead to an exception if you are trying to access an instance field of class "com.juliasoft.julia.tests.checks.BasicNullness.Example$ClassUsedByReflection" through reflection.

At line 19, the application tries to perform a java.lang.reflect.Field.get on the field hi that is an instance field of ClassUsedByReflection. In this case the java.lang.reflect.Field.get's argument is null (the method nil of ClassUsedByReflection returns always a null value), and this will lead to an NullPointerException by Java API documentation. At line 23 , the application tries to perform a java.lang.reflect.Field.get on all fields of ClassUsedByReflection. The warning at line 23 is implicitly triggered for the same reason of ActualNullReflectionWarning at line 19, because in the ClassUsedByReflection class exists at least an instance field.

Consider the following program:

using System;

namespace DocumentationExamples
{

    public class BasicNullness
    {

        public static void Main(string[] args)
        { }

        string f = null;
        public void Test1(string s)
        {
            if ((DateTime.Now.Millisecond % 2) == 0 || f != null)
                Console.WriteLine(f.Length);
            if (s != null)
                Console.WriteLine("s01 is null");
            Console.WriteLine(s.Length);
        }
        public void Test2(string par)
        {
            string s = DateTime.Now.Millisecond % 2 == 0 ? null : "ciao";
            BasicNullnessSupport ts = new BasicNullnessSupport();
            int n01 = ts.M01(s);
            int n02 = ts.M02(s);
            if (s != null)
                ts.M01(s);
            ts.M01(par);
            Console.WriteLine(n01 + n02);
        }
        public void Test3(string par)
        {
            if (par != null)
                Console.WriteLine("is non-null");
            new BasicNullnessSupport().M01(par);
        }
    }
    
    public class BasicNullnessSupport
    {
        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:

DocumentationExamples.cs:16: [BasicNullness: CallOnNullWarning] The receiver of method "get_Length" seems always null here
DocumentationExamples.cs:19: [BasicNullness: CallOnNullWarning] Is the receiver of method "get_Length" non-null? It seems to be compared against null at line 17
DocumentationExamples.cs:25: [BasicNullness: ActualNullWarning] Is the actual parameter "s" passed to method "M01" non-null? An explicit null constant from line 23 seems to be passed for it
DocumentationExamples.cs:25: [BasicNullness: ActualNullWarning] Is the actual parameter "s" passed to method "M01" non-null? It seems to be compared against null at line 27
DocumentationExamples.cs:36: [BasicNullness: ActualNullWarning] Is the actual parameter "s" passed to method "M01" non-null? It seems to be compared against null at line 34

At line 16 of DocumentationExamples.cs, field f holds always null when it is dereferenced, since it is never initialized otherwise in the program. At line 19, the expression s.Length will dereference the value of s, that is checked against null at line 17. This checker finds this suspicious, since either the dereference will run into a NullReferenceException or s will never hold null at runtime, but then the check at line 17 is useless and could be removed. At line 25, 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 23; the second is that s is later compared against null at line 27. Note that null cannot be passed as parameter to the method M01(), since that method dereferences its parameter, at line 44 in TestSupport. The situation is different for method M02(), that explicitly checks for nullness of its parameter, and this is why no warning is issued at line 18 of Test.java. At line 36, par is passed to M01() and consequently cannot be null. However, it was previously checked against null at line 34, which is suspicious.

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.


The Android frameworks introduce specific null pointer dereference cases, due to its architecture.
Consider the following program:

package example.basicnullness;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Object obj = savedInstanceState.get("key");
        
        foo(obj);
        
    }

	private void foo(Object obj){
		//do something
	}
}

This checker will issue the following warnings:

MainActivity.java:12: [BasicNullness: CallOnNullWarning] Is the receiver of method "get" non-null? An explicit null constant from the same line seems to be used for it

Note, The method onCreate is called when the activity is starting and could receive a null value as parameter value, during the application execution. The idea is that the formal parameter of that method might be null, without an evidence. The CallOnNullWarning at line 12 is triggered to highlight a possible thrown of NullPointerException. The Android frameworks contain many methods with a similar behavior. In this cases, the analysis considers the formal parameter as potential null. If does not exist an explicit nullness check, the analysis triggers a warning in the possible points where a NullPointerException can be throwed.

A fix for the above example could be:

package example.basicnullness;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

		if(savedInstanceState != null){
	        Object obj = savedInstanceState.get("key");
	        
	        foo(obj);
	        
        }else{
        	 Log.e(TAG,"Unable to get the object");
        }
    }

	private void foo(Object obj){
		//do something
	}
}