Kapt: Fix "Anonymous class types are not rendered properly in stubs" (KT-18682)
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kapt3.stubs
|
||||
|
||||
import com.sun.tools.javac.code.BoundKind
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.kapt3.mapJList
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.isAnonymousObject
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.replace
|
||||
|
||||
class AnonymousTypeHandler(private val converter: ClassFileToSourceStubConverter) {
|
||||
fun <T : JCTree.JCExpression?> getNonAnonymousType(descriptor: DeclarationDescriptor?, f: () -> T): T {
|
||||
val classType = when (descriptor) {
|
||||
is ClassDescriptor -> descriptor.defaultType
|
||||
is CallableDescriptor -> descriptor.returnType
|
||||
else -> null
|
||||
} ?: return f()
|
||||
|
||||
return getNonAnonymousType(classType, f)
|
||||
}
|
||||
|
||||
fun <T : JCTree.JCExpression?> getNonAnonymousType(type: KotlinType, f: () -> T): T {
|
||||
if (!checkIfAnonymousRecursively(type)) return f()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return convertKotlinType(convertPossiblyAnonymousType(type)) as T
|
||||
}
|
||||
|
||||
private fun checkIfAnonymousRecursively(type: KotlinType): Boolean {
|
||||
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
if (isAnonymousObject(declaration)) return true
|
||||
return type.arguments.any {
|
||||
if (it.isStarProjection) return@any false
|
||||
checkIfAnonymousRecursively(it.type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertPossiblyAnonymousType(type: KotlinType): KotlinType {
|
||||
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return type
|
||||
|
||||
val actualType = when {
|
||||
isAnonymousObject(declaration) -> findMostSuitableParentForAnonymousType(declaration)
|
||||
else -> declaration.defaultType
|
||||
}
|
||||
|
||||
if (type.arguments.isEmpty()) return actualType
|
||||
|
||||
val arguments = type.arguments.map { typeArg ->
|
||||
if (typeArg.isStarProjection) return@map typeArg
|
||||
TypeProjectionImpl(typeArg.projectionKind, convertPossiblyAnonymousType(typeArg.type))
|
||||
}
|
||||
|
||||
return actualType.replace(arguments)
|
||||
}
|
||||
|
||||
private fun findMostSuitableParentForAnonymousType(descriptor: ClassDescriptor): KotlinType {
|
||||
descriptor.getSuperClassNotAny()?.let { return it.defaultType }
|
||||
|
||||
val sortedSuperTypes = descriptor.typeConstructor.supertypes
|
||||
.sortedBy { it.constructor.declarationDescriptor?.name?.asString() ?: "" }
|
||||
|
||||
for (candidate in sortedSuperTypes) {
|
||||
if (!candidate.isAnyOrNullableAny()) return candidate
|
||||
}
|
||||
|
||||
return descriptor.builtIns.anyType
|
||||
}
|
||||
|
||||
private fun convertKotlinType(type: KotlinType): JCTree.JCExpression {
|
||||
val typeMapper = converter.kaptContext.generationState.typeMapper
|
||||
|
||||
val treeMaker = converter.treeMaker
|
||||
val selfType = treeMaker.Type(typeMapper.mapType(type))
|
||||
if (type.arguments.isEmpty()) return selfType
|
||||
|
||||
return treeMaker.TypeApply(selfType, mapJList(type.arguments) { projection ->
|
||||
if (projection.isStarProjection) return@mapJList treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.UNBOUND), null)
|
||||
|
||||
val renderedArg = convertKotlinType(projection.type)
|
||||
when (projection.projectionKind) {
|
||||
Variance.INVARIANT -> renderedArg
|
||||
Variance.OUT_VARIANCE -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.EXTENDS), renderedArg)
|
||||
Variance.IN_VARIANCE -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.SUPER), renderedArg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+8
-39
@@ -24,7 +24,6 @@ import com.sun.tools.javac.tree.JCTree
|
||||
import com.sun.tools.javac.tree.JCTree.*
|
||||
import com.sun.tools.javac.tree.TreeMaker
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.kapt3.*
|
||||
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
|
||||
@@ -33,11 +32,8 @@ import org.jetbrains.kotlin.kapt3.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
@@ -85,9 +81,13 @@ class ClassFileToSourceStubConverter(
|
||||
get() = _bindings
|
||||
|
||||
private val fileManager = kaptContext.context.get(JavaFileManager::class.java) as JavacFileManager
|
||||
|
||||
val treeMaker = TreeMaker.instance(kaptContext.context) as KaptTreeMaker
|
||||
|
||||
private val signatureParser = SignatureParser(treeMaker)
|
||||
|
||||
private val anonymousTypeHandler = AnonymousTypeHandler(this)
|
||||
|
||||
private var done = false
|
||||
|
||||
fun convert(): JavacList<JCCompilationUnit> {
|
||||
@@ -122,7 +122,7 @@ class ClassFileToSourceStubConverter(
|
||||
private fun convertTopLevelClass(clazz: ClassNode): JCCompilationUnit? {
|
||||
val origin = kaptContext.origins[clazz] ?: return null
|
||||
val ktFile = origin.element?.containingFile as? KtFile ?: return null
|
||||
val descriptor = origin.descriptor as? DeclarationDescriptor ?: return null
|
||||
val descriptor = origin.descriptor ?: return null
|
||||
|
||||
// Nested classes will be processed during the outer classes conversion
|
||||
if ((descriptor as? ClassDescriptor)?.isNested ?: false) return null
|
||||
@@ -191,7 +191,7 @@ class ClassFileToSourceStubConverter(
|
||||
if (isSynthetic(clazz.access)) return null
|
||||
if (checkIfShouldBeIgnored(Type.getObjectType(clazz.name))) return null
|
||||
|
||||
val descriptor = kaptContext.origins[clazz]?.descriptor as? DeclarationDescriptor ?: return null
|
||||
val descriptor = kaptContext.origins[clazz]?.descriptor ?: return null
|
||||
val isNested = (descriptor as? ClassDescriptor)?.isNested ?: false
|
||||
val isInner = isNested && (descriptor as? ClassDescriptor)?.isInner ?: false
|
||||
|
||||
@@ -375,7 +375,7 @@ class ClassFileToSourceStubConverter(
|
||||
val typeExpression = if (isEnum(field.access))
|
||||
treeMaker.SimpleName(type.className.substringAfterLast('.'))
|
||||
else
|
||||
getNotAnonymousType(descriptor) {
|
||||
anonymousTypeHandler.getNonAnonymousType(descriptor) {
|
||||
getNonErrorType((descriptor as? CallableDescriptor)?.returnType,
|
||||
ktTypeProvider = { (kaptContext.origins[field]?.element as? KtVariableDeclaration)?.typeReference },
|
||||
ifNonError = { signatureParser.parseFieldSignature(field.signature, treeMaker.Type(type)) })
|
||||
@@ -513,7 +513,7 @@ class ClassFileToSourceStubConverter(
|
||||
}
|
||||
})
|
||||
|
||||
val returnType = getNotAnonymousType(descriptor) {
|
||||
val returnType = anonymousTypeHandler.getNonAnonymousType(descriptor) {
|
||||
getNonErrorType(descriptor.returnType,
|
||||
ktTypeProvider = {
|
||||
val element = kaptContext.origins[method]?.element
|
||||
@@ -570,37 +570,6 @@ class ClassFileToSourceStubConverter(
|
||||
return name
|
||||
}
|
||||
|
||||
private inline fun <T : JCExpression?> getNotAnonymousType(descriptor: DeclarationDescriptor?, f: () -> T): T {
|
||||
if (descriptor is CallableDescriptor) {
|
||||
val returnTypeDescriptor = descriptor.returnType?.constructor?.declarationDescriptor
|
||||
if (returnTypeDescriptor is ClassDescriptor && DescriptorUtils.isAnonymousObject(returnTypeDescriptor)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return getMostSuitableSuperTypeForAnonymousType(returnTypeDescriptor) as T
|
||||
}
|
||||
}
|
||||
|
||||
return f()
|
||||
}
|
||||
|
||||
private fun getMostSuitableSuperTypeForAnonymousType(typeDescriptor: ClassDescriptor): JCExpression {
|
||||
val superClass = typeDescriptor.getSuperClassNotAny()
|
||||
val typeMapper = kaptContext.generationState.typeMapper
|
||||
|
||||
if (superClass != null) {
|
||||
return treeMaker.Type(typeMapper.mapType(superClass))
|
||||
} else {
|
||||
val sortedSuperTypes = typeDescriptor.typeConstructor.supertypes
|
||||
.sortedBy { it.constructor.declarationDescriptor?.name?.asString() ?: "" }
|
||||
|
||||
for (superType in sortedSuperTypes) {
|
||||
if (superType.isAnyOrNullableAny()) continue
|
||||
return treeMaker.Type(typeMapper.mapType(superType))
|
||||
}
|
||||
}
|
||||
|
||||
return treeMaker.FqName("java.lang.Object")
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun convertModifiers(
|
||||
access: Int,
|
||||
|
||||
+6
@@ -186,6 +186,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt18682.kt")
|
||||
public void testKt18682() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/kt18682.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("mapEntry.kt")
|
||||
public void testMapEntry() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/mapEntry.kt");
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun test1() = (0..10).map { n ->
|
||||
object {
|
||||
override fun hashCode() = n
|
||||
}
|
||||
}
|
||||
|
||||
fun test2() = (0..10).map { n ->
|
||||
object : Runnable {
|
||||
override fun run() {}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Foo
|
||||
|
||||
fun test3() = (0..10).map { n ->
|
||||
object : Foo() {}
|
||||
}
|
||||
|
||||
fun test4() = (0..10).map { n ->
|
||||
object : Foo(), Runnable {
|
||||
override fun run() {}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
@kotlin.Metadata()
|
||||
public abstract class Foo {
|
||||
|
||||
public Foo() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
@kotlin.Metadata()
|
||||
public final class Kt18682Kt {
|
||||
|
||||
public Kt18682Kt() {
|
||||
super();
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public static final java.util.List<java.lang.Object> test1() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public static final java.util.List<java.lang.Runnable> test2() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public static final java.util.List<Foo> test3() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public static final java.util.List<Foo> test4() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user