From b2dff10e32cde7499a916314458dda352e7116cd Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Fri, 11 Jun 2021 10:28:51 +0300 Subject: [PATCH] Implement caching states for nullability annotations --- .../JavaTypeEnhancementStateParser.kt | 9 +- .../JvmForeignAnnotationsConfigurator.kt | 12 ++- .../AllNullabilityAnnotationsAreSetUp.kt | 13 ++- .../java/JavaNullabilityAnnotationSettings.kt | 99 ++++++++++++------- 4 files changed, 81 insertions(+), 52 deletions(-) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/JavaTypeEnhancementStateParser.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/JavaTypeEnhancementStateParser.kt index 6b8a625ddcb..4b161e351f7 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/JavaTypeEnhancementStateParser.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/JavaTypeEnhancementStateParser.kt @@ -65,8 +65,9 @@ class JavaTypeEnhancementStateParser( } } - private fun parseNullabilityAnnotationReportLevels(nullabilityAnnotations: Array?): Map { - if (nullabilityAnnotations.isNullOrEmpty()) return emptyMap() + private fun parseNullabilityAnnotationReportLevels(nullabilityAnnotations: Array?): NullabilityAnnotationStates { + if (nullabilityAnnotations.isNullOrEmpty()) + return NullabilityAnnotationStates.EMPTY val annotationsWithReportLevels = mutableMapOf() 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() } } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JvmForeignAnnotationsConfigurator.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JvmForeignAnnotationsConfigurator.kt index 301ec87ae2d..da7c67a0092 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JvmForeignAnnotationsConfigurator.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JvmForeignAnnotationsConfigurator.kt @@ -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 { - 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 { + 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( diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AllNullabilityAnnotationsAreSetUp.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AllNullabilityAnnotationsAreSetUp.kt index 492da1d34dc..1499c7f834f 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AllNullabilityAnnotationsAreSetUp.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AllNullabilityAnnotationsAreSetUp.kt @@ -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" diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JavaNullabilityAnnotationSettings.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JavaNullabilityAnnotationSettings.kt index 39663aed6ab..31c643b1de6 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JavaNullabilityAnnotationSettings.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JavaNullabilityAnnotationSettings.kt @@ -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 = 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, + annotation: FqName, + configuredReportLevels: NullabilityAnnotationStates, 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 { + operator fun get(fqName: FqName): T? + fun singleOrNull(): Pair? + + companion object { + val EMPTY: NullabilityAnnotationStates = NullabilityAnnotationStatesImpl(emptyMap()) + } +} + +class NullabilityAnnotationStatesImpl(val states: Map) : NullabilityAnnotationStates { + private val cache = mutableMapOf() + + 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() +}