Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cover unnecessary null check generated by Kotlin?

Consider the following minimal Kotlin example:

fun <U> someWrapper(supplier: () -> U): () -> (U) {
    return { supplier() }
}

fun foo(taskExecutor: TaskExecutor): Int {
    val future = CompletableFuture.supplyAsync(someWrapper {
        42
    }, taskExecutor::execute)
    return future.join()
}

@Test
public void shouldFoo() {
    assertThat(foo(), is(42));
}

I have branch coverage rules in Jacoco, which fail for the code above, saying 1 of 2 branches is not covered on the line of the someWrapper call. Unfortunately, it is not an option for me to exclude all classes from which someWrapper is called.

Looking at the decompiled Java code:

public final int foo(TaskExecutor taskExecutor) {
    Object var10000 = WrappersKt.someWrapper((Function0)null.INSTANCE);
    if (var10000 != null) {
        Object var2 = var10000;
        var10000 = new Foo$sam$java_util_function_Supplier$0((Function0)var2);
    }

    Supplier var3 = (Supplier)var10000;
    Function1 var4 = (Function1)(new Function1(this.taskExecutor) {
        // $FF: synthetic method
        // $FF: bridge method
        public Object invoke(Object var1) {
        this.invoke((Runnable)var1);
        return Unit.INSTANCE;
        }

        public final void invoke(Runnable p1) {
        ((TaskExecutor)this.receiver).execute(p1);
        }

        public final KDeclarationContainer getOwner() {
        return Reflection.getOrCreateKotlinClass(TaskExecutor.class);
        }

        public final String getName() {
        return "execute";
        }

        public final String getSignature() {
        return "execute(Ljava/lang/Runnable;)V";
        }
    });
    CompletableFuture future = CompletableFuture.supplyAsync(var3, (Executor)(new Foo$sam$java_util_concurrent_Executor$0(var4)));
    var10000 = future.join();
    Intrinsics.checkExpressionValueIsNotNull(var10000, "future.join()");
    return ((Number)var10000).intValue();
}

I think, the problem is the if (var10000 != null) branch, which is even marked by the IDE to be unnecessary (always true).

Is it somehow possible to adjust the code such that it is possible to cover all branches, eg. by making sure the compiler does not generate that extra null check? I can change the code of both foo(..) and someWrapper(..) as long as I am able to supply a decorated lambda.

I use Kotlin 1.3.50 and Jacoco 0.8.4.

EDIT.

One obvious workaround is to extract supplyAsync(someWrapper { ... }) to some utils class and exclude that class only, ie.:

fun <U> supplyAsync(supplier: () -> U, executor: TaskExecutor): CompletableFuture<U> {
    return CompletableFuture.supplyAsync(someWrapper { supplier() }, executor::execute)
}

This would be good enough for me, though I am still curious why the branch is added by Kotlin, where no branch need to be.

like image 677
BKE Avatar asked Oct 09 '19 14:10

BKE


People also ask

How do you handle null safety in Kotlin?

Kotlin has a safe call operator (?.) to handle null references. This operator executes any action only when the reference has a non-null value. Otherwise, it returns a null value. The safe call operator combines a null check along with a method call in a single expression.

How does Kotlin check not null?

You can use the "?. let" operator in Kotlin to check if the value of a variable is NULL. It can only be used when we are sure that we are refereeing to a non-NULL able value.

How do you pass null in Kotlin?

Kotlin neither allows null values to be passed as parameter values nor allows null object references unless you specify that a variable can be null . In other words, Kotlin makes you tell the compiler "this (or that) variable can be null ." Such variables are referred to as nullable values.

What is the difference between safe calls (? And null check in Kotlin?

The basic difference while using ?. and !! is if you want to separate a normal flow of var property having a 'non-null' value with 'null' value flow use ?. But if you are sure that the var property value is not null use !!


1 Answers

If the return value of someWrapper is only meant to be used as an instance of Supplier, then you can remove the unnecessary null check by explicitly using Supplier as the return type.

fun <U> someWrapper(supplier: () -> U): Supplier<U> {
    return Supplier { supplier() }
}
like image 52
Leo Aso Avatar answered Oct 08 '22 10:10

Leo Aso