[KAPT] Fix stub generation for anonymous delegates

Replace anonymous classes with superclass in delegate properties
#KT-34838 Fixed
This commit is contained in:
Andrey Zinovyev
2021-02-03 17:27:41 +03:00
committed by GitHub
parent 60a05dded0
commit 923a4427c5
7 changed files with 184 additions and 49 deletions

View File

@@ -5,17 +5,12 @@
package org.jetbrains.kotlin.kapt3
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.kapt3.util.replaceAnonymousTypeWithSuperType
import org.jetbrains.kotlin.resolve.DeclarationSignatureAnonymousTypeTransformer
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.*
import org.jetbrains.kotlin.resolve.DescriptorUtils.isLocal
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.replace
import org.jetbrains.kotlin.types.typeUtil.builtIns
class KaptAnonymousTypeTransformer(
private val analysisExtension: PartialAnalysisHandlerExtension
@@ -29,46 +24,6 @@ class KaptAnonymousTypeTransformer(
return type
}
return convertPossiblyAnonymousType(type)
return replaceAnonymousTypeWithSuperType(type)
}
}
internal fun convertPossiblyAnonymousType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return type
if (KotlinBuiltIns.isArray(type)) {
val elementTypeProjection = type.arguments.singleOrNull()
if (elementTypeProjection != null && !elementTypeProjection.isStarProjection) {
return type.builtIns.getArrayType(
elementTypeProjection.projectionKind,
convertPossiblyAnonymousType(elementTypeProjection.type)
)
}
}
val actualType = when {
isAnonymousObject(declaration) || DescriptorUtils.isLocal(declaration) -> {
if (type.constructor.supertypes.size == 1) {
convertPossiblyAnonymousType(type.constructor.supertypes.iterator().next())
} else {
/*
Frontend reports an error on public properties in this case,
but we ignore errors when making stubs, so there should be a reasonable fallback.
*/
type.builtIns.anyType
}
}
else -> type
}
if (actualType.arguments.isEmpty()) return actualType
val arguments = actualType.arguments.map { typeArg ->
if (typeArg.isStarProjection)
return@map typeArg
TypeProjectionImpl(typeArg.projectionKind, convertPossiblyAnonymousType(typeArg.type))
}
return actualType.replace(newArguments = arguments)
}

View File

@@ -49,6 +49,7 @@ import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.METHOD_PARAM
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.RETURN_TYPE
import org.jetbrains.kotlin.kapt3.util.*
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
@@ -62,6 +63,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
@@ -690,7 +692,8 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
val name = field.name
if (!isValidIdentifier(name)) return null
val type = Type.getType(field.desc)
val type = getFieldType(field, origin)
if (!checkIfValidTypeName(containingClass, type)) {
return null
}
@@ -1420,6 +1423,25 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
lineMappings.registerSignature(this, node)
return this
}
private fun getFieldType(field: FieldNode, origin: JvmDeclarationOrigin?): Type {
val fieldType = Type.getType(field.desc)
return when (val declaration = origin?.element) {
is KtProperty -> {
//replace anonymous type in delegate (if any)
val delegateType = kaptContext.bindingContext[BindingContext.EXPRESSION_TYPE_INFO, declaration.delegateExpression]?.type
delegateType?.let {
val replaced = replaceAnonymousTypeWithSuperType(it)
//not changed => not anonymous type => use type from field
if (replaced != it) replaced else null
}?.let(::convertKotlinType) ?: fieldType
}
else -> fieldType
}
}
private fun convertKotlinType(type: KotlinType): Type = typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT)
}
private fun Any?.isOfPrimitiveType(): Boolean = when (this) {

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.util
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.replace
import org.jetbrains.kotlin.types.typeUtil.builtIns
fun replaceAnonymousTypeWithSuperType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return type
if (KotlinBuiltIns.isArray(type)) {
val elementTypeProjection = type.arguments.singleOrNull()
if (elementTypeProjection != null && !elementTypeProjection.isStarProjection) {
return type.builtIns.getArrayType(
elementTypeProjection.projectionKind,
replaceAnonymousTypeWithSuperType(elementTypeProjection.type)
)
}
}
val actualType = when {
DescriptorUtils.isAnonymousObject(declaration) || DescriptorUtils.isLocal(declaration) -> {
if (type.constructor.supertypes.size == 1) {
replaceAnonymousTypeWithSuperType(type.constructor.supertypes.iterator().next())
} else {
/*
Frontend reports an error on public properties in this case,
but we ignore errors when making stubs, so there should be a reasonable fallback.
*/
type.builtIns.anyType
}
}
else -> type
}
if (actualType.arguments.isEmpty()) return actualType
val arguments = actualType.arguments.map { typeArg ->
if (typeArg.isStarProjection)
return@map typeArg
TypeProjectionImpl(typeArg.projectionKind, replaceAnonymousTypeWithSuperType(typeArg.type))
}
return actualType.replace(newArguments = arguments)
}

