提交 775d6988 编写于 作者: D Denis Zharkov

Preserve flexibility for Java types annotated with @NonNull(UNKNOWN)

Before this chanhe, these annotations are simply ignored, but they should
preserve flexibility in case of enhanced nullability obtained from
enclosing default qualifier

 #KT-20158 Fixed
上级 d0e91216
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// FILE: spr/Nullable.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierNickname;
import javax.annotation.meta.When;
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull(when = When.MAYBE)
@TypeQualifierNickname
public @interface Nullable {
}
// FILE: spr/NonNullApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
public @interface NonNullApi {
}
// FILE: spr/UnknownNullability.java
package spr;
import javax.annotation.*;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.meta.TypeQualifierNickname;
import javax.annotation.meta.When;
@Documented
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
@Retention(RetentionPolicy.RUNTIME)
public @interface UnknownNullability {
}
// FILE: spr/ForceFlexibility.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
import javax.annotation.meta.When;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@UnknownNullability
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
public @interface ForceFlexibility {
}
// FILE: A.java
import spr.*;
@NonNullApi
public class A {
public String foo(String x, @Nullable CharSequence y) {
return "";
}
@ForceFlexibility
public String bar(String x, @javax.annotation.Nonnull CharSequence y) {
return "";
}
}
// FILE: main.kt
fun main(a: A) {
a.foo("", null)<!UNNECESSARY_SAFE_CALL!>?.<!>length
a.foo("", null).length
a.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>, "").length
a.bar("", "")?.length
a.bar("", "").length
a.bar(null, <!NULL_FOR_NONNULL_TYPE!>null<!>).length
}
package
public fun main(/*0*/ a: A): kotlin.Unit
@spr.NonNullApi public open class A {
public constructor A()
@spr.ForceFlexibility public open fun bar(/*0*/ x: kotlin.String!, /*1*/ @javax.annotation.Nonnull y: kotlin.CharSequence): kotlin.String!
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun foo(/*0*/ x: kotlin.String, /*1*/ @spr.Nullable y: kotlin.CharSequence?): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
package spr {
@kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @spr.UnknownNullability @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER}) public final annotation class ForceFlexibility : kotlin.Annotation {
public constructor ForceFlexibility()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FILE}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.Nonnull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER}) public final annotation class NonNullApi : kotlin.Annotation {
public constructor NonNullApi()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.VALUE_PARAMETER}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.Nonnull(when = When.MAYBE) @javax.annotation.meta.TypeQualifierNickname public final annotation class Nullable : kotlin.Annotation {
public constructor Nullable()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.annotation.MustBeDocumented @javax.annotation.meta.TypeQualifierNickname @javax.annotation.Nonnull(when = When.UNKNOWN) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class UnknownNullability : kotlin.Annotation {
public constructor UnknownNullability()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// FILE: spr/NonNullApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
public @interface NonNullApi {
}
// FILE: spr/UnknownNullability.java
package spr;
import javax.annotation.*;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.meta.TypeQualifierNickname;
import javax.annotation.meta.When;
@Documented
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
@Retention(RetentionPolicy.RUNTIME)
public @interface UnknownNullability {
}
// FILE: spr/ForceFlexibility.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
import javax.annotation.meta.When;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@UnknownNullability
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
public @interface ForceFlexibility {
}
// FILE: B.java
public interface B {
public void foo(@javax.annotation.Nonnull String x);
public void bar(@javax.annotation.Nonnull String x);
public void baz(@javax.annotation.Nonnull String x);
public void foobar(@javax.annotation.Nonnull String x);
}
// FILE: A.java
import spr.*;
@NonNullApi
public class A implements B {
@ForceFlexibility
public void foo(String x) {}
public void bar(@ForceFlexibility String x) {}
public void baz(@UnknownNullability String x) {}
public void foobar(@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) String x) {}
}
// FILE: main.kt
fun main(a: A, b: B) {
b.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
b.bar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
b.baz(<!NULL_FOR_NONNULL_TYPE!>null<!>)
b.foobar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a.foo(null)
// TODO: defaults on parameters do not work properly
a.bar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a.baz(null)
a.foobar(null)
}
package
public fun main(/*0*/ a: A, /*1*/ b: B): kotlin.Unit
@spr.NonNullApi public open class A : B {
public constructor A()
public open override /*1*/ fun bar(/*0*/ @spr.ForceFlexibility x: kotlin.String): kotlin.Unit
public open override /*1*/ fun baz(/*0*/ @spr.UnknownNullability x: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@spr.ForceFlexibility public open override /*1*/ fun foo(/*0*/ x: kotlin.String!): kotlin.Unit
public open override /*1*/ fun foobar(/*0*/ @javax.annotation.Nonnull(when = When.UNKNOWN) x: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B {
public abstract fun bar(/*0*/ @javax.annotation.Nonnull x: kotlin.String): kotlin.Unit
public abstract fun baz(/*0*/ @javax.annotation.Nonnull x: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(/*0*/ @javax.annotation.Nonnull x: kotlin.String): kotlin.Unit
public abstract fun foobar(/*0*/ @javax.annotation.Nonnull x: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
package spr {
@kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @spr.UnknownNullability @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER}) public final annotation class ForceFlexibility : kotlin.Annotation {
public constructor ForceFlexibility()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FILE}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.Nonnull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER}) public final annotation class NonNullApi : kotlin.Annotation {
public constructor NonNullApi()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.annotation.MustBeDocumented @javax.annotation.meta.TypeQualifierNickname @javax.annotation.Nonnull(when = When.UNKNOWN) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class UnknownNullability : kotlin.Annotation {
public constructor UnknownNullability()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
......@@ -352,6 +352,18 @@ public class ForeignAnnotationsNoAnnotationInClasspathTestGenerated extends Abst
doTest(fileName);
}
@TestMetadata("forceFlexibility.kt")
public void testForceFlexibility() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/forceFlexibility.kt");
doTest(fileName);
}
@TestMetadata("forceFlexibleOverOverrides.kt")
public void testForceFlexibleOverOverrides() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/forceFlexibleOverOverrides.kt");
doTest(fileName);
}
@TestMetadata("nullabilityFromOverridden.kt")
public void testNullabilityFromOverridden() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/nullabilityFromOverridden.kt");
......
......@@ -352,6 +352,18 @@ public class ForeignAnnotationsNoAnnotationInClasspathWithFastClassReadingTestGe
doTest(fileName);
}
@TestMetadata("forceFlexibility.kt")
public void testForceFlexibility() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/forceFlexibility.kt");
doTest(fileName);
}
@TestMetadata("forceFlexibleOverOverrides.kt")
public void testForceFlexibleOverOverrides() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/forceFlexibleOverOverrides.kt");
doTest(fileName);
}
@TestMetadata("nullabilityFromOverridden.kt")
public void testNullabilityFromOverridden() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/nullabilityFromOverridden.kt");
......
......@@ -352,6 +352,18 @@ public class ForeignAnnotationsTestGenerated extends AbstractForeignAnnotationsT
doTest(fileName);
}
@TestMetadata("forceFlexibility.kt")
public void testForceFlexibility() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/forceFlexibility.kt");
doTest(fileName);
}
@TestMetadata("forceFlexibleOverOverrides.kt")
public void testForceFlexibleOverOverrides() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/forceFlexibleOverOverrides.kt");
doTest(fileName);
}
@TestMetadata("nullabilityFromOverridden.kt")
public void testNullabilityFromOverridden() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/nullabilityFromOverridden.kt");
......
......@@ -352,6 +352,18 @@ public class JavacForeignAnnotationsTestGenerated extends AbstractJavacForeignAn
doTest(fileName);
}
@TestMetadata("forceFlexibility.kt")
public void testForceFlexibility() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/forceFlexibility.kt");
doTest(fileName);
}
@TestMetadata("forceFlexibleOverOverrides.kt")
public void testForceFlexibleOverOverrides() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/forceFlexibleOverOverrides.kt");
doTest(fileName);
}
@TestMetadata("nullabilityFromOverridden.kt")
public void testNullabilityFromOverridden() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/jsr305/typeQualifierDefault/nullabilityFromOverridden.kt");
......
......@@ -63,6 +63,7 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
return when (enumEntryDescriptor.name.asString()) {
"ALWAYS" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
"MAYBE", "NEVER" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
"UNKNOWN" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.FORCE_FLEXIBILITY)
else -> null
}
}
......@@ -346,16 +347,22 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
return effectiveSet.singleOrNull()
}
fun Set<NullabilityQualifier>.select(own: NullabilityQualifier?) =
if (own == NullabilityQualifier.FORCE_FLEXIBILITY)
NullabilityQualifier.FORCE_FLEXIBILITY
else
select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, own)
val ownNullability = own.takeIf { !it.isNullabilityQualifierForWarning }?.nullability
val ownNullabilityForWarning = own.nullability
val nullability = nullabilityFromSupertypes.select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, ownNullability)
val nullability = nullabilityFromSupertypes.select(ownNullability)
val mutability = mutabilityFromSupertypes.select(MutabilityQualifier.MUTABLE, MutabilityQualifier.READ_ONLY, own.mutability)
val canChange = ownNullabilityForWarning != ownNullability || nullabilityFromSupertypesWithWarning != nullabilityFromSupertypes
if (nullability == null && canChange) {
val nullabilityWithWarning =
nullabilityFromSupertypesWithWarning.select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, ownNullabilityForWarning)
nullabilityFromSupertypesWithWarning.select(ownNullabilityForWarning)
return createJavaTypeQualifiers(nullabilityWithWarning, mutability, true)
}
......
......@@ -18,7 +18,8 @@ package org.jetbrains.kotlin.load.java.typeEnhancement
enum class NullabilityQualifier {
NULLABLE,
NOT_NULL
NOT_NULL,
FORCE_FLEXIBILITY
}
enum class MutabilityQualifier {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册