Kapt+JVM_IR: fix ordering of properties in generated stubs

Apparently it depended on the ordering of the synthetic `$annotations`
methods generated for properties. And those methods were always
generated at the end of a Java stub by kapt because they lacked PSI
element, and thus `ClassFileToSourceStubConverter.convertClass` could
not compute their source position, and it placed them at the end as all
other synthetic methods.

The solution is to provide PSI element for `$annotations` methods in
JvmDeclarationOrigin. This origin is then collected in kapt in
`OriginCollectingClassBuilderFactory` and used for sorting, as in the
case of the old JVM backend.

 #KT-56360 Fixed
This commit is contained in:
Alexander Udalov
2023-02-02 16:28:44 +01:00
committed by Space Team
parent 67c4dc3cce
commit 1844869e8a
10 changed files with 90 additions and 251 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.jvm.codegen
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.backend.common.lower.ANNOTATION_IMPLEMENTATION
import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
@@ -38,7 +39,10 @@ import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.getArrayElementType
import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
@@ -525,15 +529,7 @@ class ClassCodegen private constructor(
private val IrDeclaration.descriptorOrigin: JvmDeclarationOrigin
get() {
val psiElement = PsiSourceManager.findPsiElement(this).let { element ->
// Offsets for accessors and field of delegated property in IR point to the 'by' keyword, so the closest PSI element is the
// KtPropertyDelegate (`by ...` expression). However, old JVM backend passed the PSI element of the property instead.
// This is important for example in case of KAPT stub generation in the "correct error types" mode, which tries to find the
// PSI element for each declaration with unresolved types and tries to heuristically "resolve" those unresolved types to
// generate them into the Java stub. In case of delegated property accessors, it should look for the property declaration,
// since the type can only be provided there, and not in the `by ...` expression.
if (element is KtPropertyDelegate) element.parent else element
}
val psiElement = findPsiElementForDeclarationOrigin()
return when {
origin == IrDeclarationOrigin.FILE_CLASS ->
JvmDeclarationOrigin(JvmDeclarationOriginKind.PACKAGE_PART, psiElement, toIrBasedDescriptor())
@@ -545,6 +541,27 @@ class ClassCodegen private constructor(
}
}
private fun IrDeclaration.findPsiElementForDeclarationOrigin(): PsiElement? {
// For synthetic $annotations methods for properties, use the PSI for the property or the constructor parameter.
// It's used in KAPT stub generation to sort the properties correctly based on their source position (see KT-44130).
if (this is IrFunction && name.asString().endsWith("\$annotations")) {
val metadata = metadata as? DescriptorMetadataSource.Property
if (metadata != null) {
return metadata.descriptor.psiElement
}
}
val element = PsiSourceManager.findPsiElement(this)
// Offsets for accessors and field of delegated property in IR point to the 'by' keyword, so the closest PSI element is the
// KtPropertyDelegate (`by ...` expression). However, old JVM backend passed the PSI element of the property instead.
// This is important for example in case of KAPT stub generation in the "correct error types" mode, which tries to find the
// PSI element for each declaration with unresolved types and tries to heuristically "resolve" those unresolved types to
// generate them into the Java stub. In case of delegated property accessors, it should look for the property declaration,
// since the type can only be provided there, and not in the `by ...` expression.
return if (element is KtPropertyDelegate) element.parent else element
}
private fun storeSerializedIr(serializedIr: ByteArray) {
val av = visitor.newAnnotation(JvmAnnotationNames.SERIALIZED_IR_DESC, true)
val partsVisitor = av.visitArray(JvmAnnotationNames.SERIALIZED_IR_BYTES_FIELD_NAME)