Add warnings for jsr305 nullable annotations

#KT-19115 Fixed
This commit is contained in:
e5l
2017-08-11 14:21:23 +03:00
parent 746de612ad
commit 5501cdf049
33 changed files with 506 additions and 198 deletions
@@ -19,8 +19,6 @@ package org.jetbrains.kotlin.load.java
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver.QualifierApplicabilityType
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver.TypeQualifierWithApplicability
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
@@ -28,17 +26,14 @@ import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.Jsr305State
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
private val TYPE_QUALIFIER_NICKNAME_FQNAME = FqName("javax.annotation.meta.TypeQualifierNickname")
private val TYPE_QUALIFIER_FQNAME = FqName("javax.annotation.meta.TypeQualifier")
private val TYPE_QUALIFIER_DEFAULT_FQNAME = FqName("javax.annotation.meta.TypeQualifierDefault")
interface AnnotationTypeQualifierResolver {
fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor?
fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability?
class AnnotationTypeQualifierResolver(storageManager: StorageManager, val jsr305State: Jsr305State) {
enum class QualifierApplicabilityType {
METHOD_RETURN_TYPE, VALUE_PARAMETER, FIELD, TYPE_USE
}
@@ -53,14 +48,6 @@ interface AnnotationTypeQualifierResolver {
operator fun component2() = QualifierApplicabilityType.values().filter(this::isApplicableTo)
}
object Empty : AnnotationTypeQualifierResolver {
override fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? = null
override fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? = null
}
}
class AnnotationTypeQualifierResolverImpl(storageManager: StorageManager) : AnnotationTypeQualifierResolver {
private val resolvedNicknames =
storageManager.createMemoizedFunctionWithNullableValues(this::computeTypeQualifierNickname)
@@ -76,14 +63,22 @@ class AnnotationTypeQualifierResolverImpl(storageManager: StorageManager) : Anno
return resolvedNicknames(classDescriptor)
}
override fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? {
fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? {
if (jsr305State.isIgnored()) {
return null
}
val annotationClass = annotationDescriptor.annotationClass ?: return null
if (annotationClass.isAnnotatedWithTypeQualifier) return annotationDescriptor
return resolveTypeQualifierNickname(annotationClass)
}
override fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
if (jsr305State.isIgnored()) {
return null
}
val typeQualifierDefaultAnnotatedClass =
annotationDescriptor.annotationClass?.takeIf { it.annotations.hasAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) }
?: return null
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
import org.jetbrains.kotlin.load.java.typeEnhancement.JavaTypeQualifiers
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifierWithMigrationStatus
import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
@@ -69,7 +70,7 @@ class JavaResolverComponents(
)
}
private typealias QualifierByApplicabilityType = EnumMap<AnnotationTypeQualifierResolver.QualifierApplicabilityType, NullabilityQualifier?>
private typealias QualifierByApplicabilityType = EnumMap<AnnotationTypeQualifierResolver.QualifierApplicabilityType, NullabilityQualifierWithMigrationStatus?>
class JavaTypeQualifiersByElementType(
internal val nullabilityQualifiers: QualifierByApplicabilityType
@@ -80,7 +81,7 @@ class JavaTypeQualifiersByElementType(
(
nullabilityQualifiers[applicabilityType]
?: nullabilityQualifiers[AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE]
)?.let { JavaTypeQualifiers(it, null, isNotNullTypeParameter = false) }
)?.let { JavaTypeQualifiers(it.qualifier, null, isNotNullTypeParameter = false, isNullabilityQualifierForWarning = it.isForWarningOnly) }
}
class LazyJavaResolverContext internal constructor(
@@ -121,9 +122,10 @@ fun LazyJavaResolverContext.computeNewDefaultTypeQualifiers(
?: QualifierByApplicabilityType(AnnotationTypeQualifierResolver.QualifierApplicabilityType::class.java)
var wasUpdate = false
val isForWarning = components.annotationTypeQualifierResolver.jsr305State.isWarning()
for ((nullability, applicableTo) in nullabilityQualifiersWithApplicability) {
for (applicabilityType in applicableTo) {
nullabilityQualifiersByType[applicabilityType] = nullability
nullabilityQualifiersByType[applicabilityType] = NullabilityQualifierWithMigrationStatus(nullability, isForWarning)
wasUpdate = true
}
}
@@ -140,9 +142,9 @@ private fun LazyJavaResolverContext.extractDefaultNullabilityQualifier(
components.annotationTypeQualifierResolver.resolveTypeQualifierDefaultAnnotation(annotationDescriptor)
?: return null
val nullability = components.signatureEnhancement.extractNullability(typeQualifier) ?: return null
val nullabilityQualifier = components.signatureEnhancement.extractNullability(typeQualifier)?.qualifier ?: return null
return NullabilityQualifierWithApplicability(nullability, applicability)
return NullabilityQualifierWithApplicability(nullabilityQualifier, applicability)
}
data class NullabilityQualifierWithApplicability(
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage
import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
@@ -56,7 +55,7 @@ class LazyJavaPackageFragment(
override val annotations =
// Do not resolve package annotations if JSR-305 is disabled
if (c.components.annotationTypeQualifierResolver == AnnotationTypeQualifierResolver.Empty) Annotations.EMPTY
if (c.components.annotationTypeQualifierResolver.jsr305State.isIgnored()) Annotations.EMPTY
else c.resolveAnnotations(jPackage)
internal fun getSubPackageFqNames(): List<FqName> = subPackages()
@@ -35,45 +35,56 @@ 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.firstArgumentValue
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.asFlexibleType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
data class NullabilityQualifierWithMigrationStatus(
val qualifier: NullabilityQualifier,
val isForWarningOnly: Boolean = false
)
class SignatureEnhancement(private val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver) {
fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifier? {
val annotationFqName = annotationDescriptor.fqName ?: return null
when (annotationFqName) {
in NULLABLE_ANNOTATIONS -> return NullabilityQualifier.NULLABLE
in NOT_NULL_ANNOTATIONS -> return NullabilityQualifier.NOT_NULL
}
val typeQualifier =
when {
annotationFqName == JAVAX_NONNULL_ANNOTATION -> annotationDescriptor
else -> annotationTypeQualifierResolver.resolveTypeQualifierAnnotation(annotationDescriptor)
?.takeIf { it.fqName == JAVAX_NONNULL_ANNOTATION }
} ?: return null
val enumEntryDescriptor =
typeQualifier.allValueArguments.values.singleOrNull()?.value
// if no argument is specified, use default value: NOT_NULL
?: return NullabilityQualifier.NOT_NULL
private fun AnnotationDescriptor.extractNullabilityTypeFromArgument(): NullabilityQualifierWithMigrationStatus? {
val enumEntryDescriptor = firstArgumentValue()
// if no argument is specified, use default value: NOT_NULL
?: return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
if (enumEntryDescriptor !is ClassDescriptor) return null
return when (enumEntryDescriptor.name.asString()) {
"ALWAYS" -> NullabilityQualifier.NOT_NULL
"MAYBE" -> NullabilityQualifier.NULLABLE
"ALWAYS" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
"MAYBE" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
else -> null
}
}
fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifierWithMigrationStatus? {
val annotationFqName = annotationDescriptor.fqName ?: return null
return when (annotationFqName) {
in NULLABLE_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
in NOT_NULL_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
JAVAX_NONNULL_ANNOTATION -> annotationDescriptor.extractNullabilityTypeFromArgument()
else -> {
val forWarning = annotationTypeQualifierResolver.jsr305State.isWarning()
annotationTypeQualifierResolver
.resolveTypeQualifierAnnotation(annotationDescriptor)
?.takeIf { it.fqName == JAVAX_NONNULL_ANNOTATION }
?.extractNullabilityTypeFromArgument()
?.copy(isForWarningOnly = forWarning)
}
}
}
fun <D : CallableMemberDescriptor> enhanceSignatures(c: LazyJavaResolverContext, platformSignatures: Collection<D>): Collection<D> {
return platformSignatures.map {
@@ -217,12 +228,17 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
fun <T: Any> uniqueNotNull(x: T?, y: T?) = if (x == null || y == null || x == y) x ?: y else null
val defaultTypeQualifier = defaultTopLevelQualifiers?.takeIf { isHeadTypeConstructor }
val nullability =
val nullabilityInfo =
composedAnnotation.extractNullability()
?: defaultTypeQualifier?.nullability
?: defaultTypeQualifier?.nullability?.let {
NullabilityQualifierWithMigrationStatus(
defaultTypeQualifier.nullability,
defaultTypeQualifier.isNullabilityQualifierForWarning
)
}
return JavaTypeQualifiers(
nullability,
nullabilityInfo?.qualifier,
uniqueNotNull(
READ_ONLY_ANNOTATIONS.ifPresent(
MutabilityQualifier.READ_ONLY
@@ -231,11 +247,12 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
MutabilityQualifier.MUTABLE
)
),
isNotNullTypeParameter = nullability == NullabilityQualifier.NOT_NULL && isTypeParameter()
isNotNullTypeParameter = nullabilityInfo?.qualifier == NullabilityQualifier.NOT_NULL && isTypeParameter(),
isNullabilityQualifierForWarning = nullabilityInfo?.isForWarningOnly == true
)
}
private fun Annotations.extractNullability(): NullabilityQualifier? =
private fun Annotations.extractNullability(): NullabilityQualifierWithMigrationStatus? =
this.firstNotNullResult(this@SignatureEnhancement::extractNullability)
private fun computeIndexedQualifiersForOverride(): (Int) -> JavaTypeQualifiers {
@@ -288,49 +305,57 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
fromSupertypes: Collection<KotlinType>, isCovariant: Boolean,
isHeadTypeConstructor: Boolean
): JavaTypeQualifiers {
val nullabilityFromSupertypes = fromSupertypes.mapNotNull { it.extractQualifiers().nullability }.toSet()
val mutabilityFromSupertypes = fromSupertypes.mapNotNull { it.extractQualifiers().mutability }.toSet()
val superQualifiers = fromSupertypes.map { it.extractQualifiers() }
val mutabilityFromSupertypes = superQualifiers.mapNotNull { it.mutability }.toSet()
val nullabilityFromSupertypes = superQualifiers.mapNotNull { it.nullability }.toSet()
val nullabilityFromSupertypesWithWarning = fromSupertypes
.mapNotNull { it.unwrapEnhancement().extractQualifiers().nullability }
.toSet()
.takeIf { it != nullabilityFromSupertypes }
val own = extractQualifiersFromAnnotations(isHeadTypeConstructor)
val isAnyNonNullTypeParameter = own.isNotNullTypeParameter || superQualifiers.any { it.isNotNullTypeParameter }
val isAnyNonNullTypeParameter = own.isNotNullTypeParameter || fromSupertypes.any { it.extractQualifiers().isNotNullTypeParameter }
fun createJavaTypeQualifiers(nullability: NullabilityQualifier?, mutability: MutabilityQualifier?): JavaTypeQualifiers {
fun createJavaTypeQualifiers(
nullability: NullabilityQualifier?,
mutability: MutabilityQualifier?,
forWarning: Boolean
): JavaTypeQualifiers {
if (!isAnyNonNullTypeParameter || nullability != NullabilityQualifier.NOT_NULL) {
return JavaTypeQualifiers(nullability, mutability, false)
return JavaTypeQualifiers(nullability, mutability, false, forWarning)
}
return JavaTypeQualifiers(
nullability, mutability,
isNotNullTypeParameter = true)
return JavaTypeQualifiers(nullability, mutability, true, forWarning)
}
if (isCovariant) {
fun <T : Any> Set<T>.selectCovariantly(low: T, high: T, own: T?): T? {
fun <T : Any> Set<T>.select(low: T, high: T, own: T?): T? {
if (isCovariant) {
val supertypeQualifier = if (low in this) low else if (high in this) high else null
return if (supertypeQualifier == low && own == high) null else own ?: supertypeQualifier
}
return createJavaTypeQualifiers(
nullabilityFromSupertypes.selectCovariantly(
NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, own.nullability
),
mutabilityFromSupertypes.selectCovariantly(
MutabilityQualifier.MUTABLE, MutabilityQualifier.READ_ONLY, own.mutability
)
)
// isInvariant
val effectiveSet = own?.let { (this + own).toSet() } ?: this
// if this set contains exactly one element, it is the qualifier everybody agrees upon,
// otherwise (no qualifiers, or multiple qualifiers), there's no single such qualifier
// and all qualifiers are discarded
return effectiveSet.singleOrNull()
}
else {
fun <T : Any> Set<T>.selectInvariantly(own: T?): T? {
val effectiveSet = own?.let { (this + own).toSet() } ?: this
// if this set contains exactly one element, it is the qualifier everybody agrees upon,
// otherwise (no qualifiers, or multiple qualifiers), there's no single such qualifier
// and all qualifiers are discarded
return effectiveSet.singleOrNull()
}
return createJavaTypeQualifiers(
nullabilityFromSupertypes.selectInvariantly(own.nullability),
mutabilityFromSupertypes.selectInvariantly(own.mutability)
)
val ownNullability = own.takeIf { !it.isNullabilityQualifierForWarning }?.nullability
val ownNullabilityForWarning = own.nullability
val nullability = nullabilityFromSupertypes.select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, ownNullability)
val mutability = mutabilityFromSupertypes.select(MutabilityQualifier.MUTABLE, MutabilityQualifier.READ_ONLY, own.mutability)
val canChange = ownNullabilityForWarning != ownNullability || nullabilityFromSupertypesWithWarning != null
if (nullability == null && canChange) {
val nullabilityWithWarning = (nullabilityFromSupertypesWithWarning ?: nullabilityFromSupertypes)
.select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, ownNullabilityForWarning)
return createJavaTypeQualifiers(nullabilityWithWarning, mutability, true)
}
return createJavaTypeQualifiers(nullability, mutability,nullability == null)
}
}
@@ -71,17 +71,15 @@ private fun UnwrappedType.enhancePossiblyFlexible(qualifiers: (Int) -> JavaTypeQ
}
val wereChanges = lowerResult.wereChanges || upperResult.wereChanges
val enhancement = lowerResult.type.getEnhancement() ?: upperResult.type.getEnhancement()
val type = if (!wereChanges) this@enhancePossiblyFlexible
else when {
this is RawTypeImpl -> RawTypeImpl(lowerResult.type, upperResult.type)
else -> KotlinTypeFactory.flexibleType(lowerResult.type, upperResult.type)
}.wrapEnhancement(enhancement)
Result(
if (wereChanges) {
if (this is RawTypeImpl) {
RawTypeImpl(lowerResult.type, upperResult.type)
}
else {
KotlinTypeFactory.flexibleType(lowerResult.type, upperResult.type)
}
}
else
this@enhancePossiblyFlexible,
type,
lowerResult.subtreeSize,
wereChanges
)
@@ -137,8 +135,11 @@ private fun SimpleType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers
enhancedNullability
)
val result = if (effectiveQualifiers.isNotNullTypeParameter) NotNullTypeParameter(enhancedType) else enhancedType
return SimpleResult(result, subtreeSize, wereChanges = true)
val enhancement = if (effectiveQualifiers.isNotNullTypeParameter) NotNullTypeParameter(enhancedType) else enhancedType
val nullabilityForWarning = enhancedNullabilityAnnotations != null && effectiveQualifiers.isNullabilityQualifierForWarning
val result = if (nullabilityForWarning) wrapEnhancement(enhancement) else enhancement
return SimpleResult(result as SimpleType, subtreeSize, wereChanges = true)
}
private fun List<Annotations>.compositeAnnotationsOrSingle() = when (size) {
@@ -225,8 +226,10 @@ internal class NotNullTypeParameter(override val delegate: SimpleType) : CustomT
return when (unwrappedType) {
is SimpleType -> unwrappedType.prepareReplacement()
is FlexibleType -> KotlinTypeFactory.flexibleType(unwrappedType.lowerBound.prepareReplacement(),
unwrappedType.upperBound.prepareReplacement())
is FlexibleType -> KotlinTypeFactory.flexibleType(
unwrappedType.lowerBound.prepareReplacement(),
unwrappedType.upperBound.prepareReplacement()
).wrapEnhancement(unwrappedType.getEnhancement())
else -> error("Incorrect type: $unwrappedType")
}
}
@@ -29,7 +29,8 @@ enum class MutabilityQualifier {
class JavaTypeQualifiers internal constructor(
val nullability: NullabilityQualifier?,
val mutability: MutabilityQualifier?,
internal val isNotNullTypeParameter: Boolean
internal val isNotNullTypeParameter: Boolean,
internal val isNullabilityQualifierForWarning: Boolean = false
) {
companion object {
val NONE = JavaTypeQualifiers(null, null, false)
@@ -47,7 +47,8 @@ internal class UnsafeVarianceTypeSubstitution(builtIns: KotlinBuiltIns) : TypeSu
is FlexibleType ->
KotlinTypeFactory.flexibleType(
lowerBound.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 0)),
upperBound.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 1)))
upperBound.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 1))
).inheritEnhancement(this)
is SimpleType -> annotatePartsWithUnsafeVariance(unsafeVariancePaths)
}
}