ansman/kotshi

Kotshi clashing with Micronaut DI

Closed this issue · 10 comments

Adding kotshi to a micronaut project causes issues. Seems like the micronaut DI/KSP is conflicting with the generated files from Kotshi
Removing the kotshi ksp from gradle fixes the problem (since there is no generated code)

[ksp] org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException: Descriptor wasn't found for declaration OBJECT_DECLARATION
	at org.jetbrains.kotlin.resolve.lazy.BasicAbsentDescriptorHandler.diagnoseDescriptorNotFound(AbsentDescriptorHandler.kt:18)
	at org.jetbrains.kotlin.resolve.lazy.BasicAbsentDescriptorHandler.diagnoseDescriptorNotFound(AbsentDescriptorHandler.kt:17)
	at org.jetbrains.kotlin.resolve.lazy.LazyDeclarationResolver.resolveToDescriptor(LazyDeclarationResolver.kt:91)
	at org.jetbrains.kotlin.resolve.lazy.ResolveSession.resolveToDescriptor(ResolveSession.java:368)
	at com.google.devtools.ksp.processing.impl.ResolverImpl.resolveDeclaration(ResolverImpl.kt:514)
	at com.google.devtools.ksp.processing.impl.ResolverImpl.resolveClassDeclaration(ResolverImpl.kt:565)
	at com.google.devtools.ksp.processing.impl.ResolverImpl.mapToJvmSignatureInternal$compiler_plugin(ResolverImpl.kt:356)
	at com.google.devtools.ksp.processing.impl.ResolverImpl.mapToJvmSignature(ResolverImpl.kt:353)
	at io.micronaut.kotlin.processing.ExtensionsKt.getBinaryName(extensions.kt:47)
	at io.micronaut.kotlin.processing.visitor.KotlinClassElement.<init>(KotlinClassElement.kt:142)
	at io.micronaut.kotlin.processing.visitor.KotlinElementFactory.newClassElement(KotlinElementFactory.kt:53)
	at io.micronaut.kotlin.processing.visitor.TypeElementSymbolProcessor.newClassElement(TypeElementSymbolProcessor.kt:64)
	at io.micronaut.kotlin.processing.visitor.TypeElementSymbolProcessor$ElementVisitor$visitClassDeclaration$classElement$1.invoke(TypeElementSymbolProcessor.kt:244)
	at io.micronaut.kotlin.processing.visitor.TypeElementSymbolProcessor$ElementVisitor$visitClassDeclaration$classElement$1.invoke(TypeElementSymbolProcessor.kt:243)
	at io.micronaut.kotlin.processing.visitor.TypeElementSymbolProcessor$ElementVisitor.visitClassDeclaration$lambda$0(TypeElementSymbolProcessor.kt:243)
	at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1220)

The stack trace you included points to micronaut failing to resolve a symbol. What version of Kotlin, KSP, Kotshi and Micronaut are you using?

Hey @ansman , thanks for the response. I'm like 80% sure this is an issue with Micronaut, but since micronaut worked for everything else and then broke when I added Kotshi I thought I would add it here.

These are my versions:
KSP
id("com.google.devtools.ksp") version "1.9.0-1.0.11"

Kotshi:
implementation("se.ansman.kotshi:api:2.14.0")
ksp("se.ansman.kotshi:compiler:2.14.0")

Micronaut:
ksp("io.micronaut:micronaut-inject-kotlin:4.4.0")
implementation("io.micronaut:micronaut-inject:4.4.0")

I tried extracting JUST the dependency injection stuff from micronaut, and it gave some more detailed errors. This happened for every single class I marked as @JsonSerializable, here are a couple examples. Looks like micronauts 'visitor' is trying to parse Kotshi generated classes and failing for some reason.

[ksp] C:/*/build/generated/ksp/main/kotlin/com/*/*/model/workflow/KotshiCreateCaseResponseJsonAdapter.kt:30: Error processing type visitor [io.micronaut.validation.visitor.async.AsyncTypeElementVisitor@3d392de]: Descriptor wasn't found for declaration CLASS
[ksp] C:/*/build/generated/ksp/main/kotlin/com/*/*/model/workflow/KotshiDeleteResourcesResponseJsonAdapter.kt:36: Error processing type visitor [io.micronaut.inject.beans.visitor.MapperVisitor@5f06cc47]: Descriptor wasn't found for declaration CLASS

Are you depending on Moshi as well? Otherwise you probably have to add an explicit dependency on Moshi too.

I'm using http4k which I thought brought in Moshi for me. I just added an explicit dependency on Moshi 1.15 and the issue still persists.

Ok, thanks for verifying. Usually you get this kind of error when KSP cannot resolve a type which often happens because of some transitive dependency.

Could you post the source code for CreateCaseResponse as well as KotshiCreateCaseResponseJsonAdapter.kt if it's available (you'll find it in the builddirectory: build/generated/ksp/main/kotlin/com/*/*/model/workflow/).

It could also have to do with some annotation or something like that.

@JsonSerializable
data class CreateCaseResponse (
    val userId: String,
    val caseId: String
)
internal class KotshiCreateCaseResponseJsonAdapter() :
    NamedJsonAdapter<CreateCaseResponse>("KotshiJsonAdapter(CreateCaseResponse)") {
  private val options: JsonReader.Options = JsonReader.Options.of(
      "userId",
      "caseId"
  )

  @Throws(IOException::class)
  override fun toJson(writer: JsonWriter, `value`: CreateCaseResponse?) {
    if (`value` == null) {
      writer.nullValue()
      return
    }
    writer
      .beginObject()
      .name("userId").value(`value`.userId)
      .name("caseId").value(`value`.caseId)
      .endObject()
  }

  @Throws(IOException::class)
  override fun fromJson(reader: JsonReader): CreateCaseResponse? {
    if (reader.peek() == JsonReader.Token.NULL) return reader.nextNull()

    var userId: String? = null
    var caseId: String? = null

    reader.beginObject()
    while (reader.hasNext()) {
      when (reader.selectName(options)) {
        0 -> {
          if (reader.peek() == JsonReader.Token.NULL) {
            reader.skipValue()
          } else {
            userId = reader.nextString()
          }
        }
        1 -> {
          if (reader.peek() == JsonReader.Token.NULL) {
            reader.skipValue()
          } else {
            caseId = reader.nextString()
          }
        }
        -1 -> {
          reader.skipName()
          reader.skipValue()
        }
      }
    }
    reader.endObject()

    var errorBuilder: StringBuilder? = null
    if (userId == null) {
      errorBuilder = errorBuilder.appendNullableError("userId")
    }
    if (caseId == null) {
      errorBuilder = errorBuilder.appendNullableError("caseId")
    }
    if (errorBuilder != null) {
      errorBuilder.append(" (at path ").append(reader.path).append(')')
      throw JsonDataException(errorBuilder.toString())
    }

    return CreateCaseResponse(
        userId = userId!!,
        caseId = caseId!!
    )
  }
}

That looks fairly simple. Is this a multi-module project? If so, is the JSON class in the same module as the module that fails to run KSP?

This is a single module project.

Because I was curious, I also tried this with Moshi's standard codegen KSP and got a similar error. More signs pointing to micronaut compile-time DI not liking other compile-time generated files...

implementation("com.squareup.moshi:moshi:1.15.0") ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.0")

Ok, that's a good find. Next step would be to open an issue on Micronauts

Sounds good, thanks for looking through this with me.