Add -Xsupport-compatqual-checker-framework-annotations flag

It's implemented through Jsr305State while it's not related
to jsr-305 becasue currently it's the most convenient way
to introduce the flag.

Probably, it's worth renaming Jsr305State to something more abstract
like NullabilityAnnotationsConfiguration

 #KT-21982 Fixed
This commit is contained in:
Denis Zharkov
2018-01-10 17:02:46 +03:00
parent 40706de3db
commit 3cfe43f83a
19 changed files with 132 additions and 21 deletions
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.utils.Jsr305State
import org.jetbrains.kotlin.utils.ReportLevel
class Jsr305Parser(private val collector: MessageCollector) {
fun parse(value: Array<String>?): Jsr305State {
fun parse(value: Array<String>?, supportCompatqualCheckerFrameworkAnnotations: String?): Jsr305State {
var global: ReportLevel? = null
var migration: ReportLevel? = null
val userDefined = mutableMapOf<String, ReportLevel>()
@@ -70,7 +70,25 @@ class Jsr305Parser(private val collector: MessageCollector) {
}
}
val state = Jsr305State(global ?: ReportLevel.WARN, migration, userDefined)
val enableCompatqualCheckerFrameworkAnnotations = when (supportCompatqualCheckerFrameworkAnnotations) {
"enable" -> true
"disable" -> false
null -> null
else -> {
collector.report(
CompilerMessageSeverity.ERROR,
"Unrecognized -Xsupport-compatqual-checker-framework-annotations option: $supportCompatqualCheckerFrameworkAnnotations. Possible values are 'enable'/'disable'"
)
null
}
}
val state = Jsr305State(
global ?: ReportLevel.WARN, migration, userDefined,
enableCompatqualCheckerFrameworkAnnotations =
enableCompatqualCheckerFrameworkAnnotations
?: Jsr305State.COMPATQUAL_CHECKER_FRAMEWORK_ANNOTATIONS_SUPPORT_DEFAULT_VALUE
)
return if (state == Jsr305State.DISABLED) Jsr305State.DISABLED else state
}
@@ -16,13 +16,10 @@
package org.jetbrains.kotlin.cli.common.arguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.AnalysisFlag
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.utils.Jsr305State
import org.jetbrains.kotlin.utils.ReportLevel
class K2JVMCompilerArguments : CommonCompilerArguments() {
companion object {
@@ -191,6 +188,17 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
)
var jsr305: Array<String>? by FreezableVar(null)
@Argument(
value = "-Xsupport-compatqual-checker-framework-annotations",
valueDescription = "enable|disable",
description =
"""
Specify behavior for Checker Framework compatqual annotations (NullableDecl/NonNullDecl).
Default value is 'enable'
"""
)
var supportCompatqualCheckerFrameworkAnnotations: String? by FreezableVar(null)
@Argument(
value = "-Xno-exception-on-explicit-equals-for-boxed-null",
description = "Do not throw NPE on explicit 'equals' call for null receiver of platform boxed primitive type"
@@ -202,7 +210,10 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
val result = super.configureAnalysisFlags(collector)
result[AnalysisFlag.jsr305] = Jsr305Parser(collector).parse(jsr305)
result[AnalysisFlag.jsr305] = Jsr305Parser(collector).parse(
jsr305,
supportCompatqualCheckerFrameworkAnnotations
)
return result
}
}
+3
View File
@@ -0,0 +1,3 @@
public class A {
public void foo(@org.checkerframework.checker.nullness.compatqual.NonNullDecl String x) {}
}
+5
View File
@@ -0,0 +1,5 @@
$TESTDATA_DIR$/compatqualUsage.kt
$TESTDATA_DIR$/compatqual
$FOREIGN_ANNOTATIONS_DIR$
-d
$TEMP_DIR$
+4
View File
@@ -0,0 +1,4 @@
compiler/testData/cli/jvm/compatqualUsage.kt:2:11: error: null can not be a value of a non-null type String
a.foo(null)
^
COMPILATION_ERROR
+6
View File
@@ -0,0 +1,6 @@
-Xsupport-compatqual-checker-framework-annotations=disable
$TESTDATA_DIR$/compatqualUsage.kt
$TESTDATA_DIR$/compatqual
$FOREIGN_ANNOTATIONS_DIR$
-d
$TEMP_DIR$
+1
View File
@@ -0,0 +1 @@
OK
+6
View File
@@ -0,0 +1,6 @@
-Xsupport-compatqual-checker-framework-annotations=enable
$TESTDATA_DIR$/compatqualUsage.kt
$TESTDATA_DIR$/compatqual
$FOREIGN_ANNOTATIONS_DIR$
-d
$TEMP_DIR$
+4
View File
@@ -0,0 +1,4 @@
compiler/testData/cli/jvm/compatqualUsage.kt:2:11: error: null can not be a value of a non-null type String
a.foo(null)
^
COMPILATION_ERROR
+3
View File
@@ -0,0 +1,3 @@
fun bar(a: A) {
a.foo(null)
}
+6
View File
@@ -0,0 +1,6 @@
-Xsupport-compatqual-checker-framework-annotations=true
$TESTDATA_DIR$/compatqualUsage.kt
$TESTDATA_DIR$/compatqual
$FOREIGN_ANNOTATIONS_DIR$
-d
$TEMP_DIR$
+2
View File
@@ -0,0 +1,2 @@
error: unrecognized -Xsupport-compatqual-checker-framework-annotations option: true. Possible values are 'enable'/'disable'
COMPILATION_ERROR
+5
View File
@@ -35,6 +35,11 @@ where advanced options include:
-Xskip-runtime-version-check Allow Kotlin runtime libraries of incompatible versions in the classpath
-Xstrict-java-nullability-assertions
Generate nullability assertions for non-null Java expressions
-Xsupport-compatqual-checker-framework-annotations=enable|disable
Specify behavior for Checker Framework compatqual annotations (NullableDecl/NonNullDecl).
Default value is 'enable'
-Xuse-javac Use javac for Java source and class files analysis
-Xuse-old-class-files-reading Use old class files reading implementation (may slow down the build and should be used in case of problems with the new implementation)
-Xallow-kotlin-package Allow compiling code in package 'kotlin' and allow not requiring kotlin.stdlib in module-info
@@ -99,6 +99,30 @@ public class CliTestGenerated extends AbstractCliTest {
doJvmTest(fileName);
}
@TestMetadata("compatqualDefault.args")
public void testCompatqualDefault() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/compatqualDefault.args");
doJvmTest(fileName);
}
@TestMetadata("compatqualDisable.args")
public void testCompatqualDisable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/compatqualDisable.args");
doJvmTest(fileName);
}
@TestMetadata("compatqualEnable.args")
public void testCompatqualEnable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/compatqualEnable.args");
doJvmTest(fileName);
}
@TestMetadata("compatqualWrong.args")
public void testCompatqualWrong() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/compatqualWrong.args");
doJvmTest(fileName);
}
@TestMetadata("conflictingOverloads.args")
public void testConflictingOverloads() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/conflictingOverloads.args");
@@ -24,7 +24,6 @@ val NULLABLE_ANNOTATIONS = listOf(
FqName("com.android.annotations.Nullable"),
FqName("org.eclipse.jdt.annotation.Nullable"),
FqName("org.checkerframework.checker.nullness.qual.Nullable"),
FqName("org.checkerframework.checker.nullness.compatqual.NullableDecl"),
FqName("javax.annotation.Nullable"),
FqName("javax.annotation.CheckForNull"),
FqName("edu.umd.cs.findbugs.annotations.CheckForNull"),
@@ -43,11 +42,13 @@ val NOT_NULL_ANNOTATIONS = listOf(
FqName("com.android.annotations.NonNull"),
FqName("org.eclipse.jdt.annotation.NonNull"),
FqName("org.checkerframework.checker.nullness.qual.NonNull"),
FqName("org.checkerframework.checker.nullness.compatqual.NonNullDecl"),
FqName("lombok.NonNull"),
FqName("io.reactivex.annotations.NonNull")
)
val COMPATQUAL_NULLABLE_ANNOTATION = FqName("org.checkerframework.checker.nullness.compatqual.NullableDecl")
val COMPATQUAL_NONNULL_ANNOTATION = FqName("org.checkerframework.checker.nullness.compatqual.NonNullDecl")
val READ_ONLY_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_READONLY_ANNOTATION,
JvmAnnotationNames.READONLY_ANNOTATION
@@ -31,13 +31,10 @@ 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.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.asFlexibleType
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.unwrapEnhancement
import org.jetbrains.kotlin.utils.Jsr305State
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -46,7 +43,10 @@ data class NullabilityQualifierWithMigrationStatus(
val isForWarningOnly: Boolean = false
)
class SignatureEnhancement(private val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver) {
class SignatureEnhancement(
private val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver,
private val jsr305State: Jsr305State
) {
private fun AnnotationDescriptor.extractNullabilityTypeFromArgument(): NullabilityQualifierWithMigrationStatus? {
val enumEntryDescriptor = firstArgumentValue()
@@ -81,10 +81,16 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
): 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()
return when {
annotationFqName in NULLABLE_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
annotationFqName in NOT_NULL_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
annotationFqName == JAVAX_NONNULL_ANNOTATION -> annotationDescriptor.extractNullabilityTypeFromArgument()
annotationFqName == COMPATQUAL_NULLABLE_ANNOTATION && jsr305State.enableCompatqualCheckerFrameworkAnnotations ->
NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
annotationFqName == COMPATQUAL_NONNULL_ANNOTATION && jsr305State.enableCompatqualCheckerFrameworkAnnotations ->
NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
else -> null
}
}
@@ -68,7 +68,7 @@ class RuntimeModuleData private constructor(
runtimePackagePartProvider, SupertypeLoopChecker.EMPTY, LookupTracker.DO_NOTHING, module,
ReflectionTypes(module, notFoundClasses),
annotationTypeQualifierResolver,
SignatureEnhancement(annotationTypeQualifierResolver),
SignatureEnhancement(annotationTypeQualifierResolver, Jsr305State.DISABLED),
JavaClassesTracker.Default
)
@@ -33,7 +33,8 @@ enum class ReportLevel(val description: String) {
data class Jsr305State(
val global: ReportLevel,
val migration: ReportLevel?,
val user: Map<String, ReportLevel>
val user: Map<String, ReportLevel>,
val enableCompatqualCheckerFrameworkAnnotations: Boolean = COMPATQUAL_CHECKER_FRAMEWORK_ANNOTATIONS_SUPPORT_DEFAULT_VALUE
) {
val description: Array<String> by lazy {
val result = mutableListOf<String>()
@@ -51,6 +52,8 @@ data class Jsr305State(
val disabled: Boolean get() = this === DISABLED
companion object {
const val COMPATQUAL_CHECKER_FRAMEWORK_ANNOTATIONS_SUPPORT_DEFAULT_VALUE = true
@JvmField
val DEFAULT: Jsr305State = Jsr305State(ReportLevel.WARN, null, emptyMap())
@@ -47,7 +47,10 @@ object IDELanguageSettingsProvider : LanguageSettingsProvider {
val settings = KotlinFacetSettingsProvider.getInstance(project).getSettings(module) ?: continue
val compilerArguments = settings.mergedCompilerArguments as? K2JVMCompilerArguments ?: continue
val jsr305State = Jsr305Parser(MessageCollector.NONE).parse(compilerArguments.jsr305)
val jsr305State = Jsr305Parser(MessageCollector.NONE).parse(
compilerArguments.jsr305,
compilerArguments.supportCompatqualCheckerFrameworkAnnotations
)
map.put(AnalysisFlag.jsr305, jsr305State)
}
return map