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.JCTree.*
|
||||||
import com.sun.tools.javac.tree.TreeMaker
|
import com.sun.tools.javac.tree.TreeMaker
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.kapt3.*
|
import org.jetbrains.kotlin.kapt3.*
|
||||||
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
|
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.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
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.resolve.descriptorUtil.getSuperClassOrAny
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
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.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
@@ -85,9 +81,13 @@ class ClassFileToSourceStubConverter(
|
|||||||
get() = _bindings
|
get() = _bindings
|
||||||
|
|
||||||
private val fileManager = kaptContext.context.get(JavaFileManager::class.java) as JavacFileManager
|
private val fileManager = kaptContext.context.get(JavaFileManager::class.java) as JavacFileManager
|
||||||
|
|
||||||
val treeMaker = TreeMaker.instance(kaptContext.context) as KaptTreeMaker
|
val treeMaker = TreeMaker.instance(kaptContext.context) as KaptTreeMaker
|
||||||
|
|
||||||
private val signatureParser = SignatureParser(treeMaker)
|
private val signatureParser = SignatureParser(treeMaker)
|
||||||
|
|
||||||
|
private val anonymousTypeHandler = AnonymousTypeHandler(this)
|
||||||
|
|
||||||
private var done = false
|
private var done = false
|
||||||
|
|
||||||
fun convert(): JavacList<JCCompilationUnit> {
|
fun convert(): JavacList<JCCompilationUnit> {
|
||||||
@@ -122,7 +122,7 @@ class ClassFileToSourceStubConverter(
|
|||||||
private fun convertTopLevelClass(clazz: ClassNode): JCCompilationUnit? {
|
private fun convertTopLevelClass(clazz: ClassNode): JCCompilationUnit? {
|
||||||
val origin = kaptContext.origins[clazz] ?: return null
|
val origin = kaptContext.origins[clazz] ?: return null
|
||||||
val ktFile = origin.element?.containingFile as? KtFile ?: 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
|
// Nested classes will be processed during the outer classes conversion
|
||||||
if ((descriptor as? ClassDescriptor)?.isNested ?: false) return null
|
if ((descriptor as? ClassDescriptor)?.isNested ?: false) return null
|
||||||
@@ -191,7 +191,7 @@ class ClassFileToSourceStubConverter(
|
|||||||
if (isSynthetic(clazz.access)) return null
|
if (isSynthetic(clazz.access)) return null
|
||||||
if (checkIfShouldBeIgnored(Type.getObjectType(clazz.name))) 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 isNested = (descriptor as? ClassDescriptor)?.isNested ?: false
|
||||||
val isInner = isNested && (descriptor as? ClassDescriptor)?.isInner ?: false
|
val isInner = isNested && (descriptor as? ClassDescriptor)?.isInner ?: false
|
||||||
|
|
||||||
@@ -375,7 +375,7 @@ class ClassFileToSourceStubConverter(
|
|||||||
val typeExpression = if (isEnum(field.access))
|
val typeExpression = if (isEnum(field.access))
|
||||||
treeMaker.SimpleName(type.className.substringAfterLast('.'))
|
treeMaker.SimpleName(type.className.substringAfterLast('.'))
|
||||||
else
|
else
|
||||||
getNotAnonymousType(descriptor) {
|
anonymousTypeHandler.getNonAnonymousType(descriptor) {
|
||||||
getNonErrorType((descriptor as? CallableDescriptor)?.returnType,
|
getNonErrorType((descriptor as? CallableDescriptor)?.returnType,
|
||||||
ktTypeProvider = { (kaptContext.origins[field]?.element as? KtVariableDeclaration)?.typeReference },
|
ktTypeProvider = { (kaptContext.origins[field]?.element as? KtVariableDeclaration)?.typeReference },
|
||||||
ifNonError = { signatureParser.parseFieldSignature(field.signature, treeMaker.Type(type)) })
|
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,
|
getNonErrorType(descriptor.returnType,
|
||||||
ktTypeProvider = {
|
ktTypeProvider = {
|
||||||
val element = kaptContext.origins[method]?.element
|
val element = kaptContext.origins[method]?.element
|
||||||
@@ -570,37 +570,6 @@ class ClassFileToSourceStubConverter(
|
|||||||
return name
|
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")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun convertModifiers(
|
private inline fun convertModifiers(
|
||||||
access: Int,
|
access: Int,
|
||||||
|
|||||||
+6
@@ -186,6 +186,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("mapEntry.kt")
|
||||||
public void testMapEntry() throws Exception {
|
public void testMapEntry() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/mapEntry.kt");
|
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