Kapt: Fix "Anonymous class types are not rendered properly in stubs" (KT-18682)

This commit is contained in:
Yan Zhulanow
2017-06-28 22:14:26 +03:00
parent bf2bc1fdc7
commit 4dddb00aca
5 changed files with 187 additions and 39 deletions
@@ -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)
}
})
}
}
@@ -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,
@@ -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
View File
@@ -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
View File
@@ -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;
}
}