View File

@@ -69,6 +69,11 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
runTest("plugins/kapt3/kapt3-compiler/testData/converter/annotationsWithTargets.kt");
}
@TestMetadata("anonymousDelegate.kt")
public void testAnonymousDelegate() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/anonymousDelegate.kt");
}
@TestMetadata("comments.kt")
public void testComments() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/comments.kt");

View File

@@ -70,6 +70,11 @@ public class IrClassFileToSourceStubConverterTestGenerated extends AbstractIrCla
runTest("plugins/kapt3/kapt3-compiler/testData/converter/annotationsWithTargets.kt");
}
@TestMetadata("anonymousDelegate.kt")
public void testAnonymousDelegate() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/anonymousDelegate.kt");
}
@TestMetadata("comments.kt")
public void testComments() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/comments.kt");

View File

@@ -0,0 +1,34 @@
import kotlin.reflect.KProperty
class Test {
var broken by object {
operator fun getValue(obj: Test, property: KProperty<*>) = Any()
operator fun setValue(obj: Test, property: KProperty<*>, any: Any) {
}
}
var overridden by object : java.io.Serializable {
operator fun getValue(obj: Test, property: KProperty<*>) = Any()
operator fun setValue(obj: Test, property: KProperty<*>, any: Any) {
}
}
private val lazyProp by lazy {
object : Runnable {
override fun run() {}
}
}
}
var delegate by object {
operator fun getValue(nothing: Nothing?, property: KProperty<*>) = Any()
operator fun setValue(nothing: Nothing?, property: KProperty<*>, any: Any) {
//empty
}
}

View File

@@ -0,0 +1,60 @@
import java.lang.System;
@kotlin.Metadata()
public final class AnonymousDelegateKt {
public AnonymousDelegateKt() {
super();
}
@org.jetbrains.annotations.NotNull()
private static final java.lang.Object delegate$delegate = null;
@org.jetbrains.annotations.NotNull()
public static final java.lang.Object getDelegate() {
return null;
}
public static final void setDelegate(@org.jetbrains.annotations.NotNull()
java.lang.Object p0) {
}
}
////////////////////
import java.lang.System;
@kotlin.Metadata()
public final class Test {
@org.jetbrains.annotations.NotNull()
private final java.lang.Object broken$delegate = null;
@org.jetbrains.annotations.NotNull()
private final java.io.Serializable overridden$delegate = null;
private final kotlin.Lazy lazyProp$delegate = null;
public Test() {
super();
}
@org.jetbrains.annotations.NotNull()
public final java.lang.Object getBroken() {
return null;
}
public final void setBroken(@org.jetbrains.annotations.NotNull()
java.lang.Object p0) {
}
@org.jetbrains.annotations.NotNull()
public final java.lang.Object getOverridden() {
return null;
}
public final void setOverridden(@org.jetbrains.annotations.NotNull()
java.lang.Object p0) {
}
private final java.lang.Runnable getLazyProp() {
return null;
}
}