Support TypeQualifierNickname for nullability annotations

#KT-10942 In Progress
This commit is contained in:
Denis Zharkov
2017-06-13 19:37:29 +03:00
parent f2a55d590c
commit 92f9194112
9 changed files with 167 additions and 29 deletions
@@ -18,7 +18,10 @@ package org.jetbrains.kotlin.load.java
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.resolveClassByFqName
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.storage.StorageManager
@@ -49,6 +52,16 @@ class AnnotationTypeQualifierResolver(storageManager: StorageManager) {
return resolveTypeQualifierNickname(annotationClass)
}
fun isTypeQualifier(moduleDescriptor: ModuleDescriptor, classFqName: FqName): Boolean {
val classDescriptor = moduleDescriptor.resolveClassByFqName(
classFqName, NoLookupLocation.FROM_JAVA_LOADER
) ?: return false
if (classDescriptor.isTypeQualifierAnnotation) return true
return resolveTypeQualifierNickname(classDescriptor) != null
}
}
private val ClassDescriptor.isAnnotatedWithTypeQualifier: Boolean
@@ -146,7 +146,9 @@ abstract class LazyJavaScope(protected val c: LazyJavaResolverContext) : MemberS
val annotationMethod = method.containingClass.isAnnotationType
val returnTypeAttrs = LazyJavaTypeAttributes(
TypeUsage.COMMON, annotations,
isForAnnotationParameter = annotationMethod
isForAnnotationParameter = annotationMethod,
moduleDescriptor = c.module,
annotationTypeQualifierResolver = c.components.annotationTypeQualifierResolver
)
return c.typeResolver.transformJavaType(method.returnType, returnTypeAttrs)
}
@@ -163,7 +165,12 @@ abstract class LazyJavaScope(protected val c: LazyJavaResolverContext) : MemberS
val (index, javaParameter) = pair
val annotations = c.resolveAnnotations(javaParameter)
val typeUsage = LazyJavaTypeAttributes(TypeUsage.COMMON, annotations)
val typeUsage =
LazyJavaTypeAttributes(
TypeUsage.COMMON, annotations,
annotationTypeQualifierResolver = c.components.annotationTypeQualifierResolver,
moduleDescriptor = c.module
)
val (outType, varargElementType) =
if (javaParameter.isVararg) {
val paramType = javaParameter.type as? JavaArrayType
@@ -281,7 +288,11 @@ abstract class LazyJavaScope(protected val c: LazyJavaResolverContext) : MemberS
val isNotNullable = !(field.isFinalStatic && field.hasConstantNotNullInitializer)
val propertyType = c.typeResolver.transformJavaType(
field.type,
LazyJavaTypeAttributes(TypeUsage.COMMON, annotations)
LazyJavaTypeAttributes(
TypeUsage.COMMON, annotations,
annotationTypeQualifierResolver = c.components.annotationTypeQualifierResolver,
moduleDescriptor = c.module
)
)
if (!isNotNullable) {
return TypeUtils.makeNotNullable(propertyType)
@@ -17,11 +17,13 @@
package org.jetbrains.kotlin.load.java.lazy.types
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
import org.jetbrains.kotlin.load.java.ANNOTATIONS_COPIED_TO_TYPES
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.components.TypeUsage.COMMON
import org.jetbrains.kotlin.load.java.components.TypeUsage.SUPERTYPE
@@ -309,9 +311,13 @@ enum class JavaTypeFlexibility {
class LazyJavaTypeAttributes(
override val howThisTypeIsUsed: TypeUsage,
annotations: Annotations,
override val isForAnnotationParameter: Boolean = false
override val isForAnnotationParameter: Boolean = false,
private val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver,
private val moduleDescriptor: ModuleDescriptor
): JavaTypeAttributes {
override val typeAnnotations = FilteredAnnotations(annotations) { it in ANNOTATIONS_COPIED_TO_TYPES }
override val typeAnnotations = FilteredAnnotations(annotations) {
it in ANNOTATIONS_COPIED_TO_TYPES || annotationTypeQualifierResolver.isTypeQualifier(moduleDescriptor, it)
}
}
fun TypeUsage.toAttributes(
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.load.java.typeEnhancement
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
@@ -25,6 +26,8 @@ import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.asFlexibleType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
@@ -32,7 +35,7 @@ import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import java.util.*
class SignatureEnhancement {
class SignatureEnhancement(private val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver) {
fun <D : CallableMemberDescriptor> enhanceSignatures(platformSignatures: Collection<D>): Collection<D> {
return platformSignatures.map {
it.enhanceSignature()
@@ -143,29 +146,10 @@ class SignatureEnhancement {
private fun KotlinType.extractQualifiersFromAnnotations(): JavaTypeQualifiers {
fun <T: Any> List<FqName>.ifPresent(qualifier: T) = if (any { annotations.findAnnotation(it) != null}) qualifier else null
// These two overloads are just for sake of optimization as in most cases last parameter in second overload is null
fun <T: Any> uniqueNotNull(x: T?, y: T?) = if (x == null || y == null || x == y) x ?: y else null
fun <T: Any> uniqueNotNull(a: T?, b: T?, c: T?) =
if (c == null)
uniqueNotNull(a, b)
else
listOf(a, b, c).filterNotNull().toSet().singleOrNull()
// Javax/FundBugs NonNull annotation has parameter `when` that determines actual nullability
fun FqName.extractQualifierFromAnnotationWithWhen(): NullabilityQualifier? {
val annotationDescriptor = annotations.findAnnotation(this) ?: return null
return annotationDescriptor.allValueArguments.values.singleOrNull()?.value?.let {
enumEntryDescriptor ->
if (enumEntryDescriptor !is ClassDescriptor) return@let null
if (enumEntryDescriptor.name.asString() == "ALWAYS") NullabilityQualifier.NOT_NULL else NullabilityQualifier.NULLABLE
} ?: NullabilityQualifier.NOT_NULL
}
val nullability = annotations.extractNullability()
val nullability = uniqueNotNull(
NULLABLE_ANNOTATIONS.ifPresent(NullabilityQualifier.NULLABLE),
NOT_NULL_ANNOTATIONS.ifPresent(NullabilityQualifier.NOT_NULL),
JAVAX_NONNULL_ANNOTATION.extractQualifierFromAnnotationWithWhen()
)
return JavaTypeQualifiers(
nullability,
uniqueNotNull(
@@ -180,6 +164,29 @@ class SignatureEnhancement {
)
}
private fun Annotations.extractNullability(): NullabilityQualifier? {
for (annotationDescriptor in this) {
when (annotationDescriptor.annotationClass?.fqNameSafe) {
in NULLABLE_ANNOTATIONS -> return NullabilityQualifier.NULLABLE
in NOT_NULL_ANNOTATIONS -> return NullabilityQualifier.NOT_NULL
}
val typeQualifier =
annotationTypeQualifierResolver
.resolveTypeQualifierAnnotation(annotationDescriptor)
?.takeIf { it.annotationClass?.fqNameSafe == JAVAX_NONNULL_ANNOTATION }
?: continue
return typeQualifier.allValueArguments.values.singleOrNull()?.value?.let {
enumEntryDescriptor ->
if (enumEntryDescriptor !is ClassDescriptor) return@let null
if (enumEntryDescriptor.name.asString() == "ALWAYS") NullabilityQualifier.NOT_NULL else NullabilityQualifier.NULLABLE
} ?: NullabilityQualifier.NOT_NULL
}
return null
}
fun KotlinType.computeIndexedQualifiersForOverride(
fromSupertypes: Collection<KotlinType>, isCovariant: Boolean
): (Int) -> JavaTypeQualifiers {