Comment couvrir le contrôle nul inutile généré par Kotlin?

9

Prenons l'exemple minimal de Kotlin suivant:

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

J'ai des règles de couverture de succursales à Jacoco, qui échouent pour le code ci-dessus, disant que 1 des 2 succursales n'est pas couverte sur la ligne de l' someWrapperappel. Malheureusement, ce n'est pas une option pour moi d'exclure toutes les classes à partir desquelles someWrapperest appelé.

En regardant le code Java décompilé:

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

Je pense que le problème est la if (var10000 != null)branche, qui est même marquée par l'IDE comme inutile (toujours vrai).

Est-il possible de régler le code de manière à pouvoir couvrir toutes les branches, par exemple. en vous assurant que le compilateur ne génère pas cette vérification null supplémentaire? Je peux changer le code des deux foo(..)et someWrapper(..)tant que je suis en mesure de fournir un lambda décoré.

J'utilise Kotlin 1.3.50 et Jacoco 0.8.4.

ÉDITER.

Une solution de contournement évidente consiste à extraire supplyAsync(someWrapper { ... })vers une classe utils et à exclure cette classe uniquement, c'est-à-dire:

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

Ce serait assez bon pour moi, même si je suis toujours curieux de savoir pourquoi la branche est ajoutée par Kotlin, où aucune branche n'a besoin d'être.

BKE
la source
J'obtiens Type inference faileden essayant de faire compiler votre exemple de code. Ce serait génial si vous pouviez fournir un exemple de code qui fonctionne hors de la boîte! Par exemple, taskExecutoret controllersont inconnus.
Enselic
@Enselic a ajouté une petite modification pour supprimer les erreurs gênantes. Je ne vais pas l'étendre davantage au code à part entière car cela devrait être suffisant pour faire passer l'idée.
BKE
1
En regardant comment JaCoCo s'adapte progressivement pour prendre en charge Kotlin (voir github.com/jacoco/jacoco/releases et rechercher "ajouté par le compilateur Kotlin"), je pense que c'est juste un autre écart qui va être corrigé tôt ou tard. Si vous vous sentez sérieux au sujet de dépasser votre couverture, je vous suggère de signaler un problème.
PiotrK

Réponses:

1

Si la valeur de retour de someWrapperest uniquement destinée à être utilisée comme une instance de Supplier, vous pouvez supprimer la vérification null inutile en utilisant explicitement Suppliercomme type de retour.

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