[FIR] Support @kotlin.jvm.PurelyImplements annotation

This commit is contained in:
Dmitriy Novozhilov
2021-11-19 18:06:57 +03:00
parent 339c69fef0
commit 01c0cf80d0
5 changed files with 90 additions and 45 deletions
@@ -1,2 +1,2 @@
Resolved to:
0: (in java.util) open class HashSet<E : kotlin.Any!> : java.util.AbstractSet<E!>, kotlin.collections.MutableSet<E!>, kotlin.Cloneable, java.io.Serializable
0: (in java.util) open class HashSet<E : kotlin.Any!> : java.util.AbstractSet<E!>, kotlin.Cloneable, java.io.Serializable, kotlin.collections.MutableSet<E>
@@ -1,2 +1,2 @@
Resolved to:
0: (in java.util) open class HashMap<K : kotlin.Any!, V : kotlin.Any!> : java.util.AbstractMap<K!, V!>, kotlin.collections.MutableMap<K!, V!>, kotlin.Cloneable, java.io.Serializable
0: (in java.util) open class HashMap<K : kotlin.Any!, V : kotlin.Any!> : java.util.AbstractMap<K!, V!>, kotlin.Cloneable, java.io.Serializable, kotlin.collections.MutableMap<K, V>
@@ -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,
@@ -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<ClassId, ClassId>()
private infix fun ClassId.implementedWith(implementations: List<ClassId>) {
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<ClassId> = names.map { ClassId.topLevel(FqName(it)) }
}
@@ -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<FqName, FqName>()
private infix fun FqName.implementedWith(implementations: List<FqName>) {
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<FqName> = names.map(::FqName)
}