From fb76e5dacdb7c4d0955d2ea65d0650e59842845c Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Wed, 3 May 2017 14:34:32 +0300 Subject: [PATCH] Use wrappers around java.util.* to emulate kotlin.collection.* behaviour Backend: If kotlin class extends kotlin.collection.List write it as it's super interface (light class mode only) IDE: Provide wrapper classes to java resolve that try to emulate backend behaviour For example if kotlin class implements kotlin.collections.Map, we provide a superinterface that has abstract 'getEntries' method and 'entrySet' method that is considered default. In reality all those methods are generated in the class itself. In IDE supporting this case without hacks is not feasible performance-wise since kotlin.collection.* may not be an immediate supertype and we need to compute all supertypes just to calculate own methods of the class --- .../codegen/ImplementationBodyCodegen.java | 19 +- .../CliLightClassGenerationSupport.kt | 2 +- .../asJava/LightClassGenerationSupport.kt | 2 +- .../asJava/finder/JavaElementFinder.java | 2 +- .../ideRegression/ImplementingMap.java | 5 + .../ideRegression/ImplementingMap.kt | 8 + .../ideRegression/ImplementingMutableSet.java | 43 ++ .../ideRegression/ImplementingMutableSet.kt | 35 ++ .../kotlin/load/java/specialBuiltinMembers.kt | 5 +- .../kotlin/load/kotlin/TypeMappingMode.kt | 8 + .../load/kotlin/typeSignatureMapping.kt | 5 +- .../resolve/IDELightClassGenerationSupport.kt | 31 +- .../resolve/lightClasses/platformWrappers.kt | 417 ++++++++++++++++++ .../ExtendingMutableInterfaces.java | 186 ++++++++ .../ExtendingMutableInterfaces.kt | 44 ++ .../ExtendingReadOnlyInterfaces.java | 185 ++++++++ .../ExtendingReadOnlyInterfaces.kt | 44 ++ .../UsingMutableInterfaces.java | 16 + .../UsingMutableInterfaces.kt | 2 + .../UsingReadOnlyInterfaces.java | 44 ++ .../UsingReadOnlyInterfaces.kt | 44 ++ ...nstKotlinBinariesCheckerTestGenerated.java | 24 + ...ainstKotlinSourceCheckerTestGenerated.java | 24 + .../resolve/IdeLightClassTestGenerated.java | 12 + 24 files changed, 1182 insertions(+), 25 deletions(-) create mode 100644 compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.java create mode 100644 compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.kt create mode 100644 compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.java create mode 100644 compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/platformWrappers.kt create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.java create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.kt create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.java create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.kt create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.java create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.kt create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.java create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 7b0126bdd5c..fb9b41194ba 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; +import org.jetbrains.kotlin.load.kotlin.TypeMappingMode; import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; @@ -306,15 +307,24 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { for (KotlinType supertype : descriptor.getTypeConstructor().getSupertypes()) { if (isJvmInterface(supertype.getConstructor().getDeclarationDescriptor())) { + FqName kotlinInterfaceName = DescriptorUtils.getFqName(supertype.getConstructor().getDeclarationDescriptor()).toSafe(); + sw.writeInterface(); Type jvmInterfaceType = typeMapper.mapSupertype(supertype, sw); sw.writeInterfaceEnd(); String jvmInterfaceInternalName = jvmInterfaceType.getInternalName(); + superInterfaces.add(jvmInterfaceInternalName); - FqName kotlinInterfaceName = DescriptorUtils.getFqName(supertype.getConstructor().getDeclarationDescriptor()).toSafe(); String kotlinMarkerInterfaceInternalName = KOTLIN_MARKER_INTERFACES.get(kotlinInterfaceName); if (kotlinMarkerInterfaceInternalName != null) { + if (typeMapper.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) { + sw.writeInterface(); + Type kotlinCollectionType = typeMapper.mapType(supertype, sw, TypeMappingMode.SUPER_TYPE_KOTLIN_COLLECTIONS_AS_IS); + sw.writeInterfaceEnd(); + superInterfaces.add(kotlinCollectionType.getInternalName()); + } + kotlinMarkerInterfaces.add(kotlinMarkerInterfaceInternalName); } } @@ -357,9 +367,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateFunctionsForDataClasses(); - new CollectionStubMethodGenerator(typeMapper, descriptor).generate(functionCodegen, v); + if (state.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) { + new CollectionStubMethodGenerator(typeMapper, descriptor).generate(functionCodegen, v); + + generateToArray(); + } - generateToArray(); if (context.closure != null) genClosureFields(context.closure, v, typeMapper); diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt index 0ac94641be0..ebc5914c282 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt @@ -163,7 +163,7 @@ class CliLightClassGenerationSupport(project: Project) : LightClassGenerationSup KtLightClassForFacade.createForFacade(psiManager, facadeFqName, scope, filesForFacade)) } - override fun getMultifilePartClasses(partFqName: FqName, scope: GlobalSearchScope): Collection { + override fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection { // return emptyList() } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt index 55d035aa7c5..4a587d6a8d2 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.kt @@ -69,7 +69,7 @@ abstract class LightClassGenerationSupport { abstract fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection - abstract fun getMultifilePartClasses(partFqName: FqName, scope: GlobalSearchScope): Collection + abstract fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection abstract fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/finder/JavaElementFinder.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/finder/JavaElementFinder.java index 4f960012ae3..530941dfdd2 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/finder/JavaElementFinder.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/finder/JavaElementFinder.java @@ -92,7 +92,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM findClassesAndObjects(qualifiedName, scope, answer); answer.addAll(lightClassGenerationSupport.getFacadeClasses(qualifiedName, scope)); - answer.addAll(lightClassGenerationSupport.getMultifilePartClasses(qualifiedName, scope)); + answer.addAll(lightClassGenerationSupport.getKotlinInternalClasses(qualifiedName, scope)); return sortByClasspath(answer, scope).toArray(new PsiClass[answer.size()]); } diff --git a/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.java b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.java new file mode 100644 index 00000000000..35007d9a864 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.java @@ -0,0 +1,5 @@ +public final class TypeHierarchyMap implements java.util.Map,TValue>, kotlin.collections.Map,TValue>, kotlin.jvm.internal.markers.KMappedMarker { + public boolean containsKey(@org.jetbrains.annotations.NotNull java.lang.Class key) { /* compiled code */ } + + public TypeHierarchyMap() { /* compiled code */ } +} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.kt b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.kt new file mode 100644 index 00000000000..48e98982567 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.kt @@ -0,0 +1,8 @@ +// p1.TypeHierarchyMap +package p1 + +class TypeHierarchyMap : Map, TValue> { + override fun containsKey(key: Class<*>): Boolean { + TODO("not implemented") + } +} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.java b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.java new file mode 100644 index 00000000000..de416c31910 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.java @@ -0,0 +1,43 @@ +public final class SmartSet extends kotlin.collections.AbstractSet implements java.util.Set, kotlin.collections.MutableSet, kotlin.jvm.internal.markers.KMutableSet { + private java.lang.Object data; + private int size; + private static final int ARRAY_THRESHOLD = 5; + public static final SmartSet.Companion Companion; + + public int getSize() { /* compiled code */ } + + public void setSize(int i) { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public java.util.Iterator iterator() { /* compiled code */ } + + public boolean add(T element) { /* compiled code */ } + + public void clear() { /* compiled code */ } + + public boolean contains(java.lang.Object element) { /* compiled code */ } + + private SmartSet() { /* compiled code */ } + + @kotlin.jvm.JvmStatic + @org.jetbrains.annotations.NotNull + public static final SmartSet create() { /* compiled code */ } + + @kotlin.jvm.JvmStatic + @org.jetbrains.annotations.NotNull + public static final SmartSet create(@org.jetbrains.annotations.NotNull java.util.Collection set) { /* compiled code */ } + + public static final class Companion { + private final int getARRAY_THRESHOLD() { /* compiled code */ } + + @kotlin.jvm.JvmStatic + @org.jetbrains.annotations.NotNull + public final SmartSet create() { /* compiled code */ } + + @kotlin.jvm.JvmStatic + @org.jetbrains.annotations.NotNull + public final SmartSet create(@org.jetbrains.annotations.NotNull java.util.Collection set) { /* compiled code */ } + + private Companion() { /* compiled code */ } + } +} diff --git a/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.kt b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.kt new file mode 100644 index 00000000000..c8d21f6bb6c --- /dev/null +++ b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.kt @@ -0,0 +1,35 @@ +// SmartSet + +class SmartSet private constructor() : AbstractSet(), MutableSet { + companion object { + private val ARRAY_THRESHOLD = 5 + + @JvmStatic + fun create() = SmartSet() + + @JvmStatic + fun create(set: Collection): SmartSet = TODO() + } + + private var data: Any? = null + + override var size: Int = 0 + + + + override fun iterator(): MutableIterator = TODO() + + override fun add(element: T): Boolean = TODO() + + override fun clear() { + data = null + size = 0 + } + + override fun contains(element: T): Boolean = when { + size == 0 -> false + size == 1 -> data == element + size < ARRAY_THRESHOLD -> element in data as Array + else -> element in data as Set + } +} diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt index 4a1f8b157cf..62947b4e840 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt @@ -50,7 +50,7 @@ private fun String.method(name: String, parameters: String, returnType: String) SignatureBuildingComponents.signature(this@method, "$name($parameters)$returnType")) object BuiltinSpecialProperties { - private val PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP = mapOf( + val PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP = mapOf( BUILTIN_NAMES._enum.childSafe("name") to Name.identifier("name"), BUILTIN_NAMES._enum.childSafe("ordinal") to Name.identifier("ordinal"), BUILTIN_NAMES.collection.child("size") to Name.identifier("size"), @@ -99,6 +99,7 @@ object BuiltinMethodsWithSpecialGenericSignature { ).map { "java/util/Collection".method(it, "Ljava/util/Collection;", JvmPrimitiveType.BOOLEAN.desc) } private val ERASED_COLLECTION_PARAMETER_SIGNATURES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.signature } + val ERASED_COLLECTION_PARAMETER_NAMES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.name.asString() } enum class TypeSafeBarrierDescription(val defaultValue: Any?) { NULL(null), INDEX(-1), FALSE(false), @@ -144,7 +145,7 @@ object BuiltinMethodsWithSpecialGenericSignature { private val SIGNATURE_TO_DEFAULT_VALUES_MAP = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.mapKeys { it.key.signature } private val ERASED_VALUE_PARAMETERS_SHORT_NAMES: Set - private val ERASED_VALUE_PARAMETERS_SIGNATURES: Set + val ERASED_VALUE_PARAMETERS_SIGNATURES: Set init { val allMethods = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.keys + ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/TypeMappingMode.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/TypeMappingMode.kt index 9a068f83f31..d9c582a7530 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/TypeMappingMode.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/TypeMappingMode.kt @@ -26,6 +26,7 @@ class TypeMappingMode private constructor( val skipDeclarationSiteWildcards: Boolean = false, val skipDeclarationSiteWildcardsIfPossible: Boolean = false, private val genericArgumentMode: TypeMappingMode? = null, + val kotlinCollectionsToJavaCollections: Boolean = true, private val genericContravariantArgumentMode: TypeMappingMode? = genericArgumentMode, private val genericInvariantArgumentMode: TypeMappingMode? = genericArgumentMode ) { @@ -49,6 +50,13 @@ class TypeMappingMode private constructor( @JvmField val SUPER_TYPE = TypeMappingMode(skipDeclarationSiteWildcards = true, genericArgumentMode = GENERIC_ARGUMENT) + @JvmField + val SUPER_TYPE_KOTLIN_COLLECTIONS_AS_IS = TypeMappingMode( + skipDeclarationSiteWildcards = true, + genericArgumentMode = GENERIC_ARGUMENT, + kotlinCollectionsToJavaCollections = false + ) + /** * kotlin.reflect.KClass mapped to java.lang.Class * Other types mapped as DEFAULT diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt index 0b838464db2..2bfa3d159f4 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt @@ -81,7 +81,7 @@ fun mapType( ) } - mapBuiltInType(kotlinType, factory, typeMappingConfiguration)?.let { builtInType -> + mapBuiltInType(kotlinType, factory, mode, typeMappingConfiguration)?.let { builtInType -> val jvmType = factory.boxTypeIfNeeded(builtInType, mode.needPrimitiveBoxing) writeGenericType(kotlinType, jvmType, mode) return jvmType @@ -186,6 +186,7 @@ fun hasVoidReturnType(descriptor: CallableDescriptor): Boolean { private fun mapBuiltInType( type: KotlinType, typeFactory: JvmTypeFactory, + mode: TypeMappingMode, typeMappingConfiguration: TypeMappingConfiguration ): T? { val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null @@ -211,6 +212,8 @@ private fun mapBuiltInType( val classId = JavaToKotlinClassMap.mapKotlinToJava(fqName) if (classId != null) { + if (!mode.kotlinCollectionsToJavaCollections && JavaToKotlinClassMap.mutabilityMappings.any { it.javaClass == classId }) return null + return typeFactory.createObjectType(JvmClassName.byClassId(classId, typeMappingConfiguration).internalName) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index eab7705750f..ce8ff65e0fc 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -18,10 +18,7 @@ package org.jetbrains.kotlin.idea.caches.resolve import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile -import com.intellij.psi.ClassFileViewProvider -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiManager +import com.intellij.psi.* import com.intellij.psi.impl.compiled.ClsClassImpl import com.intellij.psi.impl.compiled.ClsFileImpl import com.intellij.psi.search.GlobalSearchScope @@ -30,18 +27,12 @@ import org.jetbrains.kotlin.asJava.LightClassBuilder import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder -import org.jetbrains.kotlin.asJava.classes.FakeLightClassForFileOfPackage -import org.jetbrains.kotlin.asJava.classes.KtLightClass -import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade -import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration +import org.jetbrains.kotlin.asJava.classes.* import org.jetbrains.kotlin.asJava.finder.JavaElementFinder import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName -import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.ClsJavaStubByVirtualFileCache -import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.IDELightClassContexts -import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtLightClassForDecompiledDeclaration -import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.LazyLightClassDataHolder +import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.* import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper import org.jetbrains.kotlin.idea.stubindex.* @@ -162,11 +153,15 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG } } - override fun getMultifilePartClasses(partFqName: FqName, scope: GlobalSearchScope): Collection { - if (partFqName.isRoot) return emptyList() + override fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection { + if (fqName.isRoot) return emptyList() - val facadeKtFiles = StaticFacadeIndexUtil.getMultifileClassForPart(partFqName, scope, project) - val partShortName = partFqName.shortName().asString() + return findPackageParts(fqName, scope) + findPlatformWrapper(fqName, scope) + } + + private fun findPackageParts(fqName: FqName, scope: GlobalSearchScope): List { + val facadeKtFiles = StaticFacadeIndexUtil.getMultifileClassForPart(fqName, scope, project) + val partShortName = fqName.shortName().asString() val partClassFileShortName = partShortName + ".class" return facadeKtFiles.mapNotNull { facadeKtFile -> @@ -182,6 +177,10 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG } } + private fun findPlatformWrapper(fqName: FqName, scope: GlobalSearchScope): Collection { + return platformMutabilityWrapper(fqName) { JavaPsiFacade.getInstance(project).findClass(it, scope) }?.let { listOf(it) }.orEmpty() + } + fun createLightClassForFileFacade( facadeFqName: FqName, facadeFiles: List, diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/platformWrappers.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/platformWrappers.kt new file mode 100644 index 00000000000..38bb8ab28be --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/platformWrappers.kt @@ -0,0 +1,417 @@ +/* + * 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.idea.caches.resolve.lightClasses + + +import com.intellij.openapi.util.Key +import com.intellij.psi.* +import com.intellij.psi.impl.InheritanceImplUtil +import com.intellij.psi.impl.PsiClassImplUtil +import com.intellij.psi.impl.PsiSubstitutorImpl.createSubstitutor +import com.intellij.psi.impl.PsiSuperMethodImplUtil +import com.intellij.psi.impl.light.* +import com.intellij.psi.impl.source.ClassInnerStuffCache +import com.intellij.psi.impl.source.PsiExtensibleClass +import com.intellij.psi.impl.source.PsiImmediateClassType +import com.intellij.psi.util.MethodSignatureBackedByPsiMethod +import com.siyeh.ig.psiutils.TypeUtils +import org.jetbrains.kotlin.asJava.classes.cannotModify +import org.jetbrains.kotlin.asJava.classes.lazyPub +import org.jetbrains.kotlin.asJava.elements.KtLightElementBase +import org.jetbrains.kotlin.builtins.DefaultBuiltIns +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature +import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.JavaToKotlinClassMap +import org.jetbrains.kotlin.psi.UserDataProperty + +private val readOnlyQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMappings.associateBy { + (_, readOnly, _) -> + readOnly.asSingleFqName() +} + +private val mutableQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMappings.associateBy { + (_, _, mutable) -> + mutable.asSingleFqName() +} + +private val membersWithSpecializedSignature: Set = + BuiltinMethodsWithSpecialGenericSignature.ERASED_VALUE_PARAMETERS_SIGNATURES.mapTo(LinkedHashSet()) { + val fqNameString = it.substringBefore('(').replace('/', '.') + FqName(fqNameString).shortName().asString() + } + +private val javaGetterNameToKotlinGetterName: Map = BuiltinSpecialProperties.PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP.map { + (propertyFqName, javaGetterShortName) -> + Pair(javaGetterShortName.asString(), JvmAbi.getterName(propertyFqName.shortName().asString())) +}.toMap() + +fun platformMutabilityWrapper(fqName: FqName, findJavaClass: (String) -> PsiClass?): PsiClass? { + readOnlyQualifiedNamesToJavaClass[fqName]?.let { + (javaClass, kotlinReadOnly) -> + val javaBaseClass = findJavaClass(javaClass.asSingleFqName().asString()) ?: return null + return getOrCreateWrapper(javaBaseClass, kotlinReadOnly.asSingleFqName(), isMutable = false) + } + mutableQualifiedNamesToJavaClass[fqName]?.let { + (javaClass, _, kotlinMutable) -> + val javaBaseClass = findJavaClass(javaClass.asSingleFqName().asString()) ?: return null + return getOrCreateWrapper(javaBaseClass, kotlinMutable.asSingleFqName(), isMutable = true) + } + return null +} + +private fun getOrCreateWrapper(javaBaseClass: PsiClass, kotlinFqName: FqName, isMutable: Boolean): KtLightMutabilityPlatformWrapper { + val userDataStorage = if (isMutable) javaBaseClass::mutableWrapper else javaBaseClass::readOnlyWrapper + return userDataStorage.get() ?: KtLightMutabilityPlatformWrapper(javaBaseClass, kotlinFqName, isMutable).also { userDataStorage.set(it) } +} + +private var PsiClass.readOnlyWrapper: KtLightMutabilityPlatformWrapper? by UserDataProperty(Key.create("READ_ONLY_WRAPPER")) +private var PsiClass.mutableWrapper: KtLightMutabilityPlatformWrapper? by UserDataProperty(Key.create("MUTABLE_WRAPPER")) + +class KtLightMutabilityPlatformWrapper( + private val javaBaseClass: PsiClass, + private val kotlinInterfaceFqName: FqName, + private val isMutable: Boolean +) : KtAbstractContainerWrapper(kotlinInterfaceFqName, javaBaseClass), PsiClass { + private val _methods by lazyPub { calcMethods() } + + private fun calcMethods() = javaBaseClass.methods.flatMap { methodWrappers(it) } + + override fun getOwnMethods() = _methods + + private fun methodWrappers(method: PsiMethod): List { + val methodName = method.name + + javaGetterNameToKotlinGetterName.get(methodName)?.let { kotlinName -> + val finalBridgeForJava = method.finalBridge() + val abstractKotlinGetter = method.wrap(name = kotlinName) + return listOf(finalBridgeForJava, abstractKotlinGetter) + } + + if (!method.isInKotlinInterface()) { + // compiler generates stub override + return listOf(method.openBridge()) + } + + return methodsWithSpecializedSignature(method) + } + + private fun methodsWithSpecializedSignature(method: PsiMethod): List { + val methodName = method.name + + if (methodName !in membersWithSpecializedSignature) return emptyList() + + if (javaBaseClass.qualifiedName == CommonClassNames.JAVA_UTIL_MAP) { + val abstractKotlinVariantWithGeneric = javaUtilMapMethodWithSpecialSignature(method) ?: return emptyList() + val finalBridgeWithObject = method.finalBridge() + return listOf(finalBridgeWithObject, abstractKotlinVariantWithGeneric) + } + + if (methodName in BuiltinMethodsWithSpecialGenericSignature.ERASED_COLLECTION_PARAMETER_NAMES) { + return emptyList() + } + + if (methodName == "remove" && method.parameterList.parameters.singleOrNull()?.type == PsiType.INT) { + // remove(int) -> abstract removeAt(int), final bridge remove(int) + return listOf(method.finalBridge(), createRemoveAt(method)) + } + + + val finalBridgeWithObject = method.finalBridge() + val abstractKotlinVariantWithGeneric = method.wrap(substituteObjectWith = singleTypeParameterAsType()) + return listOf(finalBridgeWithObject, abstractKotlinVariantWithGeneric) + } + + private fun singleTypeParameterAsType() = typeParameters.single().asType() + + private fun createRemoveAt(baseMethod: PsiMethod): PsiMethod { + return baseMethod.wrap( + name = "removeAt", + signature = MethodSignature( + parameterTypes = listOf(PsiType.INT), + returnType = singleTypeParameterAsType() + ) + ) + } + + private fun PsiMethod.finalBridge() = wrap(makeFinal = true, hasImplementation = true) + private fun PsiMethod.openBridge() = wrap(makeFinal = false, hasImplementation = true) + + private fun PsiMethod.wrap( + makeFinal: Boolean = false, + hasImplementation: Boolean = false, + name: String = this.name, + substituteObjectWith: PsiType? = null, + signature: MethodSignature? = null + ) = KtLightMethodWrapper( + this@KtLightMutabilityPlatformWrapper, this@wrap, + isFinal = makeFinal, + name = name, + substituteObjectWith = substituteObjectWith, + providedSignature = signature, + hasImplementation = hasImplementation + ) + + private fun javaUtilMapMethodWithSpecialSignature(method: PsiMethod): KtLightMethodWrapper? { + val k = typeParameters[0].asType() + val v = typeParameters[1].asType() + + val signature = when (method.name) { + "get" -> MethodSignature( + parameterTypes = listOf(k), + returnType = v + ) + "getOrDefault" -> MethodSignature( + parameterTypes = listOf(k, v), + returnType = v + ) + "containsKey" -> MethodSignature( + parameterTypes = listOf(k), + returnType = PsiType.BOOLEAN + ) + "containsValue" -> MethodSignature( + parameterTypes = listOf(v), + returnType = PsiType.BOOLEAN + ) + "remove" -> + when (method.parameterList.parametersCount) { + 1 -> MethodSignature( + parameterTypes = listOf(k), + returnType = v + ) + 2 -> MethodSignature( + parameterTypes = listOf(k, v), + returnType = PsiType.BOOLEAN + ) + else -> null + } + else -> null + } ?: return null + + return method.wrap(signature = signature) + } + + private fun PsiMethod.isInKotlinInterface(): Boolean { + if (javaBaseClass.qualifiedName == CommonClassNames.JAVA_UTIL_MAP_ENTRY) { + when (name) { + "getValue", "getKey" -> return true + } + } + + val kotlinInterface = DefaultBuiltIns.Instance.getBuiltInClassByFqName(kotlinInterfaceFqName) + val scope = kotlinInterface.unsubstitutedMemberScope + + val methodName = Name.identifier(name) + return scope.getContributedFunctions(methodName, NoLookupLocation.FROM_IDE).isNotEmpty() + || scope.getContributedVariables(methodName, NoLookupLocation.FROM_IDE).isNotEmpty() + } + + override fun getContainingFile() = javaBaseClass.containingFile +} + +private data class MethodSignature(val parameterTypes: List, val returnType: PsiType) + +private class KtLightMethodWrapper( + private val containingClass: KtAbstractContainerWrapper, + private val baseMethod: PsiMethod, + private val name: String, + private val isFinal: Boolean, + private val hasImplementation: Boolean, + private val substituteObjectWith: PsiType?, + private val providedSignature: MethodSignature? +) : PsiMethod, KtLightElementBase(containingClass) { + + init { + if (!hasImplementation && isFinal) { + error("Can't be final without an implementation") + } + } + + private fun substituteType(psiType: PsiType): PsiType { + val substituted = containingClass.substitutor.substitute(psiType) + if (TypeUtils.isJavaLangObject(substituted) && substituteObjectWith != null) { + return substituteObjectWith + } + else { + return substituted + } + } + + override fun getPresentation() = baseMethod.presentation + + override val kotlinOrigin get() = null + + override fun hasModifierProperty(name: String) = + when (name) { + PsiModifier.DEFAULT -> hasImplementation + PsiModifier.ABSTRACT -> !hasImplementation + PsiModifier.FINAL -> isFinal + else -> baseMethod.hasModifierProperty(name) + } + + override fun getParameterList(): PsiParameterList { + return LightParameterListBuilder(manager, KotlinLanguage.INSTANCE).apply { + baseMethod.parameterList.parameters.forEachIndexed { index, paramFromJava -> + val type = providedSignature?.parameterTypes?.get(index) ?: substituteType(paramFromJava.type) + addParameter( + LightParameter(paramFromJava.name ?: "p$index", type, + this@KtLightMethodWrapper, KotlinLanguage.INSTANCE, paramFromJava.isVarArgs) + ) + } + } + } + + override fun getName() = name + override fun getReturnType() = providedSignature?.returnType ?: baseMethod.returnType?.let { substituteType(it) } + + override fun getTypeParameters() = baseMethod.typeParameters + override fun getTypeParameterList() = baseMethod.typeParameterList + + override fun findSuperMethods(checkAccess: Boolean) = PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess) + override fun findSuperMethods(parentClass: PsiClass) = PsiSuperMethodImplUtil.findSuperMethods(this, parentClass) + override fun findSuperMethods() = PsiSuperMethodImplUtil.findSuperMethods(this) + override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean) = PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess) + @Suppress("OverridingDeprecatedMember") override fun findDeepestSuperMethod() = PsiSuperMethodImplUtil.findDeepestSuperMethod(this) + override fun findDeepestSuperMethods() = PsiSuperMethodImplUtil.findDeepestSuperMethods(this) + override fun getHierarchicalMethodSignature() = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this) + override fun getSignature(substitutor: PsiSubstitutor) = MethodSignatureBackedByPsiMethod.create(this, substitutor) + override fun getReturnTypeElement(): PsiTypeElement? = null + override fun getContainingClass() = containingClass + override fun getThrowsList() = baseMethod.throwsList + override fun hasTypeParameters() = baseMethod.hasTypeParameters() + override fun isVarArgs() = baseMethod.isVarArgs + override fun isConstructor() = false + private val identifier by lazyPub { LightIdentifier(manager, name) } + override fun getNameIdentifier() = identifier + override fun getDocComment() = baseMethod.docComment + override fun getModifierList() = baseMethod.modifierList + override fun getBody() = null + override fun isDeprecated() = baseMethod.isDeprecated + override fun setName(name: String) = cannotModify() + + override fun toString(): String { + return "$javaClass:$name${parameterList.parameters.map { it.type }.joinToString(prefix = "(", postfix = ")", separator = ", ")}" + } +} + + +abstract class KtAbstractContainerWrapper(internal val fqName: FqName, private val superInterface: PsiClass) + : LightElement(superInterface.manager, KotlinLanguage.INSTANCE), PsiExtensibleClass { + + private val memberCache = ClassInnerStuffCache(this) + + private val superClassTypeParametersToMyTypeParameters: Map + = superInterface.typeParameters + .mapIndexed { index, supersParameter -> + supersParameter to LightTypeParameterBuilder(supersParameter.name ?: "T$index", this, index) + } + .toMap() + + internal val substitutor = createSubstitutor(superClassTypeParametersToMyTypeParameters.mapValues { + it.value.asType() + }) + + override fun getSupers() = arrayOf(superInterface) + + override fun getQualifiedName() = fqName.asString() + + override fun toString() = "$javaClass:$name" + + override fun hasModifierProperty(name: String) = name == PsiModifier.PUBLIC || name == PsiModifier.ABSTRACT + + private val _typeParameterList by lazyPub { + LightTypeParameterListBuilder(manager, KotlinLanguage.INSTANCE).apply { + superClassTypeParametersToMyTypeParameters.values.forEach { addParameter(it) } + } + } + + override fun getTypeParameterList() = _typeParameterList + + private val identifier by lazyPub { LightIdentifier(manager, name) } + override fun getNameIdentifier() = identifier + + override fun getName() = fqName.shortName().asString() + + private val _implementsList by lazyPub { + LightReferenceListBuilder(manager, PsiReferenceList.Role.IMPLEMENTS_LIST).apply { + addReference(superInterface) + } + } + + override fun getImplementsList() = _implementsList + + override fun getSuperTypes() = arrayOf(PsiImmediateClassType(superInterface, substitutor)) + + override fun getMethods() = memberCache.methods + + override fun getTypeParameters() = superClassTypeParametersToMyTypeParameters.values.toTypedArray() + + override fun getInterfaces() = arrayOf(superInterface) + + override fun getInitializers() = PsiClassInitializer.EMPTY_ARRAY + + override fun getContainingClass() = null + + override fun getFields() = PsiField.EMPTY_ARRAY + + override fun isInterface() = true + override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean) = InheritanceImplUtil.isInheritor(this, baseClass, checkDeep) + override fun getOwnInnerClasses() = emptyList() + override fun getSuperClass() = null + override fun findInnerClassByName(name: String?, checkBases: Boolean) = null + override fun getExtendsListTypes() = PsiClassType.EMPTY_ARRAY + override fun isInheritorDeep(baseClass: PsiClass, classToByPass: PsiClass?) = InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass) + override fun isAnnotationType() = false + override fun findMethodsAndTheirSubstitutorsByName(name: String?, checkBases: Boolean) + = PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases) + + override fun getInnerClasses() = PsiClass.EMPTY_ARRAY + override fun findMethodBySignature(patternMethod: PsiMethod, checkBases: Boolean) + = PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases) + + override fun findFieldByName(name: String?, checkBases: Boolean) = null + override fun getAllFields() = PsiClassImplUtil.getAllFields(this) + override fun getAllInnerClasses() = PsiClassImplUtil.getAllInnerClasses(this) + override fun findMethodsByName(name: String?, checkBases: Boolean) = memberCache.findMethodsByName(name, checkBases) + override fun getAllMethods() = PsiClassImplUtil.getAllMethods(this) + override fun getOwnFields() = emptyList() + override fun getAllMethodsAndTheirSubstitutors() = + PsiClassImplUtil.getAllWithSubstitutorsByMap(this, PsiClassImplUtil.MemberType.METHOD) + + override fun hasTypeParameters() = true + override fun getRBrace() = null + override fun getLBrace() = null + override fun getVisibleSignatures() = PsiSuperMethodImplUtil.getVisibleSignatures(this) + override fun getExtendsList() = null + override fun getDocComment() = null + override fun isEnum() = false + private val _modifierList by lazyPub { LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC) } + override fun getModifierList() = _modifierList + override fun getScope() = superInterface.scope + override fun getImplementsListTypes() = superTypes + override fun getConstructors() = PsiMethod.EMPTY_ARRAY + override fun isDeprecated() = false + override fun setName(name: String) = cannotModify() + override fun findMethodsBySignature(patternMethod: PsiMethod, checkBases: Boolean) + = PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases) +} + +private fun PsiTypeParameter.asType() = PsiImmediateClassType(this, PsiSubstitutor.EMPTY) \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.java b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.java new file mode 100644 index 00000000000..b025c7c7c8a --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.java @@ -0,0 +1,186 @@ +public class EtendingMutableInterfaces { + public static class Lists { + public static class ExtendIList1 implements IMutableList { + + } + + public static class ExtendIList2 implements IMutableList { + + } + + // Compiler bug causes remove(int) to clash https://youtrack.jetbrains.com/issue/KT-17782 + //public static class ExtendCList1 extends CMutableList { + // + //} + // + //public static class ExtendCList2 extends CMutableList { + // + //} + + public static class ExtendSList extends SMutableList { + + } + + public static class ExtendAList extends AMutableList { + + } + } + + public static class Collections { + public static class ExtendICollection1 implements IMutableCollection { + + } + + public static class ExtendICollection2 implements IMutableCollection { + + } + + public static class ExtendCCollection1 extends CMutableCollection { + + } + + public static class ExtendCCollection2 extends CMutableCollection { + + } + + public static class ExtendSCollection extends SMutableCollection { + + } + + public static class ExtendACollection extends AMutableCollection { + + } + } + + public static class Sets { + public static class ExtendISet1 implements IMutableSet { + + } + + public static class ExtendISet2 implements IMutableSet { + + } + + public static class ExtendCSet1 extends CMutableSet { + + } + + public static class ExtendCSet2 extends CMutableSet { + + } + + public static class ExtendSSet extends SMutableSet { + + } + + public static class ExtendASet extends AMutableSet { + + } + } + + public static class Iterables { + public static class ExtendIIterable1 implements IMutableIterable { + + } + + public static class ExtendIIterable2 implements IMutableIterable { + + } + + public static class ExtendCIterable1 extends CMutableIterable { + + } + + public static class ExtendCIterable2 extends CMutableIterable { + + } + + public static class ExtendSIterable extends SMutableIterable { + + } + + public static class ExtendAIterable extends AMutableIterable { + + } + } + + public static class Iterators { + public static class ExtendIIterator1 implements IMutableIterator { + + } + + public static class ExtendIIterator2 implements IMutableIterator { + + } + + public static class ExtendCIterator1 extends CMutableIterator { + + } + + public static class ExtendCIterator2 extends CMutableIterator { + + } + + public static class ExtendSIterator extends SMutableIterator { + + } + + public static class ExtendAIterator extends AMutableIterator { + + } + } + + public static class Maps { + public static class ExtendIMap1 implements IMutableMap { + + } + + public static class ExtendIMap2 implements IMutableMap { + + } + + public static class ExtendCMap1 extends CMutableMap { + + } + + public static class ExtendCMap2 extends CMutableMap { + + } + + // NOTE: looks like a bug in compiler see KT-17738 + + //public static class ExtendSMap extends SMutableMap { + // + //} + // + //public static class ExtendABMap extends ABMutableMap { + // + //} + } + + public static class MapEntrys { + public static class ExtendIMapEntry1 implements IMutableMapEntry { + + } + + public static class ExtendIMapEntry2 implements IMutableMapEntry { + + } + + public static class ExtendCMapEntry1 extends CMutableMapEntry { + + } + + public static class ExtendCMapEntry2 extends CMutableMapEntry { + + } + + public static class ExtendSMapEntry extends SMutableMapEntry { + + } + + public static class ExtendAMapEntry extends ABMutableMapEntry { + + } + } +} \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.kt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.kt new file mode 100644 index 00000000000..cae7ba0413f --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.kt @@ -0,0 +1,44 @@ +class A +class B + +// kotlin.collection.MutableCollection +interface IMutableCollection : MutableCollection +abstract class CMutableCollection : MutableCollection by mutableListOf() +abstract class SMutableCollection : MutableCollection by mutableListOf() +abstract class AMutableCollection : MutableCollection by mutableListOf() + +// kotlin.collections.MutableIterable +interface IMutableIterable : MutableIterable +abstract class CMutableIterable : MutableIterable by mutableListOf() +abstract class SMutableIterable : MutableIterable by mutableListOf() +abstract class AMutableIterable : MutableIterable by mutableListOf() + +// kotlin.collections.MutableList +interface IMutableList : MutableList +abstract class CMutableList : MutableList by mutableListOf() +abstract class SMutableList : MutableList by mutableListOf() +abstract class AMutableList : MutableList by mutableListOf() + +// kotlin.collections.Set +interface IMutableSet : MutableSet +abstract class CMutableSet : MutableSet by mutableSetOf() +abstract class SMutableSet : MutableSet by mutableSetOf() +abstract class AMutableSet : MutableSet by mutableSetOf() + +// kotlin.collections.Iterator +interface IMutableIterator : MutableIterator +abstract class CMutableIterator : MutableIterator by mutableListOf().iterator() +abstract class SMutableIterator : MutableIterator by mutableListOf().iterator() +abstract class AMutableIterator : MutableIterator by mutableListOf().iterator() + +// kotlin.collections.Map +interface IMutableMap : MutableMap +abstract class CMutableMap : MutableMap by mutableMapOf() +abstract class SMutableMap : MutableMap by mutableMapOf() +abstract class ABMutableMap : MutableMap by mutableMapOf() + +// kotlin.collections.Map.Entry +interface IMutableMapEntry : MutableMap.MutableEntry +abstract class CMutableMapEntry : MutableMap.MutableEntry by mutableMapOf().entries.first() +abstract class SMutableMapEntry : MutableMap.MutableEntry by mutableMapOf().entries.first() +abstract class ABMutableMapEntry : MutableMap.MutableEntry by mutableMapOf().entries.first() diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.java b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.java new file mode 100644 index 00000000000..0c3cf9a40f5 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.java @@ -0,0 +1,185 @@ +public class EtendingReadOnlyInterfaces { + public static class Lists { + public static class ExtendIList1 implements IList { + + } + + public static class ExtendIList2 implements IList { + + } + + public static class ExtendCList1 extends CList { + + } + + public static class ExtendCList2 extends CList { + + } + + public static class ExtendSList extends SList { + + } + + public static class ExtendAList extends AList { + + } + } + + public static class Collections { + public static class ExtendICollection1 implements ICollection { + + } + + public static class ExtendICollection2 implements ICollection { + + } + + public static class ExtendCCollection1 extends CCollection { + + } + + public static class ExtendCCollection2 extends CCollection { + + } + + public static class ExtendSCollection extends SCollection { + + } + + public static class ExtendACollection extends ACollection { + + } + } + + public static class Sets { + public static class ExtendISet1 implements ISet { + + } + + public static class ExtendISet2 implements ISet { + + } + + public static class ExtendCSet1 extends CSet { + + } + + public static class ExtendCSet2 extends CSet { + + } + + public static class ExtendSSet extends SSet { + + } + + public static class ExtendASet extends ASet { + + } + } + + public static class Iterables { + public static class ExtendIIterable1 implements IIterable { + + } + + public static class ExtendIIterable2 implements IIterable { + + } + + public static class ExtendCIterable1 extends CIterable { + + } + + public static class ExtendCIterable2 extends CIterable { + + } + + public static class ExtendSIterable extends SIterable { + + } + + public static class ExtendAIterable extends AIterable { + + } + } + + public static class Iterators { + public static class ExtendIIterator1 implements IIterator { + + } + + public static class ExtendIIterator2 implements IIterator { + + } + + public static class ExtendCIterator1 extends CIterator { + + } + + public static class ExtendCIterator2 extends CIterator { + + } + + public static class ExtendSIterator extends SIterator { + + } + + public static class ExtendAIterator extends AIterator { + + } + } + + public static class Maps { + public static class ExtendIMap1 implements IMap { + + } + + public static class ExtendIMap2 implements IMap { + + } + + public static class ExtendCMap1 extends CMap { + + } + + public static class ExtendCMap2 extends CMap { + + } + + // NOTE: looks like a bug in compiler see KT-17738 + + //public static class ExtendSMap extends SMap { + // + //} + // + //public static class ExtendABMap extends ABMap { + // + //} + } + + public static class MapEntrys { + public static class ExtendIMapEntry1 implements IMapEntry { + + } + + public static class ExtendIMapEntry2 implements IMapEntry { + + } + + public static class ExtendCMapEntry1 extends CMapEntry { + + } + + public static class ExtendCMapEntry2 extends CMapEntry { + + } + + public static class ExtendSMapEntry extends SMapEntry { + + } + + public static class ExtendAMapEntry extends ABMapEntry { + + } + } +} \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.kt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.kt new file mode 100644 index 00000000000..ea5d4447085 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.kt @@ -0,0 +1,44 @@ +class A +class B + +// kotlin.collections.Collection +interface ICollection : Collection +abstract class CCollection : Collection by emptyList() +abstract class SCollection : Collection by emptyList() +abstract class ACollection : Collection by emptyList() + +// kotlin.collections.Iterable +interface IIterable : Iterable +abstract class CIterable : Iterable by emptyList() +abstract class SIterable : Iterable by emptyList() +abstract class AIterable : Iterable by emptyList() + +// kotlin.collections.List +interface IList : List +abstract class CList : List by emptyList() +abstract class SList : List by emptyList() +abstract class AList : List by emptyList() + +// kotlin.collections.Set +interface ISet : Set +abstract class CSet : Set by emptySet() +abstract class SSet : Set by emptySet() +abstract class ASet : Set by emptySet() + +// kotlin.collections.Iterator +interface IIterator : Iterator +abstract class CIterator : Iterator by emptyList().iterator() +abstract class SIterator : Iterator by emptyList().iterator() +abstract class AIterator : Iterator by emptyList().iterator() + +// kotlin.collections.Map +interface IMap : Map +abstract class CMap : Map by emptyMap() +abstract class SMap : Map by emptyMap() +abstract class ABMap : Map by emptyMap() + +// kotlin.collections.Map.Entry +interface IMapEntry : Map.Entry +abstract class CMapEntry : Map.Entry by emptyMap().entries.first() +abstract class SMapEntry : Map.Entry by emptyMap().entries.first() +abstract class ABMapEntry : Map.Entry by emptyMap().entries.first() diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.java b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.java new file mode 100644 index 00000000000..dcff9514997 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.java @@ -0,0 +1,16 @@ +public class UsingMutableInterfaces { + public static class Lists { + public static void useCMutableList(CMutableList cMutableList, E elem, java.util.Collection other) { + java.util.Iterator iter = cMutableList.iterator(); + cMutableList.addAll(other); + cMutableList.add(elem); + cMutableList.isEmpty(); + cMutableList.clear(); + cMutableList.getSize(); + cMutableList.size(); + boolean b = cMutableList.remove(elem); + E e1 = cMutableList.remove(3); + E e2 = cMutableList.removeAt(6); + } + } +} \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.kt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.kt new file mode 100644 index 00000000000..7297d554eaa --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.kt @@ -0,0 +1,2 @@ +// kotlin.collections.List +abstract class CMutableList : MutableList by mutableListOf() diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.java b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.java new file mode 100644 index 00000000000..f766ce471f3 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.java @@ -0,0 +1,44 @@ +public class UsingReadOnlyInterfaces { + public static class Collections { + public static void useICollection(ICollection iCollection, E elem, java.util.Collection other) { + java.util.Iterator iter = iCollection.iterator(); + iCollection.addAll(other); + iCollection.add(elem); + iCollection.isEmpty(); + iCollection.clear(); + iCollection.getSize(); // this is not an error when analyzing against kotlin sources (which is a bug), this inconsistency is hard to fix with the current approach + iCollection.size(); + } + + public static void useCCollection(CCollection cCollection, E elem, java.util.Collection other) { + java.util.Iterator iter = cCollection.iterator(); + cCollection.addAll(other); + cCollection.add(elem); + cCollection.isEmpty(); + cCollection.clear(); + cCollection.getSize(); + cCollection.size(); + cCollection.contains(elem); + cCollection.contains("sasd"); + cCollection.removeAll(other); + cCollection.retainAll(other); + } + + public static class ExtendCCollection extends CCollection { + @Override + public void clear() {} + } + } + + public static class Maps { + public static void useCMap(CMap cMap) { + cMap.isEmpty(); + java.util.Set s = cMap.getKeys(); + s = cMap.keySet(); + java.util.Collection v = cMap.getValues(); + v = cMap.values(); + java.util.Set> e = cMap.entrySet(); + e = cMap.getEntries(); + } + } +} \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.kt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.kt new file mode 100644 index 00000000000..ea5d4447085 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.kt @@ -0,0 +1,44 @@ +class A +class B + +// kotlin.collections.Collection +interface ICollection : Collection +abstract class CCollection : Collection by emptyList() +abstract class SCollection : Collection by emptyList() +abstract class ACollection : Collection by emptyList() + +// kotlin.collections.Iterable +interface IIterable : Iterable +abstract class CIterable : Iterable by emptyList() +abstract class SIterable : Iterable by emptyList() +abstract class AIterable : Iterable by emptyList() + +// kotlin.collections.List +interface IList : List +abstract class CList : List by emptyList() +abstract class SList : List by emptyList() +abstract class AList : List by emptyList() + +// kotlin.collections.Set +interface ISet : Set +abstract class CSet : Set by emptySet() +abstract class SSet : Set by emptySet() +abstract class ASet : Set by emptySet() + +// kotlin.collections.Iterator +interface IIterator : Iterator +abstract class CIterator : Iterator by emptyList().iterator() +abstract class SIterator : Iterator by emptyList().iterator() +abstract class AIterator : Iterator by emptyList().iterator() + +// kotlin.collections.Map +interface IMap : Map +abstract class CMap : Map by emptyMap() +abstract class SMap : Map by emptyMap() +abstract class ABMap : Map by emptyMap() + +// kotlin.collections.Map.Entry +interface IMapEntry : Map.Entry +abstract class CMapEntry : Map.Entry by emptyMap().entries.first() +abstract class SMapEntry : Map.Entry by emptyMap().entries.first() +abstract class ABMapEntry : Map.Entry by emptyMap().entries.first() diff --git a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java index ee2e1db92a5..19cc043cb4a 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java @@ -90,6 +90,18 @@ public class JavaAgainstKotlinBinariesCheckerTestGenerated extends AbstractJavaA doTest(fileName); } + @TestMetadata("ExtendingMutableInterfaces.kt") + public void testExtendingMutableInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.kt"); + doTest(fileName); + } + + @TestMetadata("ExtendingReadOnlyInterfaces.kt") + public void testExtendingReadOnlyInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.kt"); + doTest(fileName); + } + @TestMetadata("FunctionInNestedClassInDataFlowInspection.kt") public void testFunctionInNestedClassInDataFlowInspection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/FunctionInNestedClassInDataFlowInspection.kt"); @@ -179,4 +191,16 @@ public class JavaAgainstKotlinBinariesCheckerTestGenerated extends AbstractJavaA String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingKotlinPackageDeclarations.kt"); doTest(fileName); } + + @TestMetadata("UsingMutableInterfaces.kt") + public void testUsingMutableInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.kt"); + doTest(fileName); + } + + @TestMetadata("UsingReadOnlyInterfaces.kt") + public void testUsingReadOnlyInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.kt"); + doTest(fileName); + } } diff --git a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java index c4784fba6d3..9f0e6633f86 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java @@ -92,6 +92,18 @@ public class JavaAgainstKotlinSourceCheckerTestGenerated extends AbstractJavaAga doTest(fileName); } + @TestMetadata("ExtendingMutableInterfaces.kt") + public void testExtendingMutableInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingMutableInterfaces.kt"); + doTest(fileName); + } + + @TestMetadata("ExtendingReadOnlyInterfaces.kt") + public void testExtendingReadOnlyInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendingReadOnlyInterfaces.kt"); + doTest(fileName); + } + @TestMetadata("FunctionInNestedClassInDataFlowInspection.kt") public void testFunctionInNestedClassInDataFlowInspection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/FunctionInNestedClassInDataFlowInspection.kt"); @@ -181,6 +193,18 @@ public class JavaAgainstKotlinSourceCheckerTestGenerated extends AbstractJavaAga String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingKotlinPackageDeclarations.kt"); doTest(fileName); } + + @TestMetadata("UsingMutableInterfaces.kt") + public void testUsingMutableInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingMutableInterfaces.kt"); + doTest(fileName); + } + + @TestMetadata("UsingReadOnlyInterfaces.kt") + public void testUsingReadOnlyInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/UsingReadOnlyInterfaces.kt"); + doTest(fileName); + } } @TestMetadata("idea/testData/kotlinAndJavaChecker/javaWithKotlin") diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java index 4423274fac4..5ce4380f8dd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java @@ -236,6 +236,18 @@ public class IdeLightClassTestGenerated extends AbstractIdeLightClassTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/asJava/lightClasses/ideRegression"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("ImplementingMap.kt") + public void testImplementingMap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.kt"); + doTest(fileName); + } + + @TestMetadata("ImplementingMutableSet.kt") + public void testImplementingMutableSet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/ideRegression/ImplementingMutableSet.kt"); + doTest(fileName); + } + @TestMetadata("InheritingInterfaceDefaultImpls.kt") public void testInheritingInterfaceDefaultImpls() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/ideRegression/InheritingInterfaceDefaultImpls.kt");