google / auto

A collection of source code generators for Java.
Apache License 2.0
10.44k stars 1.2k forks source link

AutoBuilder will not build Kotlin data class with Duration #1579

Open wesalvaro opened 1 year ago

wesalvaro commented 1 year ago

I have a Kotlin data class with a nested @AutoBuilder.
If I change one of my Kotlin data class constructor arguments from a Long to a Kotlin Duration, I get the follow error:

[AutoBuilderNoVisible] No visible constructor for REDACTED
    public static abstract interface Builder {
                           ^

    at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
    at com.google.devtools.kotlin.compiler.plugin.codegen.concurrent.KtCodegenExecutor.afterExecute(KtCodegenExecutor.kt:39)
    at com.google.devtools.kotlin.compiler.plugin.codegen.syncer.KtCodegenFloorExecutor.afterExecute(KtCodegenFloorExecutor.kt:27)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1623)
Caused by: error: [AutoBuilderNoVisible] No visible constructor for REDACTED
    public static abstract interface Builder {
                           ^

    at com.google.devtools.kotlin.compiler.plugin.codegen.compilation.KtCodegenPluginCompilerApp.compilePlugin(KtCodegenPluginCompilerApp.kt:151)
    at com.google.devtools.kotlin.compiler.plugin.codegen.KtCodegenCompiler.processKtCodegenFloor$lambda$6(KtCodegenCompiler.kt:294)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:577)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    ... 2 more

Perhaps because Duration is an inline class?

public inline class Duration internal constructor(private val rawValue: Long) : Comparable<Duration> {
eamonnmcmanus commented 1 year ago

We've encountered this internally. Googlers can see it at b/276527697.

Here's what I wrote there. In the discussion, we have data class Options which has a Duration parameter.


This is a tricky one. kotlin.time.Duration is an inline class. That means it isn't ordinarily possible to construct it from Java code at all: new Options(myDuration) won't work. In fact Options doesn't have a Duration field, it has a long, though the Kotlin compiler does its best to hide that from Kotlin users.

Presumably the main purpose of the builder is for Java clients, since Kotlin clients can just do Options(...) using keyword arguments and defaulting as appropriate. I think the best we can do is something like this:

data class Options(val duration: Duration) {
  @AutoBuilder(callMethod = "make")
  interface Builder {
    fun setDuration(duration: java.time.Duration): Builder
    fun build(): Options
  }

  companion object {
    @JvmStatic fun make(duration: java.time.Duration) = Options(duration.toKotlinDuration())
  }
}

I also agree that we could have a better error message here. The message is technically true: there really is no visible constructor. But the underlying problem is with the inline parameter, and perhaps we could detect that and mention it in the error message.


wesalvaro commented 1 year ago

I like that workaround! It's an extra method, but mostly copy+paste.

As for the error message: Yeah, that would have saved me some time... 😅

Rasel688 commented 1 year ago

[AutoBuilderNoVisible] No visible constructor for REDACTED public static abstract interface Builder { ^

at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
at com.google.devtools.kotlin.compiler.plugin.codegen.concurrent.KtCodegenExecutor.afterExecute(KtCodegenExecutor.kt:39)
at com.google.devtools.kotlin.compiler.plugin.codegen.syncer.KtCodegenFloorExecutor.afterExecute(KtCodegenFloorExecutor.kt:27)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1623)

Caused by: error: [AutoBuilderNoVisible] No visible constructor for REDACTED public static abstract interface Builder { ^

at com.google.devtools.kotlin.compiler.plugin.codegen.compilation.KtCodegenPluginCompilerApp.compilePlugin(KtCodegenPluginCompilerApp.kt:151)
at com.google.devtools.kotlin.compiler.plugin.codegen.KtCodegenCompiler.processKtCodegenFloor$lambda$6(KtCodegenCompiler.kt:294)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:577)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
... 2 more