FIR IDE: use correct denotable type approximator

This commit is contained in:
Ilya Kirillov
2021-02-19 13:09:01 +01:00
parent af4d300686
commit 42103b7363
5 changed files with 26 additions and 122 deletions
@@ -8,12 +8,10 @@ package org.jetbrains.kotlin.idea.fir.intentions.declarations
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.fir.api.*
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicabilityRange
import org.jetbrains.kotlin.idea.fir.api.applicator.applicabilityTarget
import org.jetbrains.kotlin.idea.fir.api.applicator.inputProvider
import org.jetbrains.kotlin.idea.fir.api.applicator.with
import org.jetbrains.kotlin.idea.fir.applicators.ApplicabilityRanges
import org.jetbrains.kotlin.idea.fir.applicators.CallableReturnTypeUpdaterApplicator
import org.jetbrains.kotlin.idea.frontend.api.types.*
import org.jetbrains.kotlin.psi.*
class HLSpecifyExplicitTypeForCallableDeclarationIntention :
@@ -31,7 +29,7 @@ class HLSpecifyExplicitTypeForCallableDeclarationIntention :
override val inputProvider = inputProvider<KtCallableDeclaration, CallableReturnTypeUpdaterApplicator.Type> { declaration ->
val returnType = declaration.getReturnKtType()
val denotableType = returnType.approximateToPublicDenotable() ?: return@inputProvider null
val denotableType = returnType.approximateToSuperPublicDenotable() ?: returnType
with(CallableReturnTypeUpdaterApplicator.Type) { createByKtType(denotableType) }
}
}
@@ -105,7 +105,13 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
val builtinTypes: KtBuiltinTypes get() = typeProvider.builtinTypes
fun KtType.approximateToPublicDenotable(): KtType? = typeProvider.approximateToPublicDenotable(this)
/**
* Approximates [KtType] with the a supertype which can be rendered in a source code
*
* Return `null` if the type do not need approximation and can be rendered as is
* Otherwise, for type `T` return type `S` such `T <: S` and `T` and every it type argument is [org.jetbrains.kotlin.idea.frontend.api.types.KtDenotableType]`
*/
fun KtType.approximateToSuperPublicDenotable(): KtType? = typeProvider.approximateToSuperPublicDenotableType(this)
fun KtClassOrObjectSymbol.buildSelfClassType(): KtType = typeProvider.buildSelfClassType(this)
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.idea.frontend.api.types.KtType
abstract class KtTypeProvider : KtAnalysisSessionComponent() {
abstract val builtinTypes: KtBuiltinTypes
abstract fun approximateToPublicDenotable(type: KtType): KtType?
abstract fun approximateToSuperPublicDenotableType(type: KtType): KtType?
abstract fun buildSelfClassType(symbol: KtClassOrObjectSymbol): KtType
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.components
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
@@ -18,6 +19,7 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType
import org.jetbrains.kotlin.idea.frontend.api.fir.types.PublicTypeApproximator
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
internal class KtFirTypeProvider(
override val analysisSession: KtFirAnalysisSession,
@@ -26,14 +28,14 @@ internal class KtFirTypeProvider(
override val builtinTypes: KtBuiltinTypes =
KtFirBuiltInTypes(analysisSession.firResolveState.rootModuleSession.builtinTypes, analysisSession.firSymbolBuilder, token)
override fun approximateToPublicDenotable(type: KtType): KtType? {
override fun approximateToSuperPublicDenotableType(type: KtType): KtType? {
require(type is KtFirType)
val coneType = type.coneType
val approximatedConeType = PublicTypeApproximator.approximateTypeToPublicDenotable(
coneType,
rootModuleSession.inferenceComponents.ctx,
rootModuleSession
)
return approximatedConeType?.asKtType()
}
@@ -50,3 +52,4 @@ internal class KtFirTypeProvider(
return type.asKtType()
}
}
@@ -6,128 +6,25 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.types
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.calls.fullyExpandedClass
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
internal object PublicTypeApproximator {
fun approximateTypeToPublicDenotable(
type: ConeKotlinType,
context: ConeInferenceContext,
session: FirSession,
): ConeKotlinType? = approximate(type, context, session) as ConeKotlinType?
private fun approximate(
type: ConeKotlinType,
context: ConeInferenceContext,
session: FirSession,
): ConeTypeProjection? = when (type) {
is ConeFlexibleType -> approximate(type.upperBound, context, session)
is ConeCapturedType -> type
is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(
approximate(
type.original,
context,
session,
) as ConeKotlinType
)
is ConeIntegerLiteralType -> type
is ConeIntersectionType -> context.commonSuperTypeOrNull(type.intersectedTypes.toList())?.let { approximate(it, context, session) }
is ConeLookupTagBasedType -> when (type) {
is ConeTypeParameterType -> type
is ConeClassErrorType -> null
is ConeClassLikeTypeImpl -> approximateClassLikeType(type, context, session)
else -> error("Unexpected type ${type::class}")
}
is ConeStubType -> null
): ConeKotlinType? {
val approximator = session.inferenceComponents.approximator
return approximator.approximateToSuperType(type, PublicApproximatorConfiguration) as ConeKotlinType?
}
private fun approximateClassLikeType(
typeImpl: ConeClassLikeTypeImpl,
context: ConeInferenceContext,
session: FirSession,
): ConeTypeProjection? {
val regularClass = typeImpl.classOrAnonymousClass(session) ?: return null
val type = if (needApproximationAsSuperType(regularClass)) {
firstSuperType(regularClass, session) ?: return null
} else {
typeImpl
}
val typeClassifier = type.classOrAnonymousClass(session) ?: return null
val typeArguments = approximateTypeParameters(type, typeClassifier, context, session)?.toTypedArray() ?: return null
return ConeClassLikeTypeImpl(
type.lookupTag,
typeArguments,
type.isNullable,
type.attributes
)
}
private fun approximateTypeParameters(
type: ConeClassLikeTypeImpl,
typeClassifier: FirClass<*>,
context: ConeInferenceContext,
session: FirSession
): List<ConeTypeProjection>? {
val result = mutableListOf<ConeTypeProjection>()
if (type.typeArguments.size != typeClassifier.typeParameters.size) return null
for ((typeArg, typeParam) in type.typeArguments.zip(typeClassifier.typeParameters)) {
val variance = (typeParam as? FirTypeParameter)?.variance ?: return null
result += approximateTypeProjection(typeArg, context, session, variance) ?: return null
}
return result
}
private fun firstSuperType(regularClass: FirClass<*>, session: FirSession): ConeClassLikeTypeImpl? {
val superTypes = lookupSuperTypes(regularClass, lookupInterfaces = true, deep = false, session, substituteTypes = true)
return superTypes.first() as? ConeClassLikeTypeImpl
}
fun ConeClassLikeTypeImpl.classOrAnonymousClass(session: FirSession): FirClass<*>? {
val fir = lookupTag.toSymbol(session)?.fir ?: return null
if (fir is FirAnonymousObject) return fir
else return fir.fullyExpandedClass(session)
}
private fun needApproximationAsSuperType(fir: FirClass<*>) = when (fir) {
is FirRegularClass -> fir.isLocal
is FirAnonymousObject -> true
else -> false
}
private fun approximateTypeProjection(
typeProjection: ConeTypeProjection,
context: ConeInferenceContext,
session: FirSession,
variance: Variance,
): ConeTypeProjection? {
val type = when (typeProjection) {
ConeStarProjection -> return ConeStarProjection
is ConeKotlinTypeProjection -> typeProjection.type
else -> error("Unexpected type ${typeProjection::class}")
}
val newType = when (val new = approximate(type, context, session)) {
ConeStarProjection -> return ConeStarProjection
is ConeKotlinTypeProjection -> new.type
null -> return null
else -> error("Unexpected type ${new::class}")
}
return when (variance) {
Variance.INVARIANT -> when {
with(context) { newType.typeConstructor().isAnyConstructor() } -> ConeStarProjection
AbstractTypeChecker.equalTypes(context, type, newType) -> newType
else -> ConeStarProjection
}
Variance.IN_VARIANCE -> if (AbstractTypeChecker.isSubtypeOf(context, newType, type)) newType else ConeStarProjection
Variance.OUT_VARIANCE -> if (AbstractTypeChecker.isSubtypeOf(context, type, newType)) newType else ConeStarProjection
}
private object PublicApproximatorConfiguration : TypeApproximatorConfiguration.AllFlexibleSameValue() {
override val allFlexible: Boolean get() = false
override val errorType: Boolean get() = true
override val definitelyNotNullType: Boolean get() = false
override val integerLiteralType: Boolean get() = true
override val intersectionTypesInContravariantPositions: Boolean get() = true
override val localTypes: Boolean get() = true
}
}