-
Notifications
You must be signed in to change notification settings - Fork 295
Error Messages
Here we explain the different warning messages that NullAway produces and how to address them. For an overview of NullAway see the main README.
- dereferenced expression is @Nullable
- returning @Nullable expression from method with @NonNull return type
- passing @Nullable parameter where @NonNull is required
- assigning @Nullable expression to @NonNull field
- method returns @Nullable, but superclass method returns @NonNull
- referenced method returns @Nullable, but functional interface method returns @NonNull
- parameter is @NonNull, but parameter in superclass method is @Nullable
- parameter is @NonNull, but parameter in functional interface method is @Nullable
- unbound instance method reference cannot be used, as first parameter of functional interface method is @Nullable
- initializer method does not guarantee @NonNull field is initialized / @NonNull field not initialized
- read of @NonNull field before initialization
- unboxing of a @Nullable value
- Method is annotated with @EnsuresNonNull but fails to ensure the following fields are non-null at exit
- Expected field to be non-null at call site due to @RequiresNonNull annotation on invoked method
- empty @RequiresNonNull/@EnsuresNonNull is the default precondition for every method, please remove it
- currently @RequiresNonNull/@EnsuresNonNull supports only class fields of the method receiver: fieldName is not supported
- for @RequiresNonNull/@EnsuresNonNull annotation, cannot find instance field in class
- postcondition inheritance is violated, this method must guarantee that all fields written in the @EnsuresNonNull annotation of overridden method are @NonNull at exit point as well. Fields must explicitly appear as parameters at this method @EnsuresNonNull annotation
- precondition inheritance is violated, method in child class cannot have a stricter precondition than its closest overridden method, adding @RequiresNonNull for fields makes this method precondition stricter
- Method is annotated with @EnsuresNonNullIf but does not ensure fields: fieldNames
- Method is annotated with @EnsuresNonNullIf but does not return boolean
This error occurs when code reads a field, writes a field, or invokes a method on some expression, and that expression might be null. Example:
Object x = null;
x.toString(); // dereferencing x, which is null
To fix this error, you can move the dereference under a check that the expression is not null. E.g.:
Object x = null;
if (x != null) {
x.toString(); // this is safe now
}
Be sure to add appropriate logic to handle the case where the expression is null! Or, even better, try to find a way to rewrite the code so the expression can't be null in the first place.
This error occurs when code is returning a @Nullable
expression from a method whose return type is not annotated as @Nullable
. Example:
Object m(@Nullable Object x) {
return x; // oops! return type is unannotated, hence @NonNull
}
To fix this error, you can add a @Nullable
annotation to the return type, e.g.:
@Nullable
Object m(@Nullable Object x) {
return x;
}
Or, you can place the return
statement under an appropriate null check:
Object m(@Nullable Object x) {
if (x != null) {
return x;
} else {
return new Object();
}
// this also works: return (x == null) ? new Object() : x;
}
This can happen in cases like the following:
void nonNullParam(Object x) {}
void caller(@Nullable Object y) {
nonNullParam(y); // bad!
}
To fix, either:
- Make sure
y
is non-null at the pointnonNullParam(y)
is called (e.g. by making the parameter tocaller
itself@NonNull
, or adding an appropriate null check beforenonNullParam(y)
is called), or - Make the parameter
@Nullable
innonNullParam
's definition.
Here's an example of how this can happen:
class Foo {
Object myField; // @NonNull since it's not annotated
void writeToField(@Nullable Object z) {
this.myField = z; // bad!
}
}
To fix, either make the right-hand side of the assignment @NonNull
, place the assignment under an appropriate null check for the right-hand side, or make the field @Nullable
.
This error means you have overridden a superclass method in an invalid way. Here's an example of why it's bad.
class Super {
Object getObj() { return new Object(); }
}
class Sub extends Super {
@Override
@Nullable
getObj() { return null; }
}
class Main {
void caller() {
Super x = new Sub();
x.getObj().toString(); // NullPointerException!
}
}
The key idea is that when code gets an object of type Super
, it needs to be able to rely on the fact that Super.getObj()
returns a @NonNull
value. If subclassing breaks this guarantee, it can lead to NullPointerException
s.
This is a version of the previous error specific to method references. Example:
interface NoArgFunc {
Object apply();
}
class Test {
static Object doApply(NoArgFunc f) {
return f.apply();
}
@Nullable
static Object returnNull() { return null; }
static void test() {
doApply(Test::returnNull).toString(); // NullPointerException!
}
}
The key idea is that when code invokes NoArgFunc.apply()
, it needs to be able to rely on the fact that the return value will be @NonNull
. If a method reference breaks this guarantee, it can lead to NullPointerException
s.
This error is similar to the previous one regarding bad overriding and return types. Here's an example of why it's needed:
class Super {
void handleObj(@Nullable Object obj) { }
}
class Sub extends Super {
@Override
void handleObj(@NonNull Object obj) {
obj.toString();
}
}
class Main {
void caller() {
Super x = new Sub();
x.handleObj(null); // this will cause a NullPointerException
}
}
Subclasses cannot safely override a method and annotate a parameter as @NonNull
if the overridden method has a @Nullable
parameter.
This is just like the previous error, but specific to lambdas and method references. Example:
interface NullableArgFunc {
void apply(@Nullable Object o);
}
class Test {
static void doApply(NullableArgFunc f, @Nullable Object p) {
f.apply(p);
}
static void derefArg(Object q) {
q.toString();
}
static void test() {
doApply((Object x) -> { print(x.hashCode()); }, null); // NullPointerException
doApply(Test::derefArg, null); // also NullPointerException
}
}
Lambdas / method references cannot assume a parameter is @NonNull
if the functional interface declares it as @Nullable
.
Note that if the first doApply
call were written as doApply(x -> { print(x.hashCode()); }
, without the explicit type for the parameter, NullAway would treat the parameter as @Nullable
(inherited from the functional interface method), and hence would report an error at the dereference x.hashCode()
.
unbound instance method reference cannot be used, as first parameter of functional interface method is @Nullable
This is the same as the previous error, specialized to the case of the first parameter of an unbound instance method reference (or a "Reference to an instance method of a arbitrary object supplied later"; see here). Example:
interface NullableArgFunc<T> {
void apply(@Nullable T o);
}
class Test {
static <T> void doApply(NullableArgFunc<T> f, @Nullable T p) {
f.apply(p);
}
void instMethod() {}
static void test() {
doApply(Test::instMethod, null); // NullPointerException
}
}
Here, writing Test::instMethod
is equivalent to writing x -> { x.instMethod(); }
. Hence, the doApply
call will cause an NPE.
initializer method does not guarantee @NonNull field is initialized / @NonNull field not initialized
This error indicates that a @NonNull
field may not be properly initialized in some case. There are two ways to initialize a @NonNull
field: (1) in the constructors of a class, or (2) in a designated initializer method. For the case of constructors, each @NonNull
field must be initialized in every constructor (a constructor that invokes another constructor via this(...)
is excluded). A field can also be initialized in a method called by the constructor, provided that the method is either private
or final
(to prevent overriding) and the constructor invokes the method at the top-level, not under an if
condition (to guarantee the call always executes). NullAway only detects cases where a field is initialized via a direct call to another method from a constructor, not via a chain of calls.
An example:
class C1 {
// non-null fields
Object f1, f2;
// good constructor
C1(Object f1) {
this.f1 = f1;
// initializing f2 in doInit() is ok, since it's a
// private method and called at the top-level
doInit();
}
private void doInit() {
this.f2 = new Object();
}
// bad constructor
C1(boolean flag) {
if (flag) {
// bad: method called under a condition
doInit();
}
// missing initialization of f1
}
}
Initialization can also occur in special initializer method to handle alternate initialization protocols, like Android lifecycles. The checker has built-in knowledge of the following initialization methods:
'android.view.View.onFinishInflate',
'android.app.Service.onCreate',
'android.app.Activity.onCreate',
'android.app.Fragment.onCreate',
'android.app.Application.onCreate',
'javax.annotation.processing.Processor.init'
Overrides of these methods will be treated as initializers. (If you think any third-party library methods should be added to the list, please file an issue.) You can specify a method as an initializer with any @Initializer
annotation, but this should be used very sparingly; it is best not to introduce one-off complex initialization patterns. As with constructors, initializers can invoke private or final methods at the top level to perform initialization.
Note that for a field to be considered initializer by an initializer method, a non-null value must be assigned to that field over all possible paths through that method. The following code would not be considered to have initialized foo
, as there is at least one case (due to the early return) where foo
was not initialized.
class C2 {
// non-null fields
Object foo;
C2() { }
@Initializer
public int initMe() {
if (!sanityCheck()) {
return -1; // Error code.
}
this.foo = new Object();
return 0
}
}
If you get a field initialization error, you can fix it by ensuring the field is initialized in all cases, or by making the field @Nullable
.
In addition to any annotation with the @Initializer
simple name, Null Away recognizes as equivalent "initializer annotations" the following:
'org.junit.Before'
'org.junit.BeforeClass'
As well as any fully-qualified annotation name passed using the -XepOpt:NullAway:CustomInitializerAnnotations=
configuration option.
This error is reported when a @NonNull
field is used before it is
initialized, e.g.:
class C {
Object foo;
C() {
this.foo.toString(); // foo not initialized yet!
this.foo = new Object();
}
}
To fix this error, perform the initialization before reading the field.
This error is reported when some @Nullable
expression gets implicitly unboxed by some operation. For example:
Integer i1 = null;
int i2 = i1 + 3; // NullPointerException
These errors can be fixed in the same manner as the dereferenced expression is @Nullable error.
Method is annotated with @EnsuresNonNull but fails to ensure the following fields are non-null at exit
This error is reported when a method is annotated with @EnsuresNonNull
and a field mentioned in the annotation's parameters is not guaranteed to be @NonNull
at all exit points.
Please see the example below where this error will be reported.
class C {
Object foo;
@EnsuresNonNull("foo")
C(boolean b) {
if(b){
foo = new Object();
}
// Here foo may still be null
}
}
To fix this error, either make sure foo
is initialized along all paths of the constructor at the exit point or remove the @EnsuresNonNull
annotation.
This error indicates that the called method is annotated with @RequiresNonNull
and expects a certain class fields given in the @RequiresNonNull
parameter to be @NonNull
before the call.
Please see the example below where this error will be reported.
class C {
@Nullable Object foo;
//this methods ensures class field "foo" to be non-null at exit point.
@EnsuresNonNull("foo")
void ensuresNonNullFoo() {
foo = new Object();
}
//this methods requires class field "foo" to be non-null at call site.
@RequiresNonNull("foo")
String requiresFoo(){
return foo.toString();
}
//class field "foo" is non-null at call site of "requiresFoo", hence no error will be reported.
void test1(){
foo = new Object();
requiresFoo()
}
//class field "foo" is non-null via call to "ensuresNonNullFoo" at call site of "requiresFoo", hence no error will be reported.
void test2(){
ensuresNonNullFoo()
requiresFoo()
}
//class field "foo" is potentially null at call site of "requiresFoo", hence the above error will be reported.
void test3(){
requiresFoo()
}
}
To fix this error, make sure the reported field is guaranteed to be non-null before the call.
empty @RequiresNonNull/@EnsuresNonNull is the default precondition for every method, please remove it
Empty @RequiresNonNull
and @EnsuresNonNull
is the default type for all methods. Therefore we report an error on such cases to keep the code simple.
Please see the example below where this error will be reported.
class C {
Object foo;
@EnsuresNonNull() //Here the following error will be reported since no class field is given in parameters
void ensures(){
}
@RequiresNonNull() //Here the following error will be reported since no class field is given in parameters
void requires(){
}
}
To fix this error, smiply remove the empty @RequiresNonNull
or @EnsuresNonNull
annotation.
currently @RequiresNonNull/@EnsuresNonNull supports only class fields of the method receiver: fieldName is not supported
This error indicates that the annotation parameter is violating the following syntax rule:
The annotation may only refer to fields of the receiver object.
Please see the example below where this error will be reported.
class C {
Object foo;
C other;
@EnsuresNonNull("other.foo") // error: can only be either "foo" or "this.foo"
void ensures(){
}
@RequiresNonNull("other.foo") // error: can only be either "foo" or "this.foo"
void requires(){
}
}
While we are considering supporting such syntax in the future, right now NullAway isn't able to gain any information from such annotation. To reduce developer confusion we choose to report an error.
This error indicates that the annotation parameter is violating the following syntax rule:
All parameters given in the annotation must be one of the fields of the containing class or its super classes.
Please see the example below where this error will be reported.
class Super {
Object bar;
}
class C extends Super{
Object foo;
@EnsuresNonNull("foo") // no error: field is present in "C"
void ensures(){
foo = new Object();
}
@RequiresNonNull("bar") // no error: field is present in super class "Super"
void requiresBar(){
//not important
}
@RequiresNonNull("tmp") // error: field is not present in this class or any of it's super classes.
void requiresTmp(){
//not important
}
}
postcondition inheritance is violated, this method must guarantee that all fields written in the @EnsuresNonNull annotation of overridden method are @NonNull at exit point as well. Fields must explicitly appear as parameters at this method @EnsuresNonNull annotation
This error indicates that the annotation violates the following inheritance rule:
Every method must satisfy all postcondition of its super method.
Without this rule, a postcondition of @EnsuresNonNull("field") void foo(...)
on type Supertype
might be violated for o.foo(...)
on Supertype o = new Subtype()
,
depending on whether and how Subtype overrides the method foo
of Supertype
.
Please see the example below that demonstrates the necessity of this rule.
class SuperType{
Object foo;
@EnsuresNonNull("foo")
void ensures(){
foo = new Object();
}
@RequiresNonNull("foo")
String requires(){
return o.toString();
}
}
class Subtype extends Super{
void ensures(){ }
void error(){
SuperType o = new Subtype();
o.ensures();
o.requires(); // call to requires potentialy leads to null pointer exception.
}
}
Please see link for more information regarding inheritance rules.
All overriding methods can only add new field names to the set of fields of its super method
mentioned in EnsuresNonNull
and all should be mentioned explicitly in the annotation parameters.
Please see the example below where this error will be reported.
class Super {
Object bar;
@EnsuresNonNull("bar")
void ensures(){
foo = new Object();
}
}
class Child extends Super{
Object foo;
@EnsuresNonNull("foo") // error: method does not satisfy all its super methods post condition. "bar" must be also mentioned explicitly in the annotation parameter.
void ensures(){
foo = new Object();
}
}
To fix this error, simply add bar
as a parameter to @EnsuresNonNull
annotation on Child.ensures
method.
precondition inheritance is violated, method in child class cannot have a stricter precondition than its closest overridden method, adding @requiresNonNull for fields makes this method precondition stricter
This error indicates that the annotation violating the following inheritance rule:
Every method cannot have a stricter precondition than its super method
Without this rule, a precondition of @RequiresNonNull("field") void foo(...)
on type Supertype
might be violated for o.foo(...)
on Supertype o = new Subtype()
,
depending on whether and how Subtype overrides the method foo
of Supertype
.
Please see the example below that demonstrates the necessity of this rule.
class SuperType{
@Nullable Object nullableFoo;
@Nullable Object nullableBar;
@RequiresNonNull("foo")
String requires(){
nullableBar = new Object();
return nullableBar.toString() + nullableFoo.toString();
}
}
class Subtype extends Super{
@RequiresNonNull("foo", "bar")
String requires(){
return nullableBar.toString() + nullableFoo.toString();
}
void error(){
SuperType o = new Subtype();
o.nullableFoo = new Object();
o.requires(); // call to requires potentialy leads to null pointer exception.
}
}
Please see link for more information regarding inheritance rules.
No overriding methods can add new field names to the set of fields of its super method mentioned in RequiresNonNull
annotation.
Please see the example below where this error will be reported.
class Super {
Object bar;
@RequiresNonNull("bar")
String Requires(){
return bar.toString();
}
}
class C extends Super{
Object foo;
@RequiresNonNull({"foo", "bar"}) // error: Having the preconidton of "foo" in addition to "bar" to be non-null at call site makes this method precondition stricter than it's super method.
String Requires(){
return bar.toString() + foo.toString();
}
// no-error: this method does not have a stricter precondition than its super method. It still requires "bar" to be non-null at call site.
void Requires(){
}
}
To fix this error, remove foo
as a parameter to @RequiresNonNull
annotation on the C.Requires
method.
This error happens when the method annotated with @EnsuresNonNullIf
doesn't ensure the non-nullability of the passed fields. For example:
class Foo {
@Nullable private Object item;
@EnsuresNonNullIf("item")
public boolean hasItem() {
// Error here
// This is wrongly implemented, as the method returns true but item is nullable
return item == null;
}
public void operation() {
if(!hasItem()) {
return;
}
// from here on, item is assumed to be non-null
}
}
This error happens when the method annotated with @EnsuresNonNullIf
doesn't return boolean. For example:
class Foo {
@Nullable private Object item;
@EnsuresNonNullIf("item")
public void hasItem() {
// ERROR: this method should return boolean
}
}