From 71f85812d64c5ea53ab889ab165718743e310dfb Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 8 Sep 2017 15:15:55 +0300 Subject: [PATCH] Improve support for TYPE_USE default qualifiers - Apply default qualifiers to type arguments if they contain TYPE_USE in applicability list - Read TYPE_USE placed default qualifier annotations #KT-19592 Fixed #KT-20016 In Progress --- .../jsr305/defaultAnnotationAppliedToType.kt | 79 ++++++++++++ .../jsr305/defaultAnnotationAppliedToType.txt | 32 +++++ .../tests/jsr305/springNullableWithTypeUse.kt | 2 +- .../jsr305/springNullableWithTypeUse.txt | 2 +- .../tests/jsr305/typeArguments.kt | 110 +++++++++++++++++ .../tests/jsr305/typeArguments.txt | 71 +++++++++++ .../ForeignJava8AnnotationsTestGenerated.java | 12 ++ ...cForeignJava8AnnotationsTestGenerated.java | 12 ++ ...bstractJavacForeignJava8AnnotationsTest.kt | 5 +- .../kotlin/load/java/lazy/context.kt | 12 +- .../typeEnhancement/signatureEnhancement.kt | 113 +++++++++++------- 11 files changed, 402 insertions(+), 48 deletions(-) create mode 100644 compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.kt create mode 100644 compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.txt create mode 100644 compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt create mode 100644 compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.txt diff --git a/compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.kt b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.kt new file mode 100644 index 00000000000..c893b630359 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.kt @@ -0,0 +1,79 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER +// SKIP_JAVAC + +// 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}) +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 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)?.length + + a.bar("").length + a.bar(null)?.length + + a.baz1().get(0).length + a.baz1()!!.get(0).length + a.baz1()!!.get(0)?.length + + a.baz2().get(0).length + a.baz2()!!.get(0).length + a.baz2()!!.get(0)?.length + + a.baz3().get(0).length + a.baz3()!!.get(0).length + a.baz3()!!.get(0)?.length +} diff --git a/compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.txt b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.txt new file mode 100644 index 00000000000..26732bde6fe --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.txt @@ -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?..@spr.NullableApi kotlin.collections.List?) + 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}) 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 + } +} diff --git a/compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.kt b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.kt index c400c6ed766..152dd1d2189 100644 --- a/compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.kt +++ b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.kt @@ -78,5 +78,5 @@ fun main(a: A) { a.baz().get(0) a.baz()!!.get(0).get(0) - a.baz()!!.get(0)?.get(0) + a.baz()!!.get(0)?.get(0) } diff --git a/compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.txt b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.txt index 5dae5b01230..e81d4f28c39 100644 --- a/compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.txt +++ b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.txt @@ -6,7 +6,7 @@ public fun main(/*0*/ a: A): kotlin.Unit public constructor A() public final var field: kotlin.String public open fun bar(): kotlin.String - @spr.Nullable public open fun baz(): kotlin.collections.(Mutable)List? + @spr.Nullable public open fun baz(): kotlin.collections.(Mutable)List? 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 diff --git a/compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt new file mode 100644 index 00000000000..e908cf5b099 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt @@ -0,0 +1,110 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER +// RENDER_PACKAGE: test + +// 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.TYPE_USE}) +@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.PACKAGE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Nonnull +@TypeQualifierDefault({ElementType.TYPE_USE}) +public @interface NonNullApi { +} + +// FILE: test/package-info.java + +@spr.NonNullApi() +package test; + +// FILE: test/L.java + +package test; + +public class L, S> { + public T t() { return null; } + public S s() { return null; } + + public void setT(@spr.Nullable T t) {} + public void setS(S s) {} +} + +// FILE: test/A.java + +package test; + +import spr.*; +import java.util.*; + +public class A { + public void foo(L, @Nullable Integer> l) {} + public void bar(L l) {} + public L, @Nullable Integer> baz1() { return null; } + public L baz2() { return null; } + public L, Integer> baz3() { return null; } +} + +// FILE: main.kt + +import test.L + +fun main(a: test.A, l: L, Int?>, l1: L, Int>) { + a.foo(l) + a.foo(l as L, Int>) + a.foo(l as L, Int?>) + + a.bar(l1) + a.bar(l1 as L, Int?>) + + a.baz1().t().containsKey("") + a.baz1().t().containsKey(null) + a.baz1().t().containsValue(1) + a.baz1().t().containsValue(null) + a.baz1().s().hashCode() + + a.baz1().setT(l.t()) + a.baz1().setT(l.t() as L, Int>) + a.baz1().setT(null) + + a.baz2().t().containsKey("") + a.baz2().t().containsKey(null) + a.baz2().t().containsValue(1) + a.baz2().t().containsValue(null) + a.baz2().s().hashCode() + + a.baz3().t().containsKey("") + a.baz3().t().containsKey(null) + a.baz3().t().containsValue(1) + a.baz3().t().containsValue(null) + a.baz3().s().hashCode() +} diff --git a/compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.txt b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.txt new file mode 100644 index 00000000000..57825cbb44e --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.txt @@ -0,0 +1,71 @@ +package test + +public open class A { + public constructor A() + public open fun bar(/*0*/ l: test.L<*, kotlin.Int>): kotlin.Unit + public open fun baz1(): test.L, @spr.Nullable kotlin.Int?> + public open fun baz2(): test.L<*, kotlin.Int> + public open fun baz3(): test.L, kotlin.Int> + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ l: test.L, @spr.Nullable kotlin.Int?>): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class L!, /*1*/ S : kotlin.Any!> { + public constructor L!, /*1*/ S : kotlin.Any!>() + 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 fun s(): S + public open fun setS(/*0*/ s: S): kotlin.Unit + public open fun setT(/*0*/ @spr.Nullable t: @spr.Nullable T?): kotlin.Unit + public open fun t(): T + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +package + +public fun main(/*0*/ a: test.A, /*1*/ l: test.L, kotlin.Int?>, /*2*/ l1: test.L, kotlin.Int>): kotlin.Unit + +package spr { + + @kotlin.annotation.Target(allowedTargets = {}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.Nonnull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.TYPE_USE}) 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.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 + } +} + +package test { + + public open class A { + public constructor A() + public open fun bar(/*0*/ l: test.L<*, kotlin.Int>): kotlin.Unit + public open fun baz1(): test.L, @spr.Nullable kotlin.Int?> + public open fun baz2(): test.L<*, kotlin.Int> + public open fun baz3(): test.L, kotlin.Int> + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ l: test.L, @spr.Nullable kotlin.Int?>): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class L!, /*1*/ S : kotlin.Any!> { + public constructor L!, /*1*/ S : kotlin.Any!>() + 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 fun s(): S + public open fun setS(/*0*/ s: S): kotlin.Unit + public open fun setT(/*0*/ @spr.Nullable t: @spr.Nullable T?): kotlin.Unit + public open fun t(): T + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/ForeignJava8AnnotationsTestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/ForeignJava8AnnotationsTestGenerated.java index 6743e74c660..44628460eec 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/ForeignJava8AnnotationsTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/ForeignJava8AnnotationsTestGenerated.java @@ -56,11 +56,23 @@ public class ForeignJava8AnnotationsTestGenerated extends AbstractForeignJava8An KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/foreignAnnotationsJava8/tests/jsr305"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("defaultAnnotationAppliedToType.kt") + public void testDefaultAnnotationAppliedToType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.kt"); + doTest(fileName); + } + @TestMetadata("springNullableWithTypeUse.kt") public void testSpringNullableWithTypeUse() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.kt"); doTest(fileName); } + + @TestMetadata("typeArguments.kt") + public void testTypeArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/foreignAnnotationsJava8/tests/typeEnhancement") diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/javac/JavacForeignJava8AnnotationsTestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/javac/JavacForeignJava8AnnotationsTestGenerated.java index 0d30ab36bcb..00046c3dacf 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/javac/JavacForeignJava8AnnotationsTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/javac/JavacForeignJava8AnnotationsTestGenerated.java @@ -56,11 +56,23 @@ public class JavacForeignJava8AnnotationsTestGenerated extends AbstractJavacFore KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/foreignAnnotationsJava8/tests/jsr305"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("defaultAnnotationAppliedToType.kt") + public void testDefaultAnnotationAppliedToType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/defaultAnnotationAppliedToType.kt"); + doTest(fileName); + } + @TestMetadata("springNullableWithTypeUse.kt") public void testSpringNullableWithTypeUse() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/springNullableWithTypeUse.kt"); doTest(fileName); } + + @TestMetadata("typeArguments.kt") + public void testTypeArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/jsr305/typeArguments.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/foreignAnnotationsJava8/tests/typeEnhancement") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/AbstractJavacForeignJava8AnnotationsTest.kt b/compiler/tests/org/jetbrains/kotlin/checkers/javac/AbstractJavacForeignJava8AnnotationsTest.kt index a4ec046a3b4..7a2180f4b48 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/AbstractJavacForeignJava8AnnotationsTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/AbstractJavacForeignJava8AnnotationsTest.kt @@ -18,11 +18,14 @@ package org.jetbrains.kotlin.checkers.javac import org.jetbrains.kotlin.checkers.AbstractForeignJava8AnnotationsTest import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.test.InTextDirectivesUtils import java.io.File abstract class AbstractJavacForeignJava8AnnotationsTest : AbstractForeignJava8AnnotationsTest() { override fun analyzeAndCheck(testDataFile: File, files: List) { + if (files.any { file -> InTextDirectivesUtils.isDirectiveDefined(file.expectedText, "// SKIP_JAVAC") }) return + val groupedByModule = files.groupBy(TestFile::module) val allKtFiles = groupedByModule.values.flatMap { getKtFiles(it, true) } environment.registerJavac(kotlinFiles = allKtFiles) @@ -31,4 +34,4 @@ abstract class AbstractJavacForeignJava8AnnotationsTest : AbstractForeignJava8An super.analyzeAndCheck(testDataFile, files) } -} \ No newline at end of file +} diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/context.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/context.kt index 85f78dc7081..6ee1f4778b5 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/context.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/context.kt @@ -76,7 +76,7 @@ class JavaTypeQualifiersByElementType( internal val nullabilityQualifiers: QualifierByApplicabilityType ) { operator fun get( - applicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType + applicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType? ): JavaTypeQualifiers? { val nullabilityQualifierWithMigrationStatus = nullabilityQualifiers[applicabilityType] @@ -204,3 +204,13 @@ fun LazyJavaResolverContext.childForClassOrPackage( containingDeclaration, typeParameterOwner, typeParametersIndexOffset, lazy(LazyThreadSafetyMode.NONE) { computeNewDefaultTypeQualifiers(containingDeclaration.annotations) } ) + +fun LazyJavaResolverContext.copyWithNewDefaultTypeQualifiers( + additionalAnnotations: Annotations +) = if (additionalAnnotations.isEmpty()) + this + else + LazyJavaResolverContext( + components, typeParameterResolver, + lazy(LazyThreadSafetyMode.NONE) { computeNewDefaultTypeQualifiers(additionalAnnotations) } + ) diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt index 331cbb709f9..d7a99849b56 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt @@ -29,7 +29,7 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext -import org.jetbrains.kotlin.load.java.lazy.computeNewDefaultTypeQualifiers +import org.jetbrains.kotlin.load.java.lazy.copyWithNewDefaultTypeQualifiers import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor @@ -109,7 +109,7 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati // Fake overrides with one overridden has been enhanced before if (kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE && original.overriddenDescriptors.size == 1) return this - val outerScopeQualifiers = c.computeNewDefaultTypeQualifiers(annotations) + val memberContext = c.copyWithNewDefaultTypeQualifiers(annotations) // When loading method as an override for a property, all annotations are stick to its getter val annotationOwnerForMember = @@ -125,9 +125,8 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati annotationOwnerForMember.safeAs() ?.getUserData(JavaMethodDescriptor.ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER), isCovariant = false, - defaultTopLevelQualifiers = - outerScopeQualifiers - ?.get(AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER) + containerContext = memberContext, + containerApplicabilityType = AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER ) { it.extensionReceiverParameter!!.type }.enhance() else null @@ -148,9 +147,8 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati p -> parts( typeContainer = p, isCovariant = false, - defaultTopLevelQualifiers = - outerScopeQualifiers - ?.get(AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER) + containerContext = memberContext, + containerApplicabilityType = AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER ) { it.valueParameters[p.index].type } .enhance(predefinedEnhancementInfo?.parametersInfo?.getOrNull(p.index)) } @@ -158,15 +156,12 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati val returnTypeEnhancement = parts( typeContainer = annotationOwnerForMember, isCovariant = true, - defaultTopLevelQualifiers = - outerScopeQualifiers?.get( - if (this.safeAs()?.isJavaField == true) - AnnotationTypeQualifierResolver.QualifierApplicabilityType.FIELD - else - AnnotationTypeQualifierResolver.QualifierApplicabilityType.METHOD_RETURN_TYPE - ) - - + containerContext = memberContext, + containerApplicabilityType = + if (this.safeAs()?.isJavaField == true) + AnnotationTypeQualifierResolver.QualifierApplicabilityType.FIELD + else + AnnotationTypeQualifierResolver.QualifierApplicabilityType.METHOD_RETURN_TYPE ) { it.returnType!! }.enhance(predefinedEnhancementInfo?.returnTypeInfo) if ((receiverTypeEnhancement?.wereChanges ?: false) @@ -183,7 +178,8 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati private val fromOverride: KotlinType, private val fromOverridden: Collection, private val isCovariant: Boolean, - private val defaultTopLevelQualifiers: JavaTypeQualifiers? + private val containerContext: LazyJavaResolverContext, + private val containerApplicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType ) { fun enhance(predefined: TypeEnhancementInfo? = null): PartEnhancementResult { val qualifiers = computeIndexedQualifiersForOverride() @@ -222,7 +218,10 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati isNotNullTypeParameter = unwrap() is NotNullTypeParameter) } - private fun KotlinType.extractQualifiersFromAnnotations(isHeadTypeConstructor: Boolean): JavaTypeQualifiers { + private fun KotlinType.extractQualifiersFromAnnotations( + isHeadTypeConstructor: Boolean, + defaultQualifiersForType: JavaTypeQualifiers? + ): JavaTypeQualifiers { val composedAnnotation = if (isHeadTypeConstructor && typeContainer != null) composeAnnotations(typeContainer.annotations, annotations) @@ -234,7 +233,12 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati fun uniqueNotNull(x: T?, y: T?) = if (x == null || y == null || x == y) x ?: y else null - val defaultTypeQualifier = defaultTopLevelQualifiers?.takeIf { isHeadTypeConstructor } + val defaultTypeQualifier = + if (isHeadTypeConstructor) + containerContext.defaultTypeQualifiers?.get(containerApplicabilityType) + else + defaultQualifiersForType + val nullabilityInfo = composedAnnotation.extractNullability() ?: defaultTypeQualifier?.nullability?.let { @@ -263,24 +267,6 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati this.firstNotNullResult(this@SignatureEnhancement::extractNullability) private fun computeIndexedQualifiersForOverride(): (Int) -> JavaTypeQualifiers { - fun KotlinType.toIndexed(): List { - val list = ArrayList(1) - - fun add(type: KotlinType) { - list.add(type) - for (arg in type.arguments) { - if (arg.isStarProjection) { - list.add(arg.type) - } - else { - add(arg.type) - } - } - } - - add(this) - return list - } val indexedFromSupertypes = fromOverridden.map { it.toIndexed() } val indexedThisType = fromOverride.toIndexed() @@ -298,18 +284,49 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati val isHeadTypeConstructor = index == 0 assert(isHeadTypeConstructor || !onlyHeadTypeConstructor) { "Only head type constructors should be computed" } - val qualifiers = indexedThisType[index] - val verticalSlice = indexedFromSupertypes.mapNotNull { it.getOrNull(index) } + val (qualifiers, defaultQualifiers) = indexedThisType[index] + val verticalSlice = indexedFromSupertypes.mapNotNull { it.getOrNull(index)?.type } // Only the head type constructor is safely co-variant - qualifiers.computeQualifiersForOverride(verticalSlice, isHeadTypeConstructor) + qualifiers.computeQualifiersForOverride(verticalSlice, defaultQualifiers, isHeadTypeConstructor) } return { index -> computedResult.getOrElse(index) { JavaTypeQualifiers.NONE } } } + + private fun KotlinType.toIndexed(): List { + val list = ArrayList(1) + + fun add(type: KotlinType, ownerContext: LazyJavaResolverContext) { + val c = ownerContext.copyWithNewDefaultTypeQualifiers(type.annotations) + + list.add( + TypeAndDefaultQualifiers( + type, + c.defaultTypeQualifiers + ?.get(AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE) + ) + ) + + for (arg in type.arguments) { + if (arg.isStarProjection) { + // TODO: sort out how to handle wildcards + list.add(TypeAndDefaultQualifiers(arg.type, null)) + } + else { + add(arg.type, c) + } + } + } + + add(this, containerContext) + return list + } + private fun KotlinType.computeQualifiersForOverride( fromSupertypes: Collection, + defaultQualifiersForType: JavaTypeQualifiers?, isHeadTypeConstructor: Boolean ): JavaTypeQualifiers { val superQualifiers = fromSupertypes.map { it.extractQualifiers() } @@ -319,7 +336,7 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati .mapNotNull { it.unwrapEnhancement().extractQualifiers().nullability } .toSet() - val own = extractQualifiersFromAnnotations(isHeadTypeConstructor) + val own = extractQualifiersFromAnnotations(isHeadTypeConstructor, defaultQualifiersForType) val ownNullability = own.takeIf { !it.isNullabilityQualifierForWarning }?.nullability val ownNullabilityForWarning = own.nullability @@ -355,7 +372,8 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati private fun D.parts( typeContainer: Annotated?, isCovariant: Boolean, - defaultTopLevelQualifiers: JavaTypeQualifiers?, + containerContext: LazyJavaResolverContext, + containerApplicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType, collector: (CallableMemberDescriptor) -> KotlinType ): SignatureParts { return SignatureParts( @@ -365,7 +383,9 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati collector(it) }, isCovariant, - defaultTopLevelQualifiers + // recompute default type qualifiers using type annotations + containerContext.copyWithNewDefaultTypeQualifiers(collector(this).annotations), + containerApplicabilityType ) } @@ -402,3 +422,8 @@ private fun Set.select(own: NullabilityQualifier?, isCovar NullabilityQualifier.FORCE_FLEXIBILITY else select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, own, isCovariant) + +private data class TypeAndDefaultQualifiers( + val type: KotlinType, + val defaultQualifiers: JavaTypeQualifiers? +)