diff --git a/analysis/analysis-api/testData/referenceResolve/JavaReference.txt b/analysis/analysis-api/testData/referenceResolve/JavaReference.txt index afe3b0fe894..9122e4a96d9 100644 --- a/analysis/analysis-api/testData/referenceResolve/JavaReference.txt +++ b/analysis/analysis-api/testData/referenceResolve/JavaReference.txt @@ -1,2 +1,2 @@ Resolved to: -0: (in java.util) open class HashSet : java.util.AbstractSet, kotlin.collections.MutableSet, kotlin.Cloneable, java.io.Serializable +0: (in java.util) open class HashSet : java.util.AbstractSet, kotlin.Cloneable, java.io.Serializable, kotlin.collections.MutableSet diff --git a/analysis/analysis-api/testData/referenceResolve/inImport/javaClass.txt b/analysis/analysis-api/testData/referenceResolve/inImport/javaClass.txt index fe6b8a51b61..7c848a3bd54 100644 --- a/analysis/analysis-api/testData/referenceResolve/inImport/javaClass.txt +++ b/analysis/analysis-api/testData/referenceResolve/inImport/javaClass.txt @@ -1,2 +1,2 @@ Resolved to: -0: (in java.util) open class HashMap : java.util.AbstractMap, kotlin.collections.MutableMap, kotlin.Cloneable, java.io.Serializable +0: (in java.util) open class HashMap : java.util.AbstractMap, kotlin.Cloneable, java.io.Serializable, kotlin.collections.MutableMap diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt index ca0363c6e82..3c70e68e3b7 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.analysis.checkers.typeParameterSymbols import org.jetbrains.kotlin.fir.caches.createCache import org.jetbrains.kotlin.fir.caches.firCachesFactory import org.jetbrains.kotlin.fir.caches.getValue @@ -25,15 +26,21 @@ import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl import org.jetbrains.kotlin.fir.declarations.utils.effectiveVisibility import org.jetbrains.kotlin.fir.declarations.utils.modality +import org.jetbrains.kotlin.fir.expressions.FirConstExpression import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.classId import org.jetbrains.kotlin.fir.java.declarations.* import org.jetbrains.kotlin.fir.java.enhancement.FirSignatureEnhancement import org.jetbrains.kotlin.fir.resolve.constructType import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider +import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl +import org.jetbrains.kotlin.load.java.FakePureImplementationsProvider import org.jetbrains.kotlin.load.java.JavaClassFinder import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.structure.* @@ -148,11 +155,54 @@ class FirJavaFacade( firJavaClass.annotations.addFromJava(session, javaClass, javaTypeParameterStack) val enhancement = FirSignatureEnhancement(firJavaClass, session) { emptyList() } enhancement.enhanceTypeParameterBounds(firJavaClass.typeParameters) - firJavaClass.superTypeRefs.replaceAll { enhancement.enhanceSuperType(it) } + val enhancedSuperTypes = buildList { + val purelyImplementedSupertype = firJavaClass.getPurelyImplementedSupertype() + val purelyImplementedSupertypeClassId = purelyImplementedSupertype?.classId + firJavaClass.superTypeRefs.mapNotNullTo(this) { superType -> + enhancement.enhanceSuperType(superType).takeUnless { + purelyImplementedSupertypeClassId != null && it.coneType.classId == purelyImplementedSupertypeClassId + } + } + purelyImplementedSupertype?.let { + add(buildResolvedTypeRef { type = it }) + } + } + firJavaClass.replaceSuperTypeRefs(enhancedSuperTypes) firJavaClass.replaceDeprecation(firJavaClass.getDeprecationInfos(session.languageVersionSettings.apiVersion)) return firJavaClass } + private fun FirJavaClass.getPurelyImplementedSupertype(): ConeKotlinType? { + val purelyImplementedClassIdFromAnnotation = annotations + .firstOrNull { it.classId?.asSingleFqName() == JvmAnnotationNames.PURELY_IMPLEMENTS_ANNOTATION } + ?.let { (it.argumentMapping.mapping.values.firstOrNull() as? FirConstExpression<*>) } + ?.let { it.value as? String } + ?.takeIf { it.isNotBlank() && isValidJavaFqName(it) } + ?.let { ClassId.topLevel(FqName(it)) } + val purelyImplementedClassId = purelyImplementedClassIdFromAnnotation + ?: FakePureImplementationsProvider.getPurelyImplementedInterface(symbol.classId) + ?: return null + val superTypeSymbol = session.symbolProvider.getClassLikeSymbolByClassId(purelyImplementedClassId) ?: return null + val superTypeParameterSymbols = superTypeSymbol.typeParameterSymbols ?: return null + val typeParameters = this.typeParameters + val supertypeParameterCount = superTypeParameterSymbols.size + val typeParameterCount = typeParameters.size + val parametersAsTypeProjections = when { + typeParameterCount == supertypeParameterCount -> + typeParameters.map { ConeTypeParameterTypeImpl(ConeTypeParameterLookupTag(it.symbol), isNullable = false) } + typeParameterCount == 1 && supertypeParameterCount > 1 && purelyImplementedClassIdFromAnnotation == null -> { + val projection = ConeTypeParameterTypeImpl(ConeTypeParameterLookupTag(typeParameters.first().symbol), isNullable = false) + (1..supertypeParameterCount).map { projection } + } + else -> return null + } + return ConeClassLikeTypeImpl( + ConeClassLikeLookupTagImpl(purelyImplementedClassId), + parametersAsTypeProjections.toTypedArray(), + isNullable = false + ) + } + private fun createFirJavaClass( javaClass: JavaClass, classSymbol: FirRegularClassSymbol, diff --git a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/FakePureImplementationsProvider.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/FakePureImplementationsProvider.kt new file mode 100644 index 00000000000..13cc5a902d8 --- /dev/null +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/FakePureImplementationsProvider.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.load.java + +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.StandardClassIds + +object FakePureImplementationsProvider { + fun getPurelyImplementedInterface(classFqName: ClassId): ClassId? = pureImplementationsClassIds[classFqName] + fun getPurelyImplementedInterface(classFqName: FqName): FqName? = pureImplementationsFqNames[classFqName] + + private val pureImplementationsClassIds = mutableMapOf() + private infix fun ClassId.implementedWith(implementations: List) { + implementations.associateWithTo(pureImplementationsClassIds) { this } + } + + init { + StandardClassIds.MutableList implementedWith fqNameListOf("java.util.ArrayList", "java.util.LinkedList") + StandardClassIds.MutableSet implementedWith fqNameListOf("java.util.HashSet", "java.util.TreeSet", "java.util.LinkedHashSet") + StandardClassIds.MutableMap implementedWith fqNameListOf( + "java.util.HashMap", "java.util.TreeMap", "java.util.LinkedHashMap", + "java.util.concurrent.ConcurrentHashMap", "java.util.concurrent.ConcurrentSkipListMap" + ) + ClassId.topLevel(FqName("java.util.function.Function")) implementedWith fqNameListOf("java.util.function.UnaryOperator") + ClassId.topLevel(FqName("java.util.function.BiFunction")) implementedWith fqNameListOf("java.util.function.BinaryOperator") + } + + private val pureImplementationsFqNames = pureImplementationsClassIds.map { (key, value) -> + key.asSingleFqName() to value.asSingleFqName() + }.toMap() + + private fun fqNameListOf(vararg names: String): List = names.map { ClassId.topLevel(FqName(it)) } +} diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/FakePureImplementationsProvider.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/FakePureImplementationsProvider.kt deleted file mode 100644 index 5e71bed6347..00000000000 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/FakePureImplementationsProvider.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2010-2015 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.load.java - -import org.jetbrains.kotlin.builtins.StandardNames.FqNames -import org.jetbrains.kotlin.name.FqName - -object FakePureImplementationsProvider { - fun getPurelyImplementedInterface(classFqName: FqName): FqName? = pureImplementations[classFqName] - - private val pureImplementations = hashMapOf() - private infix fun FqName.implementedWith(implementations: List) { - implementations.associateWithTo(pureImplementations) { this } - } - - init { - FqNames.mutableList implementedWith fqNameListOf("java.util.ArrayList", "java.util.LinkedList") - FqNames.mutableSet implementedWith fqNameListOf("java.util.HashSet", "java.util.TreeSet", "java.util.LinkedHashSet") - FqNames.mutableMap implementedWith fqNameListOf( - "java.util.HashMap", "java.util.TreeMap", "java.util.LinkedHashMap", - "java.util.concurrent.ConcurrentHashMap", "java.util.concurrent.ConcurrentSkipListMap" - ) - FqName("java.util.function.Function") implementedWith fqNameListOf("java.util.function.UnaryOperator") - FqName("java.util.function.BiFunction") implementedWith fqNameListOf("java.util.function.BinaryOperator") - } - - private fun fqNameListOf(vararg names: String): List = names.map(::FqName) -}