Checker UnusedReturnValue   as of Julia version 2.6.0 (built on 6 Sep 2018)

belongs to group Basic

Identify where the return value of a method might be incorrectly discarded


The return value is the result of the computation of a method. In some cases, it is expected that this return value gets used rather than dropped, since it contains important information about the outcome of the method or since otherwise the call to the method or constructor would be useless and hence meaningless. The latter situation occurs for calls to pure methods or constructors, that is, code that does not modify the heap memory of the caller. In many cases, this latter situation is the sign of more serious problems in the algorithmic logic of the code.

Action: Use the return value of the method or remove the method or constructor call completely, since it has no effect on the heap of the caller, or check if the logic of the code is broken because the return value of a call to a pure method or constructor is not used.

Examples


Consider the following program:

import java.io.File;

public class Test {
  private String name;
  private static int counter;

  public Test(String name) {
    this.name = name;
  }

  public Test(String name, int offset) {
    this(name);

    counter += offset;
  }

  public static void main(String[] args) {
    File file = new File("dir");
    file.mkdir();

    Test t = new Test("John");
    t.getName();
    new Test("Joan");
    new Test("Albert", 13);
  }

  public String getName() {
    return name;
  }
}

This checker issues the following warnings:

Test.java:19: [UnusedReturnValue: ReturnValueShouldBeUsedWarning] The returned value of java.io.File.mkdir():boolean is not used nor checked
Test.java:22: [UnusedReturnValue: UselessCallToAPureMethodWarning] Useless call to Test.getName():java.lang.String, that has no side-effects
Test.java:23: [UnusedReturnValue: UselessCallToAPureMethodWarning] Useless call to Test.<init>(java.lang.String):void, that has no side-effects

since the Boolean return value of mkdir() should be checked to see if the directory could be created or not; the call to getName() is useless, since its return value is not used and that method has no effect on the heap of the caller; similarly for the call to the constructor of Test(), at line 23. Note, however, that the call to the other constructor of Test(), at line 24, has effects on the heap (it modifies the static field counter) and it is consequently sensible to call that constructor and drop the created value. For this reason, Julia does not issue any warning at line 24.

In this example, the programmer should for instance modify the program as follows:

import java.io.File;

public class Test {
  private String name;
  private static int counter;

  public Test(String name) {
    this.name = name;
  }

  public Test(String name, int offset) {
    this(name);

    counter += offset;
  }

  public static void main(String[] args) {
    File file = new File("dir");

	if (!file.mkdir()) {
	  System.out.println("directory could not be created");
	  System.exit(0);
	}

    new Test("Albert", 13);
  }

  public String getName() {
    return name;
  }
}

Calls to Pure Methods that Throw Exceptions

Julia does not issue any warning if a call to a pure method, whose return value is not used, is wrapped into a try statement with a non-empty catch clause. In that case, Julia understands that the call was not meant to modify the heap but rather to check for some condition, expressed through an exception. For instance, in the following program:

import java.io.Serializable;

public class InstanceChecks {

  public static void main(String[] args) {
    InstanceChecks ic = new InstanceChecks();
    ic.test1();
    ic.test2();
  }

  private boolean test1() {
    this.getClass().asSubclass(Serializable.class); //useless
    return true;
  }

  private boolean test2() {
    try {
      // useful, since we check if an exception is raised
      this.getClass().asSubclass(Serializable.class);
      return true;
    }
    catch (ClassCastException e) {
      return false;
    }
  }
}

this checker issues only the following warning:

InstanceChecks.java:12: [UnusedReturnValue: UselessCallToAPureMethodWarning] Useless call to java.lang.Class.asSubclass(java.lang.Class):java.lang.Class, that has no side-effects

since the other (identical) call to asSubclass() at line 19 is used to check if an exception is raised, and act accordingly.

Tolerating Calls to Methods without Side-Effects

From time to time, it might be useful to instruct Julia to tolerate calls to specific methods, although they have no side-effects. For instance, this might be the case of calls to methods that check a condition and throw an exception if the condition does not hold. They are often used as assertions and such calls are not normally wrapped inside a try/catch scope. When this is the case, the programmer can annotate the method with the @TolerateUselessCall annotation, as in the subsequent example:

  public static @TolerateUselessCall <T> T notNull(final T object, final String message, final Object... values) {
    if (object == null)
      throw new NullPointerException(String.format(message, values));

    return object;
  }

Without that annotation, a waning would be issued at each call to method notNull().