【问题标题】:How to cover unnecessary null check generated by Kotlin?如何覆盖 Kotlin 生成的不必要的空检查?
【发布时间】:2019-10-09 14:33:09
【问题描述】:

考虑以下最小的 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 中有分支覆盖规则,但上面的代码失败了,说 someWrapper 调用的行中没有覆盖 2 个分支中的 1 个。不幸的是,我不能排除所有调用 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 标记为不必要(始终正确)。

是否可以通过某种方式调整代码以覆盖所有分支,例如。通过确保编译器不会生成额外的空检查?只要我能够提供修饰的 lambda,我就可以更改 foo(..)someWrapper(..) 的代码。

我使用 Kotlin 1.3.50 和 Jacoco 0.8.4。

编辑。

一个明显的解决方法是将supplyAsync(someWrapper { ... }) 提取到某个实用程序类并仅排除该类,即:

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

这对我来说已经足够了,尽管我仍然很好奇为什么分支是由 Kotlin 添加的,而不需要分支。

【问题讨论】:

  • 我在尝试编译您的示例代码时收到Type inference failed。如果您能提供开箱即用的示例代码,那就太好了!例如,taskExecutorcontroller 是未知数。
  • @Enselic 添加了小编辑以消除分散注意力的错误。我不打算将它进一步扩展为完整的代码,因为这应该足以让这个想法得到理解。
  • 看看 JaCoCo 如何逐步适应支持 Kotlin(参见 github.com/jacoco/jacoco/releases 并搜索“由 Kotlin 编译器添加”),我认为这只是另一个迟早会修复的差距.如果您对超出覆盖范围感到认真,我建议您报告问题。

标签: kotlin code-coverage jacoco


【解决方案1】:

如果someWrapper 的返回值仅用作Supplier 的实例,那么您可以通过显式使用Supplier 作为返回类型来删除不必要的空检查。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-24
    • 2021-08-16
    • 1970-01-01
    • 2017-06-13
    • 2015-02-01
    • 2019-05-19
    • 2017-03-12
    • 1970-01-01
    相关资源
    最近更新 更多