kopykat-kt / kopykat

Little utilities for more pleasant immutable data in Kotlin
Other
280 stars 16 forks source link

Bug: Imports are not added to generated code when referencing a class in the parent package #90

Open dronda-t opened 10 months ago

dronda-t commented 10 months ago

Hi! I ran into an issue where if a class references a class that is in the parent package, imports are not added.

For example:

Screenshot 2023-11-11 at 16 13 26
package me.test.data.inner

import at.kopyk.CopyExtensions
import me.test.data.Outer

@CopyExtensions
data class Inner(
    val outer: Outer
)
package me.test.data

import at.kopyk.CopyExtensions

@CopyExtensions
data class Outer(
    val test: String
)

The generated code for inner will look like this:

package me.test.`data`.`inner`

import kotlin.DslMarker
import kotlin.Unit
import me.test.`data`.`Outer$Mutable`
import me.test.`data`.`inner`.Inner
import me.test.`data`.`inner`.`Inner$DslMarker`
import me.test.`data`.`inner`.`Inner$Mutable`

private val generatedByKopyKat: Unit = Unit

@DslMarker
public annotation class `Inner$DslMarker`

@`Inner$DslMarker`
public class `Inner$Mutable`(
  public var outer: `Outer$Mutable`,
  public val old: Inner,
)

public fun `Inner$Mutable`.freeze(): Inner = me.test.`data`.`inner`.Inner(outer = outer.freeze())

public fun Inner.toMutable(): `Inner$Mutable` = me.test.`data`.`inner`.`Inner$Mutable`(old = this,
    outer = outer.toMutable())

public inline fun Inner.copy(block: `Inner$Mutable`.() -> Unit): Inner =
    toMutable().apply(block).freeze()

And fail with the error:

e: file:///kopy-kt-missing-import/build/generated/ksp/main/kotlin/me/test/data/inner/me.test.%60data%60.%60inner%60.Inner$Mutable.kt:21:89 Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: 
public fun `Inner$Mutable`.freeze(): Inner defined in me.test.data.inner in file me.test.`data`.`inner`.Inner$Mutable.kt
e: file:///kopy-kt-missing-import/build/generated/ksp/main/kotlin/me/test/data/inner/me.test.%60data%60.%60inner%60.Inner$Mutable.kt:24:19 Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: 
public fun Inner.toMutable(): `Inner$Mutable` defined in me.test.data.inner in file me.test.`data`.`inner`.Inner$Mutable.kt

In order to fix this the following imports need to be added to the inner generated file:

import me.test.data.freeze
import me.test.data.toMutable

I created a reproducer at https://github.com/dronda-t/kopy-kt-missing-import if you want to try for yourself.

dronda-t commented 10 months ago

I think it may be due to this line here https://github.com/kopykat-kt/kopykat/blob/2800bc06c451375b3b0200ff1e6c6bae00365ade/kopykat-ksp/src/main/kotlin/at/kopyk/MutableCopy.kt#L100

Everything is passed in as a string instead of a template. I don't have much experience with Kotlin Poet but based off the documentation it seems like you need to use the template format in order to get the correct imports.

dronda-t commented 10 months ago

I took a stab at this but I'm running into some very strange errors.

Stacktrace ``` [ksp] java.lang.ClassCastException: class [Lcom.squareup.kotlinpoet.MemberName; cannot be cast to class com.squareup.kotlinpoet.MemberName ([Lcom.squareup.kotlinpoet.MemberName; and com.squareup.kotlinpoet.MemberName are in unnamed module of loader java.net.URLClassLoader @5ed5da13) at com.squareup.kotlinpoet.CodeWriter.emitCode(CodeWriter.kt:310) at com.squareup.kotlinpoet.CodeWriter.emitCode$default(CodeWriter.kt:237) at com.squareup.kotlinpoet.CodeWriter.emitLiteral(CodeWriter.kt:408) at com.squareup.kotlinpoet.CodeWriter.emitCode(CodeWriter.kt:247) at com.squareup.kotlinpoet.CodeWriter.emitCode$default(CodeWriter.kt:237) at com.squareup.kotlinpoet.FunSpec.emit$kotlinpoet(FunSpec.kt:121) at com.squareup.kotlinpoet.FileSpec.emit(FileSpec.kt:171) at com.squareup.kotlinpoet.FileSpec.access$emit(FileSpec.kt:46) at com.squareup.kotlinpoet.FileSpec$writeTo$codeWriter$1.invoke(FileSpec.kt:68) at com.squareup.kotlinpoet.FileSpec$writeTo$codeWriter$1.invoke(FileSpec.kt:64) at com.squareup.kotlinpoet.CodeWriter$Companion.withCollectedImports(CodeWriter.kt:708) at com.squareup.kotlinpoet.FileSpec.writeTo(FileSpec.kt:64) at com.squareup.kotlinpoet.ksp.OriginatingKSFilesKt.writeTo(OriginatingKSFiles.kt:131) at com.squareup.kotlinpoet.ksp.OriginatingKSFilesKt.writeTo(OriginatingKSFiles.kt:111) at com.squareup.kotlinpoet.ksp.OriginatingKSFilesKt.writeTo$default(OriginatingKSFiles.kt:105) at at.kopyk.poet.KotlinPoetExtensionsKt.writeTo(KotlinPoetExtensions.kt:36) at at.kopyk.FileCompileScope.write(FileCompileScope.kt:74) at at.kopyk.KopyKatProcessor$process$1.invoke(KopyKatProcessor.kt:50) at at.kopyk.KopyKatProcessor$process$1.invoke(KopyKatProcessor.kt:42) at at.kopyk.FileCompileScopeKt.processFiles(FileCompileScope.kt:29) at at.kopyk.KopyKatProcessor.process(KopyKatProcessor.kt:42) at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension$doAnalysis$8$1.invoke(KotlinSymbolProcessingExtension.kt:305) at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension$doAnalysis$8$1.invoke(KotlinSymbolProcessingExtension.kt:303) at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.handleException(KotlinSymbolProcessingExtension.kt:409) at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:303) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:112) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:77) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:256) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:247) at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:115) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:247) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:87) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:43) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:165) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:50) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:104) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:48) at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1523) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197) at java.base/java.security.AccessController.doPrivileged(AccessController.java:712) at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196) at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:840) ```