FE: extract FIR-reusable code from SignatureParts
This commit is contained in:
+230
@@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* 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.typeEnhancement
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||||
|
import org.jetbrains.kotlin.load.java.AbstractAnnotationTypeQualifierResolver
|
||||||
|
import org.jetbrains.kotlin.load.java.AnnotationQualifierApplicabilityType
|
||||||
|
import org.jetbrains.kotlin.load.java.JavaTypeQualifiersByElementType
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
|
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||||
|
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||||
|
import org.jetbrains.kotlin.types.model.TypeSystemContext
|
||||||
|
import org.jetbrains.kotlin.types.model.TypeVariance
|
||||||
|
|
||||||
|
abstract class AbstractSignatureParts<Annotation : Any> {
|
||||||
|
// TODO: some of this might be better off as parameters
|
||||||
|
abstract val annotationTypeQualifierResolver: AbstractAnnotationTypeQualifierResolver<Annotation>
|
||||||
|
abstract val enableImprovementsInStrictMode: Boolean
|
||||||
|
abstract val containerAnnotations: Iterable<Annotation>
|
||||||
|
abstract val containerApplicabilityType: AnnotationQualifierApplicabilityType
|
||||||
|
abstract val containerDefaultTypeQualifiers: JavaTypeQualifiersByElementType?
|
||||||
|
abstract val containerIsVarargParameter: Boolean
|
||||||
|
abstract val isCovariant: Boolean
|
||||||
|
abstract val skipRawTypeArguments: Boolean
|
||||||
|
abstract val typeSystem: TypeSystemContext
|
||||||
|
|
||||||
|
abstract val Annotation.forceWarning: Boolean
|
||||||
|
|
||||||
|
abstract val KotlinTypeMarker.annotations: Iterable<Annotation>
|
||||||
|
abstract val KotlinTypeMarker.enhancedForWarnings: KotlinTypeMarker?
|
||||||
|
abstract val KotlinTypeMarker.fqNameUnsafe: FqNameUnsafe?
|
||||||
|
abstract fun KotlinTypeMarker.isEqual(other: KotlinTypeMarker): Boolean
|
||||||
|
|
||||||
|
abstract val TypeParameterMarker.starProjectedType: KotlinTypeMarker?
|
||||||
|
abstract val TypeParameterMarker.isFromJava: Boolean
|
||||||
|
|
||||||
|
open val KotlinTypeMarker.isNotNullTypeParameterCompat: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
private val KotlinTypeMarker.nullabilityQualifier: NullabilityQualifier?
|
||||||
|
get() = with(typeSystem) {
|
||||||
|
when {
|
||||||
|
lowerBoundIfFlexible().isMarkedNullable() -> NullabilityQualifier.NULLABLE
|
||||||
|
!upperBoundIfFlexible().isMarkedNullable() -> NullabilityQualifier.NOT_NULL
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KotlinTypeMarker.extractQualifiers(): JavaTypeQualifiers {
|
||||||
|
val forErrors = nullabilityQualifier
|
||||||
|
val forErrorsOrWarnings = forErrors ?: enhancedForWarnings?.nullabilityQualifier
|
||||||
|
val mutability = with(typeSystem) {
|
||||||
|
when {
|
||||||
|
JavaToKotlinClassMap.isReadOnly(lowerBoundIfFlexible().fqNameUnsafe) -> MutabilityQualifier.READ_ONLY
|
||||||
|
JavaToKotlinClassMap.isMutable(upperBoundIfFlexible().fqNameUnsafe) -> MutabilityQualifier.MUTABLE
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val isNotNullTypeParameter = with(typeSystem) { isDefinitelyNotNullType() } || isNotNullTypeParameterCompat
|
||||||
|
return JavaTypeQualifiers(forErrorsOrWarnings, mutability, isNotNullTypeParameter, forErrorsOrWarnings != forErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TypeAndDefaultQualifiers.extractQualifiersFromAnnotations(): JavaTypeQualifiers {
|
||||||
|
if (type == null && with(typeSystem) { typeParameterForArgument?.getVariance() } == TypeVariance.IN) {
|
||||||
|
// Star projections can only be enhanced in one way: `?` -> `? extends <something>`. Given a Kotlin type `C<in T>
|
||||||
|
// (declaration-site variance), this is not a valid enhancement due to conflicting variances.
|
||||||
|
return JavaTypeQualifiers.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
val isHeadTypeConstructor = typeParameterForArgument == null
|
||||||
|
val typeOrBound = type ?: typeParameterForArgument?.starProjectedType ?: return JavaTypeQualifiers.NONE
|
||||||
|
val typeParameterUse = with(typeSystem) { typeOrBound.typeConstructor().getTypeParameterClassifier() }
|
||||||
|
val typeParameterBounds = containerApplicabilityType == AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS
|
||||||
|
val composedAnnotation = when {
|
||||||
|
!isHeadTypeConstructor -> typeOrBound.annotations
|
||||||
|
!typeParameterBounds && enableImprovementsInStrictMode ->
|
||||||
|
// We don't apply container type use annotations to avoid double applying them like with arrays:
|
||||||
|
// @NotNull Integer [] f15();
|
||||||
|
// Otherwise, in the example above we would apply `@NotNull` to `Integer` (i.e. array element; as TYPE_USE annotation)
|
||||||
|
// and to entire array (as METHOD annotation).
|
||||||
|
// In other words, we prefer TYPE_USE target of an annotation, and apply the annotation only according to it, if it's present.
|
||||||
|
// See KT-24392 for more details.
|
||||||
|
containerAnnotations.filter { !annotationTypeQualifierResolver.isTypeUseAnnotation(it) } + typeOrBound.annotations
|
||||||
|
else -> containerAnnotations + typeOrBound.annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
val annotationsMutability = annotationTypeQualifierResolver.extractMutability(composedAnnotation)
|
||||||
|
val annotationsNullability = annotationTypeQualifierResolver.extractNullability(composedAnnotation) { forceWarning }
|
||||||
|
|
||||||
|
if (type != null && annotationsNullability != null) {
|
||||||
|
return JavaTypeQualifiers(
|
||||||
|
annotationsNullability.qualifier, annotationsMutability,
|
||||||
|
annotationsNullability.qualifier == NullabilityQualifier.NOT_NULL && typeParameterUse != null,
|
||||||
|
annotationsNullability.isForWarningOnly
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check whether the code below works properly for star projections (when typeOrBound != type)
|
||||||
|
val applicabilityType = when {
|
||||||
|
isHeadTypeConstructor || typeParameterBounds -> containerApplicabilityType
|
||||||
|
else -> AnnotationQualifierApplicabilityType.TYPE_USE
|
||||||
|
}
|
||||||
|
val defaultTypeQualifier = defaultQualifiers?.get(applicabilityType)
|
||||||
|
?.takeIf { (it.affectsTypeParameterBasedTypes || typeParameterUse == null) && (it.affectsStarProjection || type != null) }
|
||||||
|
|
||||||
|
val referencedParameterBoundsNullability = typeParameterUse?.boundsNullability
|
||||||
|
// For type parameter uses, we have *three* options:
|
||||||
|
// T!! - NOT_NULL, isNotNullTypeParameter = true
|
||||||
|
// happens if T is bounded by @NotNull (technically !! is redundant) or context says unannotated
|
||||||
|
// type parameters are non-null;
|
||||||
|
// T - NOT_NULL, isNotNullTypeParameter = false
|
||||||
|
// happens if T is bounded by @Nullable or context says unannotated types in general are non-null;
|
||||||
|
// T? - NULLABLE, isNotNullTypeParameter = false
|
||||||
|
// happens if context says unannotated types in general are nullable.
|
||||||
|
// For other types, this is more straightforward (just take nullability from the context).
|
||||||
|
// TODO: clean up the representation of those cases in JavaTypeQualifiers
|
||||||
|
val defaultNullability =
|
||||||
|
referencedParameterBoundsNullability?.copy(qualifier = NullabilityQualifier.NOT_NULL)
|
||||||
|
?: defaultTypeQualifier?.nullabilityQualifier
|
||||||
|
val isNotNullTypeParameter =
|
||||||
|
referencedParameterBoundsNullability?.qualifier == NullabilityQualifier.NOT_NULL ||
|
||||||
|
(typeParameterUse != null && defaultTypeQualifier?.nullabilityQualifier?.qualifier == NullabilityQualifier.NOT_NULL)
|
||||||
|
|
||||||
|
// We should also enhance this type to satisfy the bound of the type parameter it is instantiating:
|
||||||
|
// for C<T extends @NotNull V>, C<X!> becomes C<X!!> regardless of the above.
|
||||||
|
val substitutedParameterBoundsNullability = typeParameterForArgument?.boundsNullability
|
||||||
|
val result = when {
|
||||||
|
substitutedParameterBoundsNullability == null -> defaultNullability
|
||||||
|
defaultNullability == null ->
|
||||||
|
if (substitutedParameterBoundsNullability.qualifier == NullabilityQualifier.NULLABLE)
|
||||||
|
substitutedParameterBoundsNullability.copy(qualifier = NullabilityQualifier.FORCE_FLEXIBILITY)
|
||||||
|
else
|
||||||
|
substitutedParameterBoundsNullability
|
||||||
|
type == null -> substitutedParameterBoundsNullability
|
||||||
|
else -> mostSpecific(substitutedParameterBoundsNullability, defaultNullability)
|
||||||
|
}
|
||||||
|
return JavaTypeQualifiers(result?.qualifier, annotationsMutability, isNotNullTypeParameter, result?.isForWarningOnly == true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mostSpecific(
|
||||||
|
a: NullabilityQualifierWithMigrationStatus,
|
||||||
|
b: NullabilityQualifierWithMigrationStatus
|
||||||
|
): NullabilityQualifierWithMigrationStatus {
|
||||||
|
// TODO: this probably behaves really weirdly when some of those are warnings.
|
||||||
|
if (a.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return b
|
||||||
|
if (b.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return a
|
||||||
|
if (a.qualifier == NullabilityQualifier.NULLABLE) return b
|
||||||
|
if (b.qualifier == NullabilityQualifier.NULLABLE) return a
|
||||||
|
assert(a.qualifier == b.qualifier && a.qualifier == NullabilityQualifier.NOT_NULL) {
|
||||||
|
"Expected everything is NOT_NULL, but $a and $b are found"
|
||||||
|
}
|
||||||
|
|
||||||
|
return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val TypeParameterMarker.boundsNullability: NullabilityQualifierWithMigrationStatus?
|
||||||
|
get() = with(typeSystem) {
|
||||||
|
if (!isFromJava) return null
|
||||||
|
val bounds = getUpperBounds()
|
||||||
|
val enhancedBounds = when {
|
||||||
|
bounds.all { it.isError() } -> return null
|
||||||
|
// TODO: what if e.g. one bound is nullable and another is not null for warnings?
|
||||||
|
bounds.any { it.nullabilityQualifier != null } -> bounds
|
||||||
|
bounds.any { it.enhancedForWarnings != null } -> bounds.mapNotNull { it.enhancedForWarnings }
|
||||||
|
else -> return null
|
||||||
|
}
|
||||||
|
val qualifier = if (enhancedBounds.all { it.isNullableType() }) NullabilityQualifier.NULLABLE else NullabilityQualifier.NOT_NULL
|
||||||
|
return NullabilityQualifierWithMigrationStatus(qualifier, isForWarningOnly = enhancedBounds !== bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KotlinTypeMarker.computeIndexedQualifiers(
|
||||||
|
overrides: Iterable<KotlinTypeMarker>, predefined: TypeEnhancementInfo?
|
||||||
|
): IndexedJavaTypeQualifiers {
|
||||||
|
val indexedThisType = toIndexed()
|
||||||
|
val indexedFromSupertypes = overrides.map { it.toIndexed() }
|
||||||
|
|
||||||
|
// The covariant case may be hard, e.g. in the superclass the return may be Super<T>, but in the subclass it may be Derived, which
|
||||||
|
// is declared to extend Super<T>, and propagating data here is highly non-trivial, so we only look at the head type constructor
|
||||||
|
// (outermost type), unless the type in the subclass is interchangeable with the all the types in superclasses:
|
||||||
|
// e.g. we have (Mutable)List<String!>! in the subclass and { List<String!>, (Mutable)List<String>! } from superclasses
|
||||||
|
// Note that `this` is flexible here, so it's equal to it's bounds
|
||||||
|
val onlyHeadTypeConstructor = isCovariant && overrides.any { !this@computeIndexedQualifiers.isEqual(it) }
|
||||||
|
|
||||||
|
val treeSize = if (onlyHeadTypeConstructor) 1 else indexedThisType.size
|
||||||
|
val computedResult = Array(treeSize) { index ->
|
||||||
|
val qualifiers = indexedThisType[index].extractQualifiersFromAnnotations()
|
||||||
|
val superQualifiers = indexedFromSupertypes.mapNotNull { it.getOrNull(index)?.type?.extractQualifiers() }
|
||||||
|
qualifiers.computeQualifiersForOverride(superQualifiers, index == 0 && isCovariant, index == 0 && containerIsVarargParameter)
|
||||||
|
}
|
||||||
|
return { index -> predefined?.map?.get(index) ?: computedResult.getOrElse(index) { JavaTypeQualifiers.NONE } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> T.flattenTree(result: MutableList<T>, children: (T) -> Iterable<T>?) {
|
||||||
|
result.add(this)
|
||||||
|
children(this)?.forEach { it.flattenTree(result, children) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> T.flattenTree(children: (T) -> Iterable<T>?): List<T> =
|
||||||
|
ArrayList<T>(1).also { flattenTree(it, children) }
|
||||||
|
|
||||||
|
private fun KotlinTypeMarker.extractAndMergeDefaultQualifiers(oldQualifiers: JavaTypeQualifiersByElementType?) =
|
||||||
|
annotationTypeQualifierResolver.extractAndMergeDefaultQualifiers(oldQualifiers, annotations)
|
||||||
|
|
||||||
|
private fun KotlinTypeMarker.toIndexed(): List<TypeAndDefaultQualifiers> = with(typeSystem) {
|
||||||
|
TypeAndDefaultQualifiers(this@toIndexed, extractAndMergeDefaultQualifiers(containerDefaultTypeQualifiers), null).flattenTree {
|
||||||
|
// Enhancement of raw type arguments may enter a loop in FE1.0.
|
||||||
|
if (skipRawTypeArguments && it.type?.asFlexibleType()?.asRawType() != null) return@flattenTree null
|
||||||
|
|
||||||
|
it.type?.typeConstructor()?.getParameters()?.zip(it.type.getArguments()) { parameter, arg ->
|
||||||
|
if (arg.isStarProjection()) {
|
||||||
|
TypeAndDefaultQualifiers(null, it.defaultQualifiers, parameter)
|
||||||
|
} else {
|
||||||
|
val type = arg.getType()
|
||||||
|
TypeAndDefaultQualifiers(type, type.extractAndMergeDefaultQualifiers(it.defaultQualifiers), parameter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TypeAndDefaultQualifiers(
|
||||||
|
val type: KotlinTypeMarker?,
|
||||||
|
val defaultQualifiers: JavaTypeQualifiersByElementType?,
|
||||||
|
val typeParameterForArgument: TypeParameterMarker?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias IndexedJavaTypeQualifiers = (Int) -> JavaTypeQualifiers
|
||||||
+74
-245
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.load.java.typeEnhancement
|
|||||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.composeAnnotations
|
|
||||||
import org.jetbrains.kotlin.load.java.*
|
import org.jetbrains.kotlin.load.java.*
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.*
|
import org.jetbrains.kotlin.load.java.descriptors.*
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||||
@@ -37,9 +37,11 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|||||||
import org.jetbrains.kotlin.resolve.deprecation.DEPRECATED_FUNCTION_KEY
|
import org.jetbrains.kotlin.resolve.deprecation.DEPRECATED_FUNCTION_KEY
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext
|
||||||
|
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||||
|
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||||
|
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext
|
||||||
import org.jetbrains.kotlin.types.typeUtil.contains
|
import org.jetbrains.kotlin.types.typeUtil.contains
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) {
|
class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) {
|
||||||
@@ -81,12 +83,12 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) {
|
|||||||
|
|
||||||
val receiverTypeEnhancement =
|
val receiverTypeEnhancement =
|
||||||
if (extensionReceiverParameter != null)
|
if (extensionReceiverParameter != null)
|
||||||
partsForValueParameter(
|
enhanceValueParameter(
|
||||||
parameterDescriptor =
|
parameterDescriptor =
|
||||||
annotationOwnerForMember.safeAs<FunctionDescriptor>()
|
annotationOwnerForMember.safeAs<FunctionDescriptor>()
|
||||||
?.getUserData(JavaMethodDescriptor.ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER),
|
?.getUserData(JavaMethodDescriptor.ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER),
|
||||||
methodContext = memberContext
|
methodContext = memberContext, predefined = null
|
||||||
) { it.extensionReceiverParameter!!.type }.enhance()
|
) { it.extensionReceiverParameter!!.type }
|
||||||
else null
|
else null
|
||||||
|
|
||||||
val predefinedEnhancementInfo =
|
val predefinedEnhancementInfo =
|
||||||
@@ -102,20 +104,21 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val valueParameterEnhancements = annotationOwnerForMember.valueParameters.map { p ->
|
val valueParameterEnhancements = annotationOwnerForMember.valueParameters.map { p ->
|
||||||
partsForValueParameter(p, memberContext) { it.valueParameters[p.index].type }
|
val predefined = predefinedEnhancementInfo?.parametersInfo?.getOrNull(p.index)
|
||||||
.enhance(predefinedEnhancementInfo?.parametersInfo?.getOrNull(p.index))
|
enhanceValueParameter(p, memberContext, predefined) { it.valueParameters[p.index].type }
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnTypeEnhancement =
|
val returnTypeEnhancement =
|
||||||
parts(
|
enhance(
|
||||||
typeContainer = annotationOwnerForMember, isCovariant = true,
|
typeContainer = annotationOwnerForMember, isCovariant = true,
|
||||||
containerContext = memberContext,
|
containerContext = memberContext,
|
||||||
containerApplicabilityType =
|
containerApplicabilityType =
|
||||||
if (this.safeAs<PropertyDescriptor>()?.isJavaField == true)
|
if (this.safeAs<PropertyDescriptor>()?.isJavaField == true)
|
||||||
AnnotationQualifierApplicabilityType.FIELD
|
AnnotationQualifierApplicabilityType.FIELD
|
||||||
else
|
else
|
||||||
AnnotationQualifierApplicabilityType.METHOD_RETURN_TYPE
|
AnnotationQualifierApplicabilityType.METHOD_RETURN_TYPE,
|
||||||
) { it.returnType!! }.enhance(predefinedEnhancementInfo?.returnTypeInfo)
|
predefinedEnhancementInfo?.returnTypeInfo
|
||||||
|
) { it.returnType!! }
|
||||||
|
|
||||||
val containsFunctionN = returnType!!.containsFunctionN() ||
|
val containsFunctionN = returnType!!.containsFunctionN() ||
|
||||||
extensionReceiverParameter?.type?.containsFunctionN() ?: false ||
|
extensionReceiverParameter?.type?.containsFunctionN() ?: false ||
|
||||||
@@ -151,11 +154,8 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) {
|
|||||||
// TODO: would not enhancing raw type arguments be sufficient?
|
// TODO: would not enhancing raw type arguments be sufficient?
|
||||||
if (bound.contains { it is RawType }) return@map bound
|
if (bound.contains { it is RawType }) return@map bound
|
||||||
|
|
||||||
SignatureParts(
|
SignatureParts(typeParameter, false, context, AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS)
|
||||||
typeEnhancement, typeParameter, bound, emptyList(), false, context,
|
.enhance(bound, emptyList()) ?: bound
|
||||||
AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS,
|
|
||||||
typeParameterBounds = true
|
|
||||||
).enhance() ?: bound
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,9 +165,9 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) {
|
|||||||
*/
|
*/
|
||||||
fun enhanceSuperType(type: KotlinType, context: LazyJavaResolverContext) =
|
fun enhanceSuperType(type: KotlinType, context: LazyJavaResolverContext) =
|
||||||
SignatureParts(
|
SignatureParts(
|
||||||
typeEnhancement, typeContainer = null, type, emptyList(), isCovariant = false,
|
typeContainer = null, isCovariant = false,
|
||||||
context, AnnotationQualifierApplicabilityType.TYPE_USE, isSuperTypesEnhancement = true
|
context, AnnotationQualifierApplicabilityType.TYPE_USE, skipRawTypeArguments = true
|
||||||
).enhance() ?: type
|
).enhance(type, emptyList()) ?: type
|
||||||
|
|
||||||
private fun KotlinType.containsFunctionN(): Boolean =
|
private fun KotlinType.containsFunctionN(): Boolean =
|
||||||
TypeUtils.contains(this) {
|
TypeUtils.contains(this) {
|
||||||
@@ -176,255 +176,84 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) {
|
|||||||
classifier.fqNameOrNull() == JavaToKotlinClassMap.FUNCTION_N_FQ_NAME
|
classifier.fqNameOrNull() == JavaToKotlinClassMap.FUNCTION_N_FQ_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun CallableMemberDescriptor.enhanceValueParameter(
|
||||||
|
// TODO: investigate if it's really can be a null (check properties' with extension overrides in Java)
|
||||||
|
parameterDescriptor: ValueParameterDescriptor?,
|
||||||
|
methodContext: LazyJavaResolverContext,
|
||||||
|
predefined: TypeEnhancementInfo?,
|
||||||
|
collector: (CallableMemberDescriptor) -> KotlinType
|
||||||
|
) = enhance(
|
||||||
|
parameterDescriptor, false,
|
||||||
|
parameterDescriptor?.let { methodContext.copyWithNewDefaultTypeQualifiers(it.annotations) } ?: methodContext,
|
||||||
|
AnnotationQualifierApplicabilityType.VALUE_PARAMETER,
|
||||||
|
predefined, collector
|
||||||
|
)
|
||||||
|
|
||||||
private fun CallableMemberDescriptor.parts(
|
private fun CallableMemberDescriptor.enhance(
|
||||||
typeContainer: Annotated?,
|
typeContainer: Annotated?,
|
||||||
isCovariant: Boolean,
|
isCovariant: Boolean,
|
||||||
containerContext: LazyJavaResolverContext,
|
containerContext: LazyJavaResolverContext,
|
||||||
containerApplicabilityType: AnnotationQualifierApplicabilityType,
|
containerApplicabilityType: AnnotationQualifierApplicabilityType,
|
||||||
|
predefined: TypeEnhancementInfo?,
|
||||||
collector: (CallableMemberDescriptor) -> KotlinType
|
collector: (CallableMemberDescriptor) -> KotlinType
|
||||||
): SignatureParts {
|
): KotlinType? {
|
||||||
return SignatureParts(
|
return SignatureParts(typeContainer, isCovariant, containerContext, containerApplicabilityType)
|
||||||
typeEnhancement,
|
.enhance(collector(this), overriddenDescriptors.map { collector(it) }, predefined)
|
||||||
typeContainer,
|
|
||||||
collector(this),
|
|
||||||
this.overriddenDescriptors.map {
|
|
||||||
collector(it)
|
|
||||||
},
|
|
||||||
isCovariant,
|
|
||||||
// recompute default type qualifiers using type annotations
|
|
||||||
containerContext.copyWithNewDefaultTypeQualifiers(collector(this).annotations),
|
|
||||||
containerApplicabilityType
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun SignatureParts.enhance(type: KotlinType, overrides: List<KotlinType>, predefined: TypeEnhancementInfo? = null) =
|
||||||
|
with(typeEnhancement) { type.enhance(type.computeIndexedQualifiers(overrides, predefined), skipRawTypeArguments) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SignatureParts(
|
private class SignatureParts(
|
||||||
private val typeEnhancement: JavaTypeEnhancement,
|
|
||||||
private val typeContainer: Annotated?,
|
private val typeContainer: Annotated?,
|
||||||
private val fromOverride: KotlinType,
|
override val isCovariant: Boolean,
|
||||||
private val fromOverridden: Collection<KotlinType>,
|
|
||||||
private val isCovariant: Boolean,
|
|
||||||
private val containerContext: LazyJavaResolverContext,
|
private val containerContext: LazyJavaResolverContext,
|
||||||
private val containerApplicabilityType: AnnotationQualifierApplicabilityType,
|
override val containerApplicabilityType: AnnotationQualifierApplicabilityType,
|
||||||
private val typeParameterBounds: Boolean = false,
|
override val skipRawTypeArguments: Boolean = false
|
||||||
private val isSuperTypesEnhancement: Boolean = false
|
) : AbstractSignatureParts<AnnotationDescriptor>() {
|
||||||
) {
|
override val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver
|
||||||
private val isForVarargParameter get() = typeContainer.safeAs<ValueParameterDescriptor>()?.varargElementType != null
|
get() = containerContext.components.annotationTypeQualifierResolver
|
||||||
|
|
||||||
fun enhance(predefined: TypeEnhancementInfo? = null): KotlinType? =
|
override val enableImprovementsInStrictMode: Boolean
|
||||||
with(typeEnhancement) {
|
get() = containerContext.components.settings.typeEnhancementImprovementsInStrictMode
|
||||||
fromOverride.enhance(computeIndexedQualifiersForOverride(predefined), isSuperTypesEnhancement)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val KotlinType.isNotNullTypeParameter: Boolean
|
override val containerAnnotations: Iterable<AnnotationDescriptor>
|
||||||
get() = unwrap().let { it is NotNullTypeParameter || it is DefinitelyNotNullType }
|
get() = typeContainer?.annotations ?: emptyList()
|
||||||
|
|
||||||
private val KotlinType.fqNameUnsafe: FqNameUnsafe?
|
override val containerDefaultTypeQualifiers: JavaTypeQualifiersByElementType?
|
||||||
get() = TypeUtils.getClassDescriptor(this)?.let { DescriptorUtils.getFqName(it) }
|
get() = containerContext.defaultTypeQualifiers
|
||||||
|
|
||||||
private val KotlinType.nullabilityQualifier: NullabilityQualifier?
|
override val containerIsVarargParameter: Boolean
|
||||||
get() = when {
|
get() = typeContainer is ValueParameterDescriptor && typeContainer.varargElementType != null
|
||||||
lowerIfFlexible().isMarkedNullable -> NullabilityQualifier.NULLABLE
|
|
||||||
!upperIfFlexible().isMarkedNullable -> NullabilityQualifier.NOT_NULL
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
private val KotlinType.mutabilityQualifier: MutabilityQualifier?
|
override val typeSystem: TypeSystemInferenceExtensionContext
|
||||||
get() = when {
|
get() = SimpleClassicTypeSystemContext
|
||||||
JavaToKotlinClassMap.isReadOnly(lowerIfFlexible().fqNameUnsafe) -> MutabilityQualifier.READ_ONLY
|
|
||||||
JavaToKotlinClassMap.isMutable(upperIfFlexible().fqNameUnsafe) -> MutabilityQualifier.MUTABLE
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KotlinType.extractQualifiers(): JavaTypeQualifiers {
|
override val AnnotationDescriptor.forceWarning: Boolean
|
||||||
val forErrors = nullabilityQualifier
|
get() = (this is PossiblyExternalAnnotationDescriptor && isIdeExternalAnnotation) ||
|
||||||
val forErrorsOrWarnings = forErrors ?: unwrapEnhancement().nullabilityQualifier
|
(this is LazyJavaAnnotationDescriptor && !enableImprovementsInStrictMode &&
|
||||||
return JavaTypeQualifiers(forErrorsOrWarnings, mutabilityQualifier, isNotNullTypeParameter, forErrorsOrWarnings != forErrors)
|
(isFreshlySupportedTypeUseAnnotation ||
|
||||||
}
|
containerApplicabilityType == AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS))
|
||||||
|
|
||||||
private fun TypeAndDefaultQualifiers.extractQualifiersFromAnnotations(): JavaTypeQualifiers {
|
override val KotlinTypeMarker.annotations: Iterable<AnnotationDescriptor>
|
||||||
if (type == null && typeParameterForArgument?.variance == Variance.IN_VARIANCE) {
|
get() = (this as KotlinType).annotations
|
||||||
// Star projections can only be enhanced in one way: `?` -> `? extends <something>`. Given a Kotlin type `C<in T>
|
|
||||||
// (declaration-site variance), this is not a valid enhancement due to conflicting variances.
|
|
||||||
return JavaTypeQualifiers.NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
val annotationTypeQualifierResolver = containerContext.components.annotationTypeQualifierResolver
|
override val KotlinTypeMarker.enhancedForWarnings: KotlinType?
|
||||||
val areImprovementsInStrictMode = containerContext.components.settings.typeEnhancementImprovementsInStrictMode
|
get() = (this as KotlinType).getEnhancement()
|
||||||
|
|
||||||
val isHeadTypeConstructor = typeParameterForArgument == null
|
override val KotlinTypeMarker.fqNameUnsafe: FqNameUnsafe?
|
||||||
val typeOrBound = type ?: typeParameterForArgument!!.starProjectionType()
|
get() = TypeUtils.getClassDescriptor(this as KotlinType)?.let { DescriptorUtils.getFqName(it) }
|
||||||
val composedAnnotation =
|
|
||||||
if (isHeadTypeConstructor && typeContainer != null && !typeParameterBounds && areImprovementsInStrictMode) {
|
|
||||||
val filteredContainerAnnotations = typeContainer.annotations.filter {
|
|
||||||
/*
|
|
||||||
* We don't apply container type use annotations to avoid double applying them like with arrays:
|
|
||||||
* @NotNull Integer [] f15();
|
|
||||||
* Otherwise, in the example above we would apply `@NotNull` to `Integer` (i.e. array element; as TYPE_USE annotation)
|
|
||||||
* and to entire array (as METHOD annotation).
|
|
||||||
* In other words, we prefer TYPE_USE target of an annotation, and apply the annotation only according to it, if it's present.
|
|
||||||
* See KT-24392 for more details.
|
|
||||||
*/
|
|
||||||
!annotationTypeQualifierResolver.isTypeUseAnnotation(it)
|
|
||||||
}
|
|
||||||
composeAnnotations(Annotations.create(filteredContainerAnnotations), typeOrBound.annotations)
|
|
||||||
} else if (isHeadTypeConstructor && typeContainer != null) {
|
|
||||||
composeAnnotations(typeContainer.annotations, typeOrBound.annotations)
|
|
||||||
} else typeOrBound.annotations
|
|
||||||
|
|
||||||
val annotationsMutability = annotationTypeQualifierResolver.extractMutability(composedAnnotation)
|
override val KotlinTypeMarker.isNotNullTypeParameterCompat: Boolean
|
||||||
val annotationsNullability = annotationTypeQualifierResolver.extractNullability(composedAnnotation) {
|
get() = (this as KotlinType).unwrap() is NotNullTypeParameter
|
||||||
(this is LazyJavaAnnotationDescriptor && (isFreshlySupportedTypeUseAnnotation || typeParameterBounds) && !areImprovementsInStrictMode) ||
|
|
||||||
(this is PossiblyExternalAnnotationDescriptor && isIdeExternalAnnotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type != null && annotationsNullability != null) {
|
override fun KotlinTypeMarker.isEqual(other: KotlinTypeMarker): Boolean =
|
||||||
return JavaTypeQualifiers(
|
containerContext.components.kotlinTypeChecker.equalTypes(this as KotlinType, other as KotlinType)
|
||||||
annotationsNullability.qualifier, annotationsMutability,
|
|
||||||
annotationsNullability.qualifier == NullabilityQualifier.NOT_NULL && type.isTypeParameter(),
|
|
||||||
annotationsNullability.isForWarningOnly
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check whether the code below works properly for star projections (when typeOrBound != type)
|
override val TypeParameterMarker.starProjectedType: KotlinType
|
||||||
val defaultTypeQualifier = (
|
get() = (this as TypeParameterDescriptor).starProjectionType()
|
||||||
if (isHeadTypeConstructor)
|
|
||||||
containerContext.defaultTypeQualifiers?.get(containerApplicabilityType)
|
|
||||||
else
|
|
||||||
defaultQualifiers?.get(
|
|
||||||
if (typeParameterBounds)
|
|
||||||
AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS
|
|
||||||
else
|
|
||||||
AnnotationQualifierApplicabilityType.TYPE_USE
|
|
||||||
)
|
|
||||||
)?.takeIf { (it.affectsTypeParameterBasedTypes || !typeOrBound.isTypeParameter()) && (it.affectsStarProjection || type != null) }
|
|
||||||
|
|
||||||
val referencedParameterBoundsNullability =
|
override val TypeParameterMarker.isFromJava: Boolean
|
||||||
(typeOrBound.constructor.declarationDescriptor as? TypeParameterDescriptor)?.boundsNullability()
|
get() = this is LazyJavaTypeParameterDescriptor
|
||||||
|
|
||||||
// For type parameter uses, we have *three* options:
|
|
||||||
// T!! - NOT_NULL, isNotNullTypeParameter = true
|
|
||||||
// happens if T is bounded by @NotNull (technically !! is redundant) or context says unannotated
|
|
||||||
// type parameters are non-null;
|
|
||||||
// T - NOT_NULL, isNotNullTypeParameter = false
|
|
||||||
// happens if T is bounded by @Nullable or context says unannotated types in general are non-null;
|
|
||||||
// T? - NULLABLE, isNotNullTypeParameter = false
|
|
||||||
// happens if context says unannotated types in general are nullable.
|
|
||||||
// For other types, this is more straightforward (just take nullability from the context).
|
|
||||||
// TODO: clean up the representation of those cases in JavaTypeQualifiers
|
|
||||||
val defaultNullability =
|
|
||||||
referencedParameterBoundsNullability?.copy(qualifier = NullabilityQualifier.NOT_NULL)
|
|
||||||
?: defaultTypeQualifier?.nullabilityQualifier
|
|
||||||
val isNotNullTypeParameter =
|
|
||||||
referencedParameterBoundsNullability?.qualifier == NullabilityQualifier.NOT_NULL ||
|
|
||||||
(typeOrBound.isTypeParameter() && defaultTypeQualifier?.nullabilityQualifier?.qualifier == NullabilityQualifier.NOT_NULL)
|
|
||||||
|
|
||||||
// We should also enhance this type to satisfy the bound of the type parameter it is instantiating:
|
|
||||||
// for C<T extends @NotNull V>, C<X!> becomes C<X!!> regardless of the above.
|
|
||||||
val substitutedParameterBoundsNullability = typeParameterForArgument?.boundsNullability()
|
|
||||||
val result = when {
|
|
||||||
substitutedParameterBoundsNullability == null -> defaultNullability
|
|
||||||
defaultNullability == null ->
|
|
||||||
if (substitutedParameterBoundsNullability.qualifier == NullabilityQualifier.NULLABLE)
|
|
||||||
substitutedParameterBoundsNullability.copy(qualifier = NullabilityQualifier.FORCE_FLEXIBILITY)
|
|
||||||
else
|
|
||||||
substitutedParameterBoundsNullability
|
|
||||||
else -> mostSpecific(substitutedParameterBoundsNullability, defaultNullability)
|
|
||||||
}
|
|
||||||
return JavaTypeQualifiers(result?.qualifier, annotationsMutability, isNotNullTypeParameter, result?.isForWarningOnly == true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun mostSpecific(
|
|
||||||
a: NullabilityQualifierWithMigrationStatus,
|
|
||||||
b: NullabilityQualifierWithMigrationStatus
|
|
||||||
): NullabilityQualifierWithMigrationStatus {
|
|
||||||
// TODO: this probably behaves really weirdly when some of those are warnings.
|
|
||||||
if (a.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return b
|
|
||||||
if (b.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return a
|
|
||||||
if (a.qualifier == NullabilityQualifier.NULLABLE) return b
|
|
||||||
if (b.qualifier == NullabilityQualifier.NULLABLE) return a
|
|
||||||
assert(a.qualifier == b.qualifier && a.qualifier == NullabilityQualifier.NOT_NULL) {
|
|
||||||
"Expected everything is NOT_NULL, but $a and $b are found"
|
|
||||||
}
|
|
||||||
|
|
||||||
return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TypeParameterDescriptor.boundsNullability(): NullabilityQualifierWithMigrationStatus? {
|
|
||||||
// Do not use bounds from Kotlin-defined type parameters
|
|
||||||
if (this !is LazyJavaTypeParameterDescriptor || upperBounds.all(KotlinType::isError)) return null
|
|
||||||
|
|
||||||
if (upperBounds.all(KotlinType::isNullabilityFlexible)) {
|
|
||||||
if (upperBounds.any { it is FlexibleTypeWithEnhancement && !it.enhancement.isNullable() }) {
|
|
||||||
return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL, isForWarningOnly = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (upperBounds.any { it is FlexibleTypeWithEnhancement && it.enhancement.isNullable() }) {
|
|
||||||
return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE, isForWarningOnly = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val resultingQualifier =
|
|
||||||
if (upperBounds.any { !it.isNullable() }) NullabilityQualifier.NOT_NULL else NullabilityQualifier.NULLABLE
|
|
||||||
|
|
||||||
return NullabilityQualifierWithMigrationStatus(resultingQualifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeIndexedQualifiersForOverride(predefined: TypeEnhancementInfo?): (Int) -> JavaTypeQualifiers {
|
|
||||||
val indexedFromSupertypes = fromOverridden.map { it.toIndexed() }
|
|
||||||
val indexedThisType = fromOverride.toIndexed()
|
|
||||||
|
|
||||||
// The covariant case may be hard, e.g. in the superclass the return may be Super<T>, but in the subclass it may be Derived, which
|
|
||||||
// is declared to extend Super<T>, and propagating data here is highly non-trivial, so we only look at the head type constructor
|
|
||||||
// (outermost type), unless the type in the subclass is interchangeable with the all the types in superclasses:
|
|
||||||
// e.g. we have (Mutable)List<String!>! in the subclass and { List<String!>, (Mutable)List<String>! } from superclasses
|
|
||||||
// Note that `this` is flexible here, so it's equal to it's bounds
|
|
||||||
val onlyHeadTypeConstructor = isCovariant && fromOverridden.any { !KotlinTypeChecker.DEFAULT.equalTypes(it, fromOverride) }
|
|
||||||
|
|
||||||
val treeSize = if (onlyHeadTypeConstructor) 1 else indexedThisType.size
|
|
||||||
val computedResult = Array(treeSize) { index ->
|
|
||||||
val qualifiers = indexedThisType[index].extractQualifiersFromAnnotations()
|
|
||||||
val superQualifiers = indexedFromSupertypes.mapNotNull { it.getOrNull(index)?.type?.extractQualifiers() }
|
|
||||||
qualifiers.computeQualifiersForOverride(superQualifiers, index == 0 && isCovariant, index == 0 && isForVarargParameter)
|
|
||||||
}
|
|
||||||
return { index -> predefined?.map?.get(index) ?: computedResult.getOrElse(index) { JavaTypeQualifiers.NONE } }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> T.flattenTree(result: MutableList<T>, children: (T) -> Iterable<T>?) {
|
|
||||||
result.add(this)
|
|
||||||
children(this)?.forEach { it.flattenTree(result, children) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> T.flattenTree(children: (T) -> Iterable<T>?): List<T> =
|
|
||||||
ArrayList<T>(1).also { flattenTree(it, children) }
|
|
||||||
|
|
||||||
private fun KotlinType.extractAndMergeDefaultQualifiers(oldQualifiers: JavaTypeQualifiersByElementType?) =
|
|
||||||
containerContext.components.annotationTypeQualifierResolver.extractAndMergeDefaultQualifiers(oldQualifiers, annotations)
|
|
||||||
|
|
||||||
private fun KotlinType.toIndexed(): List<TypeAndDefaultQualifiers> =
|
|
||||||
TypeAndDefaultQualifiers(this, extractAndMergeDefaultQualifiers(containerContext.defaultTypeQualifiers), null).flattenTree {
|
|
||||||
// Enhancement of raw type arguments may enter a loop.
|
|
||||||
if (isSuperTypesEnhancement && it.type is RawType) return@flattenTree null
|
|
||||||
|
|
||||||
it.type?.arguments?.zip(it.type.constructor.parameters) { arg, parameter ->
|
|
||||||
if (arg.isStarProjection)
|
|
||||||
TypeAndDefaultQualifiers(null, it.defaultQualifiers, parameter)
|
|
||||||
else
|
|
||||||
TypeAndDefaultQualifiers(arg.type, arg.type.extractAndMergeDefaultQualifiers(it.defaultQualifiers), parameter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class TypeAndDefaultQualifiers(
|
|
||||||
val type: KotlinType?,
|
|
||||||
val defaultQualifiers: JavaTypeQualifiersByElementType?,
|
|
||||||
val typeParameterForArgument: TypeParameterDescriptor?
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun KotlinType.isNullabilityFlexible(): Boolean {
|
|
||||||
val flexibility = unwrap() as? FlexibleType ?: return false
|
|
||||||
return flexibility.lowerBound.isMarkedNullable != flexibility.upperBound.isMarkedNullable
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user