diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/AnonymousTypeHandler.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/AnonymousTypeHandler.kt new file mode 100644 index 00000000000..9ed48a0190d --- /dev/null +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/AnonymousTypeHandler.kt @@ -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 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 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) + } + }) + } +} \ No newline at end of file diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt index 2b80d3aa42b..ac4ba724e22 100644 --- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt @@ -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 { @@ -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 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, diff --git a/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java index 17dcbe48616..9e8be9f4008 100644 --- a/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java +++ b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java @@ -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"); diff --git a/plugins/kapt3/testData/converter/kt18682.kt b/plugins/kapt3/testData/converter/kt18682.kt new file mode 100644 index 00000000000..a9f1c4633f5 --- /dev/null +++ b/plugins/kapt3/testData/converter/kt18682.kt @@ -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() {} + } +} \ No newline at end of file diff --git a/plugins/kapt3/testData/converter/kt18682.txt b/plugins/kapt3/testData/converter/kt18682.txt new file mode 100644 index 00000000000..e02c7426051 --- /dev/null +++ b/plugins/kapt3/testData/converter/kt18682.txt @@ -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 test1() { + return null; + } + + @org.jetbrains.annotations.NotNull() + public static final java.util.List test2() { + return null; + } + + @org.jetbrains.annotations.NotNull() + public static final java.util.List test3() { + return null; + } + + @org.jetbrains.annotations.NotNull() + public static final java.util.List test4() { + return null; + } +}