Как покрыть ненужную нулевую проверку, сгенерированную Kotlin?

9

Рассмотрим следующий минимальный пример 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));
}

У меня есть правила покрытия ветвей в Jacoco, которые не выполняются для приведенного выше кода, говоря, что 1 из 2 ветвей не покрывается в строке someWrapperвызова. К сожалению, я не могу исключить все классы, из которых someWrapperвызывается.

Глядя на декомпилированный код Java:

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

Я думаю, что проблема в if (var10000 != null)ветке, которая даже помечена IDE как ненужная (всегда верно).

Можно ли как-то настроить код так, чтобы можно было охватить все ветви, например. убедившись, что компилятор не генерирует эту дополнительную нулевую проверку? Я могу изменить код обоих foo(..)и someWrapper(..)до тех пор, пока я могу поставить украшенную лямбду.

Я использую Kotlin 1.3.50 и Jacoco 0.8.4.

РЕДАКТИРОВАТЬ.

Одним из очевидных обходных путей является извлечение supplyAsync(someWrapper { ... })в некоторый класс utils и исключение только этого класса, т.е.

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

Это было бы достаточно для меня, хотя мне все еще любопытно, почему ветвь добавлена ​​Kotlin, где нет необходимости в ветке.

БКА
источник
Я получаю, Type inference failedкогда пытаюсь скомпилировать ваш пример кода. Было бы здорово, если бы вы могли предоставить пример кода, который работает из коробки! Например taskExecutorи controllerнеизвестны.
Enselic
@Enselic добавил небольшое редактирование, чтобы убрать отвлекающие ошибки. Я не собираюсь расширять его дальше до полноценного кода, так как этого должно быть достаточно, чтобы донести идею.
БКА
1
Глядя на то, как JaCoCo постепенно адаптируется для поддержки Kotlin (см. Github.com/jacoco/jacoco/releases и поиск «добавлено компилятором Kotlin»), я думаю, что это просто еще один пробел, который рано или поздно будет исправлен. Если вы серьезно относитесь к вопросу о покрытии, я предлагаю сообщить о проблеме.
PiotrK

Ответы:

1

Если возвращаемое значение someWrapperпредназначено только для использования в качестве экземпляра Supplier, то вы можете удалить ненужную нулевую проверку, явно указав Supplierв качестве возвращаемого типа.

fun <U> someWrapper(supplier: () -> U): Supplier<U> {
    return Supplier { supplier() }
}
Лео Асо
источник