CheckReturnValue
Ignored return value of method that is annotated with @CheckReturnValue

Severity
ERROR
Has Fix?
NO_FIX

Alternate names: ResultOfMethodCallIgnored, ReturnValueIgnored

The problem

The JSR 305 @CheckReturnValue annotation marks methods whose return values should be checked. This error is triggered when one of these methods is called but the result is not used.

@CheckReturnValue may be applied to a class or package 1 to indicate that all methods in that class or package must have their return values checked. For convenience, we provide an annotation, @CanIgnoreReturnValue, to exempt specific methods or classes from this behavior. @CanIgnoreReturnValue is available from the Error Prone annotations package, com.google.errorprone.annotations.

If you really want to ignore the return value of a method annotated with @CheckReturnValue, a cleaner alternative to @SuppressWarnings is to assign the result to a variable that starts with unused:

public void setNameFormat(String nameFormat) {
  String unused = format(nameFormat, 0); // fail fast if the format is bad or null
  this.nameFormat = nameFormat;
}

NOTE: @CheckReturnValue is ignored under the following conditions (which saves users from having to use either an unused variable or @SuppressWarnings):

  1. calls from Mockito.verify(); e.g., Mockito.verify(t).foo() (where foo() is annotated with @CheckReturnValue)

  2. calls from Stubber.when(); e.g. doReturn(val).when(t).foo() (where foo() is annotated with @CheckReturnValue)

  3. code that is using the try/execute/fail/catch pattern; e.g.:

try {
  user.setName(null);
  fail("Expected a NullPointerException to be thrown on a null name");
} catch (NullPointerException expected) {
}

This is because such tests meant to check if a method is invoked and/or throws the correct exception type, rather than consuming the return value.

Suppression

Suppress false positives by adding the suppression annotation @SuppressWarnings("CheckReturnValue") to the enclosing element.


Positive examples

CheckReturnValuePositiveCases.java

/*
* Copyright 2012 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package com.google.errorprone.bugpatterns.testdata;

import org.junit.rules.ExpectedException;

/** @author eaftan@google.com (Eddie Aftandilian) */
public class CheckReturnValuePositiveCases {

IntValue intValue = new IntValue(0);

@javax.annotation.CheckReturnValue
private int increment(int bar) {
return bar + 1;
}

public void foo() {
int i = 1;
// BUG: Diagnostic contains: remove this line
increment(i);
System.out.println(i);
}

public void bar() {
// BUG: Diagnostic contains: this.intValue = this.intValue.increment()
this.intValue.increment();
}

public void testIntValue() {
IntValue value = new IntValue(10);
// BUG: Diagnostic contains: value = value.increment()
value.increment();
}

private void callRunnable(Runnable runnable) {
runnable.run();
}

public void testResolvedToVoidLambda() {
// BUG: Diagnostic contains: Ignored return value
callRunnable(() -> this.intValue.increment());
}

public void testResolvedToVoidMethodReference() {
// BUG: Diagnostic contains: Ignored return value
callRunnable(this.intValue::increment);
}

public void testRegularLambda() {
callRunnable(
() -> {
// BUG: Diagnostic contains: Ignored return value
this.intValue.increment();
});
}

public void testBeforeAndAfterRule() {
// BUG: Diagnostic contains: remove this line
new IntValue(1).increment();
ExpectedException.none().expect(IllegalStateException.class);
new IntValue(1).increment(); // No error here, last statement in block
}

public void constructor() {
/*
* We may or may not want to treat this as a bug. On the one hand, the
* subclass might be "using" the superclass, so it might not be being
* "ignored." (Plus, it would be a pain to produce a valid suggested fix
* that incorporates any subclass constructor body, which might even contain
* calls to methods in the class.) On the other hand, the more likely
* scenario may be a class like IteratorTester, which requires (a) that the
* user subclass it to implement a method and (b) that the user call test()
* on the constructed object. There, it would be nice if IteratorTester
* could be annotated with @CheckReturnValue to mean "anyone who creates an
* anonymous subclasses of this should still do something with that
* subclass." But perhaps that's an abuse of @CheckForNull.
*
* Anyway, these tests are here to ensure that subclasses don't don't crash
* the compiler.
*/

new MyObject() {};

class MySubObject1 extends MyObject {}

class MySubObject2 extends MyObject {
MySubObject2() {}
}

class MySubObject3 extends MyObject {
MySubObject3() {
super();
}
}

// TODO(cpovirk): This one probably ought to be treated as a bug:
new MyObject();
}

private class IntValue {
final int i;

public IntValue(int i) {
this.i = i;
}

@javax.annotation.CheckReturnValue
public IntValue increment() {
return new IntValue(i + 1);
}

public void increment2() {
// BUG: Diagnostic contains: remove this line
this.increment();
}

public void increment3() {
// BUG: Diagnostic contains: remove this line
increment();
}
}

private static class MyObject {
@javax.annotation.CheckReturnValue
MyObject() {}
}

private abstract static class LB1<A> {}

private static class LB2<A> extends LB1<A> {

@javax.annotation.CheckReturnValue
public static <T> LB2<T> lb1() {
return new LB2<T>();
}

public static <T> LB2<T> lb2() {
// BUG: Diagnostic contains: remove this line
lb1();
return lb1();
}
}

private static class ErrorProneAnnotation {
@com.google.errorprone.annotations.CheckReturnValue
public static int check() {
return 1;
}

public static void ignoresCheck() {
// BUG: Diagnostic contains: remove this line
check();
}
}
}

Negative examples

CheckReturnValueNegativeCases.java

/*
* Copyright 2012 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package com.google.errorprone.bugpatterns.testdata;

import java.util.function.Supplier;
import javax.annotation.CheckReturnValue;

/** @author eaftan@google.com (Eddie Aftandilian) */
public class CheckReturnValueNegativeCases {

public void test1() {
test2();
Object obj = new String();
obj.toString();
}

@SuppressWarnings("foo") // wrong annotation
public void test2() {}

@CheckReturnValue
private int mustCheck() {
return 5;
}

private void callRunnable(Runnable runnable) {
runnable.run();
}

private void testNonCheckedCallsWithMethodReferences() {
Object obj = new String();
callRunnable(String::new);
callRunnable(this::test2);
callRunnable(obj::toString);
}

private void callSupplier(Supplier<Integer> supplier) {
supplier.get();
}

public void testResolvedToVoidLambda() {
callSupplier(() -> mustCheck());
}

public void testMethodReference() {
callSupplier(this::mustCheck);
}
}
  1. To annotate a package, create a package-info.java file in the package directory, add a package statement, and annotate the package statement.