diff --git a/compiler-plugin/src/main/kotlin/community/flock/kmapper/compiler/fir/KMapperFirMappingChecker.kt b/compiler-plugin/src/main/kotlin/community/flock/kmapper/compiler/fir/KMapperFirMappingChecker.kt index 558a0b1..e39244f 100644 --- a/compiler-plugin/src/main/kotlin/community/flock/kmapper/compiler/fir/KMapperFirMappingChecker.kt +++ b/compiler-plugin/src/main/kotlin/community/flock/kmapper/compiler/fir/KMapperFirMappingChecker.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass import org.jetbrains.kotlin.fir.expressions.FirAnonymousFunctionExpression import org.jetbrains.kotlin.fir.expressions.FirCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.arguments import org.jetbrains.kotlin.fir.expressions.explicitReceiver import org.jetbrains.kotlin.fir.expressions.toReference @@ -96,10 +97,13 @@ class KMapperFirMappingChecker(val collector: MessageCollector, private val sess val propSymbol = call.explicitReceiver ?.toReference(session) ?.toResolvedPropertySymbol() + val propName = propSymbol?.name + ?: (call.explicitReceiver as? FirQualifiedAccessExpression) + ?.calleeReference?.let { it as? FirNamedReference }?.name val valueExpr = call.arguments.firstOrNull() - if (propSymbol != null && valueExpr != null) { + if (propName != null && valueExpr != null) { Field( - name = propSymbol.name, + name = propName, type = valueExpr.resolvedType, hasDefaultValue = false, fields = valueExpr.resolvedType.resolveConstructorFields() @@ -110,9 +114,12 @@ class KMapperFirMappingChecker(val collector: MessageCollector, private val sess val propSymbol = call.extensionReceiver ?.toReference(session) ?.toResolvedPropertySymbol() - if (propSymbol != null) { + val propName = propSymbol?.name + ?: (call.extensionReceiver as? FirQualifiedAccessExpression) + ?.calleeReference?.let { it as? FirNamedReference }?.name + if (propName != null) { // Find the matching toField to include its type info - toFields.find { it.name == propSymbol.name } + toFields.find { it.name == propName } } else null } else -> null diff --git a/test-integration/src/test/kotlin/community/flock/kmapper/SerializableTest.kt b/test-integration/src/test/kotlin/community/flock/kmapper/SerializableTest.kt index 28edc48..52c38f7 100644 --- a/test-integration/src/test/kotlin/community/flock/kmapper/SerializableTest.kt +++ b/test-integration/src/test/kotlin/community/flock/kmapper/SerializableTest.kt @@ -111,6 +111,65 @@ class SerializableTest { } } + @Test + fun shouldCompile_explicitMappingWithSerializableTargetInSeparateFile() { + IntegrationTest(options) + .file("Dto.kt") { + $$""" + |package sample + | + |import kotlinx.serialization.Serializable + |import kotlinx.serialization.SerialName + | + |@Serializable + |@SerialName("RunAnnotationDataDto") + |data class RunAnnotationDataDto( + | val runId: String, + | val displayId: String, + | val inputHash: String, + | val outputJson: String, + |) + | + """.trimMargin() + } + .file("App.kt") { + $$""" + |package sample + | + |import community.flock.kmapper.mapper + | + |data class RunAnnotationData( + | val runId: String, + | val sequenceNumber: Int, + | val inputHash: String, + | val outputJson: String, + |) + | + |fun RunAnnotationData.toDto(): RunAnnotationDataDto = mapper { + | displayId = it.sequenceNumber.toString() + |} + | + |fun main() { + | val data = RunAnnotationData( + | runId = "run-1", + | sequenceNumber = 42, + | inputHash = "abc123", + | outputJson = "{}" + | ) + | val dto = data.toDto() + | println(dto) + |} + | + """.trimMargin() + } + .compileSuccess { output -> + assertTrue( + output.contains("RunAnnotationDataDto(runId=run-1, displayId=42, inputHash=abc123, outputJson={})"), + "Expected RunAnnotationDataDto(runId=run-1, displayId=42, inputHash=abc123, outputJson={}) in output" + ) + } + } + @Test fun shouldCompile_allFieldsAutoMappedWithSerializable() { IntegrationTest(options)