Depend on passed language version explicitly to compute nullability annotation settings

This commit is contained in:
Victor Petukhov
2021-06-11 10:25:29 +03:00
parent 18384788a4
commit a82772f31a
16 changed files with 102 additions and 47 deletions
@@ -365,7 +365,7 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
) )
var extendedCompilerChecks: Boolean by FreezableVar(false) var extendedCompilerChecks: Boolean by FreezableVar(false)
open fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> { open fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> {
return HashMap<AnalysisFlag<*>, Any>().apply { return HashMap<AnalysisFlag<*>, Any>().apply {
put(AnalysisFlags.skipMetadataVersionCheck, skipMetadataVersionCheck) put(AnalysisFlags.skipMetadataVersionCheck, skipMetadataVersionCheck)
put(AnalysisFlags.skipPrereleaseCheck, skipPrereleaseCheck || skipMetadataVersionCheck) put(AnalysisFlags.skipPrereleaseCheck, skipPrereleaseCheck || skipMetadataVersionCheck)
@@ -516,7 +516,7 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
val languageVersionSettings = LanguageVersionSettingsImpl( val languageVersionSettings = LanguageVersionSettingsImpl(
languageVersion, languageVersion,
ApiVersion.createByLanguageVersion(apiVersion), ApiVersion.createByLanguageVersion(apiVersion),
configureAnalysisFlags(collector), configureAnalysisFlags(collector, languageVersion),
configureLanguageFeatures(collector) configureLanguageFeatures(collector)
) )
@@ -22,34 +22,45 @@ import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isSubpackageOf import org.jetbrains.kotlin.name.isSubpackageOf
class JavaTypeEnhancementStateParser(private val collector: MessageCollector) { class JavaTypeEnhancementStateParser(
private val collector: MessageCollector,
private val kotlinVersion: KotlinVersion
) {
fun parse( fun parse(
jsr305Args: Array<String>?, jsr305Args: Array<String>?,
supportCompatqualCheckerFrameworkAnnotations: String?, supportCompatqualCheckerFrameworkAnnotations: String?,
jspecifyState: String?, jspecifyState: String?,
nullabilityAnnotations: Array<String>?, nullabilityAnnotations: Array<String>?
): JavaTypeEnhancementState { ): JavaTypeEnhancementState {
val nullabilityAnnotationReportLevels = parseNullabilityAnnotationReportLevels(nullabilityAnnotations)
val compatqualCheckerFrameworkAnnotationsReportLevel = when (supportCompatqualCheckerFrameworkAnnotations) { val compatqualCheckerFrameworkAnnotationsReportLevel = when (supportCompatqualCheckerFrameworkAnnotations) {
"enable" -> ReportLevel.STRICT "enable" -> ReportLevel.STRICT
"disable" -> ReportLevel.IGNORE "disable" -> ReportLevel.IGNORE
null -> getDefaultReportLevelForAnnotation(CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE) null -> getReportLevelForAnnotation(
CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE,
nullabilityAnnotationReportLevels,
kotlinVersion
)
else -> { else -> {
collector.report( collector.report(
CompilerMessageSeverity.ERROR, CompilerMessageSeverity.ERROR,
"Unrecognized -Xsupport-compatqual-checker-framework-annotations option: $supportCompatqualCheckerFrameworkAnnotations. Possible values are 'enable'/'disable'" "Unrecognized -Xsupport-compatqual-checker-framework-annotations option: $supportCompatqualCheckerFrameworkAnnotations. Possible values are 'enable'/'disable'"
) )
getDefaultReportLevelForAnnotation(CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE) getReportLevelForAnnotation(
CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE,
nullabilityAnnotationReportLevels,
kotlinVersion
)
} }
} }
val jsr305Settings = parseJsr305State(jsr305Args) val jsr305Settings = parseJsr305State(jsr305Args)
val jspecifyReportLevel = parseJspecifyReportLevel(jspecifyState) val jspecifyReportLevel = parseJspecifyReportLevel(jspecifyState, nullabilityAnnotationReportLevels)
val nullabilityAnnotationReportLevels = parseNullabilityAnnotationReportLevels(nullabilityAnnotations)
return JavaTypeEnhancementState(jsr305Settings) { return JavaTypeEnhancementState(jsr305Settings) {
when { when {
it.isSubpackageOf(JSPECIFY_ANNOTATIONS_PACKAGE) -> jspecifyReportLevel it.isSubpackageOf(JSPECIFY_ANNOTATIONS_PACKAGE) -> jspecifyReportLevel
it.isSubpackageOf(CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE) -> compatqualCheckerFrameworkAnnotationsReportLevel it.isSubpackageOf(CHECKER_FRAMEWORK_COMPATQUAL_ANNOTATIONS_PACKAGE) -> compatqualCheckerFrameworkAnnotationsReportLevel
else -> getReportLevelForAnnotation(it, nullabilityAnnotationReportLevels) else -> getReportLevelForAnnotation(it, nullabilityAnnotationReportLevels, kotlinVersion)
} }
} }
} }
@@ -79,9 +90,12 @@ class JavaTypeEnhancementStateParser(private val collector: MessageCollector) {
return annotationsWithReportLevels return annotationsWithReportLevels
} }
private fun parseJspecifyReportLevel(jspecifyState: String?): ReportLevel { private fun parseJspecifyReportLevel(
jspecifyState: String?,
nullabilityAnnotationReportLevels: NullabilityAnnotationStates<out ReportLevel>
): ReportLevel {
if (jspecifyState == null) if (jspecifyState == null)
return getDefaultReportLevelForAnnotation(JSPECIFY_ANNOTATIONS_PACKAGE) return getReportLevelForAnnotation(JSPECIFY_ANNOTATIONS_PACKAGE, nullabilityAnnotationReportLevels, kotlinVersion)
val reportLevel = ReportLevel.findByDescription(jspecifyState) val reportLevel = ReportLevel.findByDescription(jspecifyState)
@@ -90,7 +104,7 @@ class JavaTypeEnhancementStateParser(private val collector: MessageCollector) {
CompilerMessageSeverity.ERROR, CompilerMessageSeverity.ERROR,
"Unrecognized -Xjspecify-annotations option: $jspecifyState. Possible values are 'disable'/'warn'/'strict'" "Unrecognized -Xjspecify-annotations option: $jspecifyState. Possible values are 'disable'/'warn'/'strict'"
) )
return getDefaultReportLevelForAnnotation(JSPECIFY_ANNOTATIONS_PACKAGE) return getReportLevelForAnnotation(JSPECIFY_ANNOTATIONS_PACKAGE, nullabilityAnnotationReportLevels, kotlinVersion)
} }
return reportLevel return reportLevel
@@ -101,7 +115,7 @@ class JavaTypeEnhancementStateParser(private val collector: MessageCollector) {
var migration: ReportLevel? = null var migration: ReportLevel? = null
val userDefined = mutableMapOf<FqName, ReportLevel>() val userDefined = mutableMapOf<FqName, ReportLevel>()
val compilerOption = "-Xjsr305" val compilerOption = "-Xjsr305"
val defaultSettings = Jsr305Settings.DEFAULT val defaultSettings = getDefaultJsr305Settings(kotlinVersion)
fun parseJsr305UnderMigration(item: String): ReportLevel? { fun parseJsr305UnderMigration(item: String): ReportLevel? {
val rawState = item.split(":").takeIf { it.size == 2 }?.get(1) val rawState = item.split(":").takeIf { it.size == 2 }?.get(1)
@@ -181,7 +195,7 @@ class JavaTypeEnhancementStateParser(private val collector: MessageCollector) {
} }
companion object { companion object {
private val DEFAULT = JavaTypeEnhancementStateParser(MessageCollector.NONE) private val DEFAULT = JavaTypeEnhancementStateParser(MessageCollector.NONE, KotlinVersion.CURRENT)
fun parsePlainNullabilityAnnotationReportLevels(nullabilityAnnotations: String) = fun parsePlainNullabilityAnnotationReportLevels(nullabilityAnnotations: String) =
DEFAULT.parseNullabilityAnnotationReportLevels(arrayOf(nullabilityAnnotations)).entries.singleOrNull()?.toPair() DEFAULT.parseNullabilityAnnotationReportLevels(arrayOf(nullabilityAnnotations)).entries.singleOrNull()?.toPair()
@@ -499,15 +499,11 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
) )
var typeEnhancementImprovementsInStrictMode: Boolean by FreezableVar(false) var typeEnhancementImprovementsInStrictMode: Boolean by FreezableVar(false)
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> { override fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> {
val result = super.configureAnalysisFlags(collector) val result = super.configureAnalysisFlags(collector, languageVersion)
result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics
result[JvmAnalysisFlags.javaTypeEnhancementState] = JavaTypeEnhancementStateParser(collector).parse( result[JvmAnalysisFlags.javaTypeEnhancementState] = JavaTypeEnhancementStateParser(collector, languageVersion.toKotlinVersion())
jsr305, .parse(jsr305, supportCompatqualCheckerFrameworkAnnotations, jspecifyAnnotations, nullabilityAnnotations)
supportCompatqualCheckerFrameworkAnnotations,
jspecifyAnnotations,
nullabilityAnnotations
)
result[AnalysisFlags.ignoreDataFlowInAssert] = JVMAssertionsMode.fromString(assertionsMode) != JVMAssertionsMode.LEGACY result[AnalysisFlags.ignoreDataFlowInAssert] = JVMAssertionsMode.fromString(assertionsMode) != JVMAssertionsMode.LEGACY
JvmDefaultMode.fromStringOrNull(jvmDefault)?.let { JvmDefaultMode.fromStringOrNull(jvmDefault)?.let {
result[JvmAnalysisFlags.jvmDefaultMode] = it result[JvmAnalysisFlags.jvmDefaultMode] = it
@@ -83,7 +83,7 @@ class LanguageVersionSettingsBuilder {
analysisFlags.forEach { withFlag(it.first, it.second) } analysisFlags.forEach { withFlag(it.first, it.second) }
environmentConfigurators.forEach { environmentConfigurators.forEach {
it.provideAdditionalAnalysisFlags(directives).entries.forEach { (flag, value) -> it.provideAdditionalAnalysisFlags(directives, languageVersion).entries.forEach { (flag, value) ->
withFlag(flag, value) withFlag(flag, value)
} }
} }
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.AnalysisFlag
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.test.directives.model.* import org.jetbrains.kotlin.test.directives.model.*
import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.model.TestModule
@@ -36,7 +37,10 @@ abstract class EnvironmentConfigurator(protected val testServices: TestServices)
open fun DirectiveToConfigurationKeyExtractor.provideConfigurationKeys() {} open fun DirectiveToConfigurationKeyExtractor.provideConfigurationKeys() {}
open fun provideAdditionalAnalysisFlags(directives: RegisteredDirectives): Map<AnalysisFlag<*>, Any?> { open fun provideAdditionalAnalysisFlags(
directives: RegisteredDirectives,
languageVersion: LanguageVersion
): Map<AnalysisFlag<*>, Any?> {
return emptyMap() return emptyMap()
} }
+7
View File
@@ -0,0 +1,7 @@
-language-version
1.5
$TESTDATA_DIR$/jspecifyUsage.kt
$TESTDATA_DIR$/jspecify
$FOREIGN_ANNOTATIONS_DIR$
-d
$TEMP_DIR$
+7
View File
@@ -0,0 +1,7 @@
compiler/testData/cli/jvm/jspecifyUsage.kt:2:11: warning: type mismatch: inferred type is Nothing? but String was expected
a.foo(null)
^
compiler/testData/cli/jvm/jspecifyUsage.kt:3:5: warning: unsafe use of a nullable receiver of type String?
a.bar().hashCode()
^
OK
+7
View File
@@ -0,0 +1,7 @@
-language-version
1.6
$TESTDATA_DIR$/jspecifyUsage.kt
$TESTDATA_DIR$/jspecify
$FOREIGN_ANNOTATIONS_DIR$
-d
$TEMP_DIR$
+5
View File
@@ -0,0 +1,5 @@
warning: language version 1.6 is experimental, there are no backwards compatibility guarantees for new language and library features
compiler/testData/cli/jvm/jspecifyUsage.kt:2:11: error: null can not be a value of a non-null type String
a.foo(null)
^
COMPILATION_ERROR
@@ -9,9 +9,7 @@ import org.jetbrains.kotlin.cli.jvm.addModularRootIfNotNull
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JvmAnalysisFlags
import org.jetbrains.kotlin.load.java.* import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.test.MockLibraryUtil import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.test.TestJavacVersion import org.jetbrains.kotlin.test.TestJavacVersion
@@ -46,8 +44,11 @@ open class JvmForeignAnnotationsConfigurator(testServices: TestServices) : Envir
get() = listOf(ForeignAnnotationsDirectives) get() = listOf(ForeignAnnotationsDirectives)
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
override fun provideAdditionalAnalysisFlags(directives: RegisteredDirectives): Map<AnalysisFlag<*>, Any?> { override fun provideAdditionalAnalysisFlags(
val defaultJsr305Settings = Jsr305Settings.DEFAULT directives: RegisteredDirectives,
languageVersion: LanguageVersion
): Map<AnalysisFlag<*>, Any?> {
val defaultJsr305Settings = getDefaultJsr305Settings(languageVersion.toKotlinVersion())
val globalState = directives.singleOrZeroValue(JSR305_GLOBAL_REPORT) ?: defaultJsr305Settings.globalLevel val globalState = directives.singleOrZeroValue(JSR305_GLOBAL_REPORT) ?: defaultJsr305Settings.globalLevel
val migrationState = directives.singleOrZeroValue(JSR305_MIGRATION_REPORT) ?: defaultJsr305Settings.migrationLevel val migrationState = directives.singleOrZeroValue(JSR305_MIGRATION_REPORT) ?: defaultJsr305Settings.migrationLevel
val userAnnotationsState = directives[JSR305_SPECIAL_REPORT].mapNotNull { val userAnnotationsState = directives[JSR305_SPECIAL_REPORT].mapNotNull {
@@ -446,6 +446,16 @@ public class CliTestGenerated extends AbstractCliTest {
runTest("compiler/testData/cli/jvm/jdkPathDoesNotExist.args"); runTest("compiler/testData/cli/jvm/jdkPathDoesNotExist.args");
} }
@TestMetadata("jspecifyByLv15.args")
public void testJspecifyByLv15() throws Exception {
runTest("compiler/testData/cli/jvm/jspecifyByLv15.args");
}
@TestMetadata("jspecifyByLv16.args")
public void testJspecifyByLv16() throws Exception {
runTest("compiler/testData/cli/jvm/jspecifyByLv16.args");
}
@TestMetadata("jspecifyDefault.args") @TestMetadata("jspecifyDefault.args")
public void testJspecifyDefault() throws Exception { public void testJspecifyDefault() throws Exception {
runTest("compiler/testData/cli/jvm/jspecifyDefault.args"); runTest("compiler/testData/cli/jvm/jspecifyDefault.args");
@@ -359,6 +359,8 @@ enum class LanguageVersion(val major: Int, val minor: Int) : DescriptionAware {
fun LanguageVersion.isStableOrReadyForPreview(): Boolean = fun LanguageVersion.isStableOrReadyForPreview(): Boolean =
isStable || this == KOTLIN_1_5 isStable || this == KOTLIN_1_5
fun LanguageVersion.toKotlinVersion() = KotlinVersion(major, minor)
interface LanguageVersionSettings { interface LanguageVersionSettings {
fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State
@@ -44,17 +44,18 @@ val nullabilityAnnotationSettings = mapOf(
), ),
) )
private val jsr305Settings = JavaNullabilityAnnotationsStatus( private val JSR_305_DEFAULT_SETTINGS = JavaNullabilityAnnotationsStatus(
reportLevelBefore = ReportLevel.WARN, reportLevelBefore = ReportLevel.WARN,
sinceVersion = null sinceVersion = null
) )
fun getDefaultJsr305Settings(): Jsr305Settings { fun getDefaultJsr305Settings(configuredKotlinVersion: KotlinVersion = KotlinVersion.CURRENT): Jsr305Settings {
val globalReportLevel = if (jsr305Settings.sinceVersion != null && jsr305Settings.sinceVersion <= KotlinVersion.CURRENT) { val globalReportLevel =
jsr305Settings.reportLevelAfter if (JSR_305_DEFAULT_SETTINGS.sinceVersion != null && JSR_305_DEFAULT_SETTINGS.sinceVersion <= configuredKotlinVersion) {
} else { JSR_305_DEFAULT_SETTINGS.reportLevelAfter
jsr305Settings.reportLevelBefore } else {
} JSR_305_DEFAULT_SETTINGS.reportLevelBefore
}
val migrationLevel = getDefaultMigrationJsr305ReportLevelForGivenGlobal(globalReportLevel) val migrationLevel = getDefaultMigrationJsr305ReportLevelForGivenGlobal(globalReportLevel)
return Jsr305Settings(globalReportLevel, migrationLevel) return Jsr305Settings(globalReportLevel, migrationLevel)
} }
@@ -64,12 +65,15 @@ fun getDefaultMigrationJsr305ReportLevelForGivenGlobal(globalReportLevel: Report
fun getDefaultReportLevelForAnnotation(annotationFqName: FqName) = getReportLevelForAnnotation(annotationFqName, emptyMap()) fun getDefaultReportLevelForAnnotation(annotationFqName: FqName) = getReportLevelForAnnotation(annotationFqName, emptyMap())
fun getReportLevelForAnnotation(annotation: FqName, configuredReportLevels: Map<FqName, ReportLevel>): ReportLevel { fun getReportLevelForAnnotation(
annotation: FqName, configuredReportLevels: Map<FqName, ReportLevel>,
configuredKotlinVersion: KotlinVersion = KotlinVersion.CURRENT
): ReportLevel {
annotation.findValueForMostSpecificFqname(configuredReportLevels)?.let { return it } annotation.findValueForMostSpecificFqname(configuredReportLevels)?.let { return it }
val defaultReportLevel = annotation.findValueForMostSpecificFqname(nullabilityAnnotationSettings) ?: return ReportLevel.IGNORE val defaultReportLevel = annotation.findValueForMostSpecificFqname(nullabilityAnnotationSettings) ?: return ReportLevel.IGNORE
return if (defaultReportLevel.sinceVersion != null && defaultReportLevel.sinceVersion <= KotlinVersion.CURRENT) { return if (defaultReportLevel.sinceVersion != null && defaultReportLevel.sinceVersion <= configuredKotlinVersion) {
defaultReportLevel.reportLevelAfter defaultReportLevel.reportLevelAfter
} else { } else {
defaultReportLevel.reportLevelBefore defaultReportLevel.reportLevelBefore
@@ -8,12 +8,12 @@ package org.jetbrains.kotlin.load.java
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
class JavaTypeEnhancementState( class JavaTypeEnhancementState(
val jsr305: Jsr305Settings = Jsr305Settings.DEFAULT, val jsr305: Jsr305Settings,
val getReportLevelForAnnotation: (FqName) -> ReportLevel = ::getDefaultReportLevelForAnnotation val getReportLevelForAnnotation: (FqName) -> ReportLevel
) { ) {
val disabledDefaultAnnotations = jsr305.isDisabled || getReportLevelForAnnotation(JSPECIFY_ANNOTATIONS_PACKAGE) == ReportLevel.IGNORE val disabledDefaultAnnotations = jsr305.isDisabled || getReportLevelForAnnotation(JSPECIFY_ANNOTATIONS_PACKAGE) == ReportLevel.IGNORE
companion object { companion object {
val DEFAULT = JavaTypeEnhancementState() val DEFAULT = JavaTypeEnhancementState(getDefaultJsr305Settings(), ::getDefaultReportLevelForAnnotation)
} }
} }
@@ -12,10 +12,6 @@ data class Jsr305Settings(
val migrationLevel: ReportLevel? = null, val migrationLevel: ReportLevel? = null,
val userDefinedLevelForSpecificAnnotation: Map<FqName, ReportLevel> = emptyMap() val userDefinedLevelForSpecificAnnotation: Map<FqName, ReportLevel> = emptyMap()
) { ) {
companion object {
val DEFAULT = getDefaultJsr305Settings()
}
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
val description by lazy { val description by lazy {
buildList { buildList {
@@ -75,11 +75,13 @@ object IDELanguageSettingsProvider : LanguageSettingsProvider {
for (module in ModuleManager.getInstance(project).modules) { for (module in ModuleManager.getInstance(project).modules) {
val settings = KotlinFacetSettingsProvider.getInstance(project)?.getSettings(module) ?: continue val settings = KotlinFacetSettingsProvider.getInstance(project)?.getSettings(module) ?: continue
val compilerArguments = settings.mergedCompilerArguments as? K2JVMCompilerArguments ?: continue val compilerArguments = settings.mergedCompilerArguments as? K2JVMCompilerArguments ?: continue
val kotlinVersion = settings.languageLevel?.toKotlinVersion() ?: KotlinVersion.CURRENT
result = JavaTypeEnhancementStateParser(MessageCollector.NONE).parse( result = JavaTypeEnhancementStateParser(MessageCollector.NONE, kotlinVersion).parse(
compilerArguments.jsr305, compilerArguments.jsr305,
compilerArguments.supportCompatqualCheckerFrameworkAnnotations, compilerArguments.supportCompatqualCheckerFrameworkAnnotations,
compilerArguments.jspecifyAnnotations compilerArguments.jspecifyAnnotations,
compilerArguments.nullabilityAnnotations
) )
} }