Implement caching states for nullability annotations
This commit is contained in:
+5
-4
@@ -65,8 +65,9 @@ class JavaTypeEnhancementStateParser(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseNullabilityAnnotationReportLevels(nullabilityAnnotations: Array<String>?): Map<FqName, ReportLevel> {
|
||||
if (nullabilityAnnotations.isNullOrEmpty()) return emptyMap()
|
||||
private fun parseNullabilityAnnotationReportLevels(nullabilityAnnotations: Array<String>?): NullabilityAnnotationStates<out ReportLevel> {
|
||||
if (nullabilityAnnotations.isNullOrEmpty())
|
||||
return NullabilityAnnotationStates.EMPTY
|
||||
|
||||
val annotationsWithReportLevels = mutableMapOf<FqName, ReportLevel>()
|
||||
val compilerOption = "-Xnullability-annotations"
|
||||
@@ -87,7 +88,7 @@ class JavaTypeEnhancementStateParser(
|
||||
}
|
||||
}
|
||||
|
||||
return annotationsWithReportLevels
|
||||
return NullabilityAnnotationStatesImpl(annotationsWithReportLevels)
|
||||
}
|
||||
|
||||
private fun parseJspecifyReportLevel(
|
||||
@@ -198,6 +199,6 @@ class JavaTypeEnhancementStateParser(
|
||||
private val DEFAULT = JavaTypeEnhancementStateParser(MessageCollector.NONE, KotlinVersion.CURRENT)
|
||||
|
||||
fun parsePlainNullabilityAnnotationReportLevels(nullabilityAnnotations: String) =
|
||||
DEFAULT.parseNullabilityAnnotationReportLevels(arrayOf(nullabilityAnnotations)).entries.singleOrNull()?.toPair()
|
||||
DEFAULT.parseNullabilityAnnotationReportLevels(arrayOf(nullabilityAnnotations)).singleOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -56,12 +56,14 @@ open class JvmForeignAnnotationsConfigurator(testServices: TestServices) : Envir
|
||||
val state = ReportLevel.findByDescription(stateDescription) ?: return@mapNotNull null
|
||||
FqName(name) to state
|
||||
}.toMap()
|
||||
val configuredReportLevels = buildMap<FqName, ReportLevel> {
|
||||
directives.singleOrZeroValue(JSPECIFY_STATE)?.let { put(JSPECIFY_ANNOTATIONS_PACKAGE, it) }
|
||||
for ((fqname, reportLevel) in directives[ForeignAnnotationsDirectives.NULLABILITY_ANNOTATIONS]) {
|
||||
put(fqname, reportLevel)
|
||||
val configuredReportLevels = NullabilityAnnotationStatesImpl(
|
||||
buildMap<FqName, ReportLevel> {
|
||||
directives.singleOrZeroValue(JSPECIFY_STATE)?.let { put(JSPECIFY_ANNOTATIONS_PACKAGE, it) }
|
||||
for ((fqname, reportLevel) in directives[ForeignAnnotationsDirectives.NULLABILITY_ANNOTATIONS]) {
|
||||
put(fqname, reportLevel)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return mapOf(
|
||||
JvmAnalysisFlags.javaTypeEnhancementState to JavaTypeEnhancementState(
|
||||
|
||||
+8
-5
@@ -6,25 +6,28 @@
|
||||
package org.jetbrains.kotlin.jvm.compiler
|
||||
|
||||
import org.jetbrains.kotlin.load.java.NULLABILITY_ANNOTATIONS
|
||||
import org.jetbrains.kotlin.load.java.nullabilityAnnotationSettings
|
||||
import org.jetbrains.kotlin.load.java.NULLABILITY_ANNOTATION_SETTINGS
|
||||
import org.jetbrains.kotlin.load.java.NullabilityAnnotationStatesImpl
|
||||
import org.jetbrains.kotlin.name.isChildOf
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
|
||||
class AllNullabilityAnnotationsAreSetUpTest : KtUsefulTestCase() {
|
||||
fun testAllAnnotationsAreSetUp() {
|
||||
assert(NULLABILITY_ANNOTATIONS.all { annotation -> nullabilityAnnotationSettings.keys.any { annotation.isChildOf(it) } }) {
|
||||
val annotationsRawMap = (NULLABILITY_ANNOTATION_SETTINGS as NullabilityAnnotationStatesImpl).states
|
||||
assert(NULLABILITY_ANNOTATIONS.all { annotation -> annotationsRawMap.keys.any { annotation.isChildOf(it) } }) {
|
||||
val missedAnnotations = NULLABILITY_ANNOTATIONS.filter { annotation ->
|
||||
nullabilityAnnotationSettings.keys.none { annotation.isChildOf(it) }
|
||||
annotationsRawMap.keys.none { annotation.isChildOf(it) }
|
||||
}
|
||||
"Not all nullability annotations are presented in `nullabilityAnnotationSettings`. Missed annotations: $missedAnnotations"
|
||||
}
|
||||
}
|
||||
|
||||
fun testAllSetUpAnnotationsArePresent() {
|
||||
assert(nullabilityAnnotationSettings.keys.all { annotationsPackage ->
|
||||
val annotationsRawMap = (NULLABILITY_ANNOTATION_SETTINGS as NullabilityAnnotationStatesImpl).states
|
||||
assert(annotationsRawMap.keys.all { annotationsPackage ->
|
||||
NULLABILITY_ANNOTATIONS.any { it.isChildOf(annotationsPackage) }
|
||||
}) {
|
||||
val missedAnnotations = nullabilityAnnotationSettings.keys.filter { annotationsPackage ->
|
||||
val missedAnnotations = annotationsRawMap.keys.filter { annotationsPackage ->
|
||||
NULLABILITY_ANNOTATIONS.none { it.isChildOf(annotationsPackage) }
|
||||
}
|
||||
"Not all set up nullability annotations are presented in `NULLABILITY_ANNOTATIONS`. Missed annotations: $missedAnnotations"
|
||||
|
||||
+61
-38
@@ -11,37 +11,39 @@ import org.jetbrains.kotlin.name.findValueForMostSpecificFqname
|
||||
val JSPECIFY_ANNOTATIONS_PACKAGE = FqName("org.jspecify.nullness")
|
||||
val CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE = FqName("org.checkerframework.checker.nullness.compatqual")
|
||||
|
||||
val nullabilityAnnotationSettings = mapOf(
|
||||
FqName("org.jetbrains.annotations") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("androidx.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("android.support.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("android.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("com.android.annotations") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("org.eclipse.jdt.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("org.checkerframework.checker.nullness.qual") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("javax.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("edu.umd.cs.findbugs.annotations") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("io.reactivex.annotations") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("androidx.annotation.RecentlyNullable") to JavaNullabilityAnnotationsStatus(
|
||||
reportLevelBefore = ReportLevel.WARN,
|
||||
sinceVersion = null
|
||||
),
|
||||
FqName("androidx.annotation.RecentlyNonNull") to JavaNullabilityAnnotationsStatus(
|
||||
reportLevelBefore = ReportLevel.WARN,
|
||||
sinceVersion = null
|
||||
),
|
||||
FqName("lombok") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
JSPECIFY_ANNOTATIONS_PACKAGE to JavaNullabilityAnnotationsStatus(
|
||||
reportLevelBefore = ReportLevel.WARN,
|
||||
sinceVersion = KotlinVersion(1, 6),
|
||||
reportLevelAfter = ReportLevel.STRICT
|
||||
),
|
||||
FqName("io.reactivex.rxjava3.annotations") to JavaNullabilityAnnotationsStatus(
|
||||
reportLevelBefore = ReportLevel.WARN,
|
||||
sinceVersion = KotlinVersion(1, 7),
|
||||
reportLevelAfter = ReportLevel.STRICT
|
||||
),
|
||||
val NULLABILITY_ANNOTATION_SETTINGS: NullabilityAnnotationStates<JavaNullabilityAnnotationsStatus> = NullabilityAnnotationStatesImpl(
|
||||
mapOf(
|
||||
FqName("org.jetbrains.annotations") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("androidx.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("android.support.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("android.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("com.android.annotations") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("org.eclipse.jdt.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("org.checkerframework.checker.nullness.qual") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("javax.annotation") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("edu.umd.cs.findbugs.annotations") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("io.reactivex.annotations") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
FqName("androidx.annotation.RecentlyNullable") to JavaNullabilityAnnotationsStatus(
|
||||
reportLevelBefore = ReportLevel.WARN,
|
||||
sinceVersion = null
|
||||
),
|
||||
FqName("androidx.annotation.RecentlyNonNull") to JavaNullabilityAnnotationsStatus(
|
||||
reportLevelBefore = ReportLevel.WARN,
|
||||
sinceVersion = null
|
||||
),
|
||||
FqName("lombok") to JavaNullabilityAnnotationsStatus.DEFAULT,
|
||||
JSPECIFY_ANNOTATIONS_PACKAGE to JavaNullabilityAnnotationsStatus(
|
||||
reportLevelBefore = ReportLevel.WARN,
|
||||
sinceVersion = KotlinVersion(1, 6),
|
||||
reportLevelAfter = ReportLevel.STRICT
|
||||
),
|
||||
FqName("io.reactivex.rxjava3.annotations") to JavaNullabilityAnnotationsStatus(
|
||||
reportLevelBefore = ReportLevel.WARN,
|
||||
sinceVersion = KotlinVersion(1, 7),
|
||||
reportLevelAfter = ReportLevel.STRICT
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
private val JSR_305_DEFAULT_SETTINGS = JavaNullabilityAnnotationsStatus(
|
||||
@@ -63,21 +65,42 @@ fun getDefaultJsr305Settings(configuredKotlinVersion: KotlinVersion = KotlinVers
|
||||
fun getDefaultMigrationJsr305ReportLevelForGivenGlobal(globalReportLevel: ReportLevel) =
|
||||
if (globalReportLevel == ReportLevel.WARN) null else globalReportLevel
|
||||
|
||||
fun getDefaultReportLevelForAnnotation(annotationFqName: FqName) = getReportLevelForAnnotation(annotationFqName, emptyMap())
|
||||
fun getDefaultReportLevelForAnnotation(annotationFqName: FqName) =
|
||||
getReportLevelForAnnotation(annotationFqName, NullabilityAnnotationStates.EMPTY)
|
||||
|
||||
fun getReportLevelForAnnotation(
|
||||
annotation: FqName, configuredReportLevels: Map<FqName, ReportLevel>,
|
||||
annotation: FqName,
|
||||
configuredReportLevels: NullabilityAnnotationStates<out ReportLevel>,
|
||||
configuredKotlinVersion: KotlinVersion = KotlinVersion.CURRENT
|
||||
): ReportLevel {
|
||||
annotation.findValueForMostSpecificFqname(configuredReportLevels)?.let { return it }
|
||||
configuredReportLevels[annotation]?.let { return it }
|
||||
|
||||
val defaultReportLevel = annotation.findValueForMostSpecificFqname(nullabilityAnnotationSettings) ?: return ReportLevel.IGNORE
|
||||
val defaultStatus = NULLABILITY_ANNOTATION_SETTINGS[annotation] ?: return ReportLevel.IGNORE
|
||||
|
||||
return if (defaultReportLevel.sinceVersion != null && defaultReportLevel.sinceVersion <= configuredKotlinVersion) {
|
||||
defaultReportLevel.reportLevelAfter
|
||||
return if (defaultStatus.sinceVersion != null && defaultStatus.sinceVersion <= configuredKotlinVersion) {
|
||||
defaultStatus.reportLevelAfter
|
||||
} else {
|
||||
defaultReportLevel.reportLevelBefore
|
||||
defaultStatus.reportLevelBefore
|
||||
}
|
||||
}
|
||||
|
||||
interface NullabilityAnnotationStates<T : Any> {
|
||||
operator fun get(fqName: FqName): T?
|
||||
|
||||
fun singleOrNull(): Pair<FqName, T>?
|
||||
|
||||
companion object {
|
||||
val EMPTY: NullabilityAnnotationStates<Nothing> = NullabilityAnnotationStatesImpl(emptyMap())
|
||||
}
|
||||
}
|
||||
|
||||
class NullabilityAnnotationStatesImpl<T : Any>(val states: Map<FqName, T>) : NullabilityAnnotationStates<T> {
|
||||
private val cache = mutableMapOf<FqName, T?>()
|
||||
|
||||
override operator fun get(fqName: FqName): T? {
|
||||
if (fqName in cache) return cache[fqName]
|
||||
return fqName.findValueForMostSpecificFqname(states).also { cache[fqName] = it }
|
||||
}
|
||||
|
||||
override fun singleOrNull() = states.entries.singleOrNull()?.toPair()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user