Make TYPE_USE default qualifiers overriding all other applicabilities

If there is default qualifier with TYPE_USE closer than one with METHOD
then its nullability should be considered even when enhancing return type

 #KT-20016 Fixed
This commit is contained in:
Denis Zharkov
2017-09-28 15:29:26 +03:00
parent 62f293280c
commit 13bf35f48e
8 changed files with 143 additions and 6 deletions
@@ -0,0 +1,80 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// SKIP_JAVAC
// SKIP_COMPILED_JAVA
// 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, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull
@TypeQualifierDefault({ElementType.TYPE_USE, ElementType.METHOD})
public @interface NonNullApi {
}
// FILE: spr/NullableApi.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;
@Target({ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull(when = When.MAYBE)
@TypeQualifierDefault({ElementType.TYPE_USE})
public @interface NullableApi {
}
// FILE: A.java
import spr.*;
import java.util.*;
@NonNullApi
public class A {
public String foo(String x) { return ""; }
public @NullableApi String bar(@NullableApi String y) { return ""; }
public @NullableApi List<String> baz1() { return null; }
public List<@NullableApi String> baz2() { return null; }
public @NullableApi List<@NonNullApi String> baz3() { return null; }
}
// FILE: main.kt
fun main(a: A) {
a.foo("").length
a.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)<!UNNECESSARY_SAFE_CALL!>?.<!>length
a.bar("")<!UNSAFE_CALL!>.<!>length
a.bar(null)?.length
a.baz1()<!UNSAFE_CALL!>.<!>get(0)<!UNSAFE_CALL!>.<!>length
a.baz1()!!.get(0)<!UNSAFE_CALL!>.<!>length
a.baz1()!!.get(0)?.length
a.baz2().get(0)<!UNSAFE_CALL!>.<!>length
a.baz2()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.get(0)<!UNSAFE_CALL!>.<!>length
a.baz2()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.get(0)?.length
a.baz3()<!UNSAFE_CALL!>.<!>get(0).length
a.baz3()!!.get(0).length
a.baz3()!!.get(0)<!UNNECESSARY_SAFE_CALL!>?.<!>length
}
@@ -0,0 +1,32 @@
package
public fun main(/*0*/ a: A): kotlin.Unit
@spr.NonNullApi public open class A {
public constructor A()
@spr.NullableApi public open fun bar(/*0*/ @spr.NullableApi y: @spr.NullableApi kotlin.String?): @spr.NullableApi kotlin.String?
@spr.NullableApi public open fun baz1(): (@spr.NullableApi kotlin.collections.MutableList<kotlin.String?>?..@spr.NullableApi kotlin.collections.List<kotlin.String?>?)
public open fun baz2(): kotlin.collections.(Mutable)List<@spr.NullableApi kotlin.String?>
@spr.NullableApi public open fun baz3(): (@spr.NullableApi kotlin.collections.MutableList<@spr.NonNullApi kotlin.String>?..@spr.NullableApi kotlin.collections.List<@spr.NonNullApi kotlin.String>?)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun foo(/*0*/ x: kotlin.String): 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.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FILE, AnnotationTarget.TYPE}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.Nonnull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.TYPE_USE, ElementType.METHOD}) 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.TYPE}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.Nonnull(when = When.MAYBE) @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.TYPE_USE}) public final annotation class NullableApi : kotlin.Annotation {
public constructor NullableApi()
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
}
}
@@ -79,6 +79,12 @@ public class ForeignJava8AnnotationsNoAnnotationInClasspathTestGenerated extends
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt");
doTest(fileName);
}
@TestMetadata("typeUseVsMethodConflict.kt")
public void testTypeUseVsMethodConflict() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeUseVsMethodConflict.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/foreignAnnotationsJava8/tests/typeEnhancement")
@@ -79,6 +79,12 @@ public class ForeignJava8AnnotationsNoAnnotationInClasspathWithFastClassReadingT
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt");
doTest(fileName);
}
@TestMetadata("typeUseVsMethodConflict.kt")
public void testTypeUseVsMethodConflict() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeUseVsMethodConflict.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/foreignAnnotationsJava8/tests/typeEnhancement")
@@ -79,6 +79,12 @@ public class ForeignJava8AnnotationsTestGenerated extends AbstractForeignJava8An
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt");
doTest(fileName);
}
@TestMetadata("typeUseVsMethodConflict.kt")
public void testTypeUseVsMethodConflict() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeUseVsMethodConflict.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/foreignAnnotationsJava8/tests/typeEnhancement")
@@ -79,6 +79,12 @@ public class JavacForeignJava8AnnotationsTestGenerated extends AbstractJavacFore
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt");
doTest(fileName);
}
@TestMetadata("typeUseVsMethodConflict.kt")
public void testTypeUseVsMethodConflict() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeUseVsMethodConflict.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/foreignAnnotationsJava8/tests/typeEnhancement")
@@ -42,10 +42,14 @@ class AnnotationTypeQualifierResolver(storageManager: StorageManager, val jsr305
private val typeQualifier: AnnotationDescriptor,
private val applicability: Int
) {
private fun isApplicableTo(elementType: QualifierApplicabilityType) = (applicability and (1 shl elementType.ordinal)) != 0
operator fun component1() = typeQualifier
operator fun component2() = QualifierApplicabilityType.values().filter(this::isApplicableTo)
private fun isApplicableTo(elementType: QualifierApplicabilityType) =
isApplicableConsideringMask(QualifierApplicabilityType.TYPE_USE) || isApplicableConsideringMask(elementType)
private fun isApplicableConsideringMask(elementType: QualifierApplicabilityType) =
(applicability and (1 shl elementType.ordinal)) != 0
}
private val resolvedNicknames =
@@ -78,10 +78,7 @@ class JavaTypeQualifiersByElementType(
operator fun get(
applicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType?
): JavaTypeQualifiers? {
val nullabilityQualifierWithMigrationStatus =
nullabilityQualifiers[applicabilityType]
?: nullabilityQualifiers[AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE]
?: return null
val nullabilityQualifierWithMigrationStatus = nullabilityQualifiers[applicabilityType] ?: return null
return JavaTypeQualifiers(
nullabilityQualifierWithMigrationStatus.qualifier, null,