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");