Como cobrir a verificação nula desnecessária gerada pelo Kotlin?

9

Considere o seguinte exemplo mínimo de Kotlin:

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));
}

Tenho regras de cobertura de filial em Jacoco, que falham no código acima, dizendo que 1 de 2 agências não é coberta na linha da someWrapperchamada. Infelizmente, não é uma opção para mim excluir todas as classes das quais someWrapperé chamada.

Observando o código Java descompilado:

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();
}

Penso que o problema é o if (var10000 != null)ramo, que é até marcado pelo IDE como desnecessário (sempre verdadeiro).

É de alguma forma possível ajustar o código de forma que seja possível cobrir todos os ramos, por exemplo. certificando-se de que o compilador não gere essa verificação nula extra? Posso alterar o código de ambos foo(..)e someWrapper(..)desde que eu seja capaz de fornecer um lambda decorado.

Eu uso o Kotlin 1.3.50 e o Jacoco 0.8.4.

EDITAR.

Uma solução óbvia é extrair supplyAsync(someWrapper { ... })para algumas classes de utilitários e excluir apenas essa classe, ou seja:

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

Isso seria bom o suficiente para mim, embora ainda esteja curioso para saber por que o ramo é adicionado por Kotlin, onde nenhum ramo precisa estar.

BKE
fonte
Recebo Type inference failedao tentar fazer seu código de amostra compilar. Seria ótimo se você pudesse fornecer um código de exemplo que funcione imediatamente! Por exemplo, taskExecutore controllersão desconhecidos.
Enselic 9/10/19
A @Enselic adicionou pequenas edições para remover erros de distração. Não vou expandi-lo ainda mais para o código completo, pois isso deve ser suficiente para transmitir a ideia.
BKE
11
Observando como a JaCoCo se adapta progressivamente para dar suporte ao Kotlin (consulte github.com/jacoco/jacoco/releases e procure "adicionado pelo compilador Kotlin"), acho que essa é apenas outra lacuna que será corrigida mais cedo ou mais tarde. Se você acha que está no topo de sua cobertura, sugiro relatar um problema.
PiotrK

Respostas:

1

Se o valor de retorno de someWrapperfor usado apenas como uma instância de Supplier, você poderá remover a verificação nula desnecessária usando explicitamente Suppliercomo o tipo de retorno.

fun <U> someWrapper(supplier: () -> U): Supplier<U> {
    return Supplier { supplier() }
}
Leo Aso
fonte