diff --git a/compiler/testData/foreignAnnotations/annotations/javax/annotation/ParametersAreNonnullByDefault.java b/compiler/testData/foreignAnnotations/annotations/javax/annotation/ParametersAreNonnullByDefault.java new file mode 100644 index 00000000000..a2402b5d98f --- /dev/null +++ b/compiler/testData/foreignAnnotations/annotations/javax/annotation/ParametersAreNonnullByDefault.java @@ -0,0 +1,28 @@ +package javax.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.annotation.meta.TypeQualifierDefault; + +/** + * This annotation can be applied to a package, class or method to indicate that + * the method parameters in that element are nonnull by default unless there is: + * + * + * @see Nonnull + */ +@Documented +@Nonnull +@TypeQualifierDefault(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface ParametersAreNonnullByDefault { +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/fieldsAreNullable.kt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/fieldsAreNullable.kt new file mode 100644 index 00000000000..bf2f75921a9 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/fieldsAreNullable.kt @@ -0,0 +1,58 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +// FILE: FieldsAreNullable.java +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.CheckForNull; +import javax.annotation.meta.TypeQualifierDefault; + +@Retention(RetentionPolicy.RUNTIME) +@Documented +@CheckForNull +@TypeQualifierDefault({ElementType.FIELD}) +public @interface FieldsAreNullable { +} + +// FILE: A.java + +import javax.annotation.*; + +@FieldsAreNullable +public class A { + public String field = null; + @Nonnull + public String nonNullField = ""; + + public String foo(String q, @Nonnull String x, @CheckForNull CharSequence y) { + return ""; + } + + @Nonnull + public String bar() { + return ""; + } +} + +// FILE: main.kt + +fun main(a: A) { + // foo is platform + a.foo("", "", null)?.length + a.foo("", "", null).length + a.foo(null, null, "").length + + a.bar().length + a.bar()!!.length + + a.field?.length + a.field.length + a.field = null + + a.nonNullField?.length + a.nonNullField.length + a.nonNullField = null +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/fieldsAreNullable.txt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/fieldsAreNullable.txt new file mode 100644 index 00000000000..48ecc072f23 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/fieldsAreNullable.txt @@ -0,0 +1,21 @@ +package + +public fun main(/*0*/ a: A): kotlin.Unit + +@FieldsAreNullable public open class A { + public constructor A() + public final var field: kotlin.String? + @javax.annotation.Nonnull public final var nonNullField: kotlin.String + @javax.annotation.Nonnull public open fun bar(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ q: kotlin.String!, /*1*/ @javax.annotation.Nonnull x: kotlin.String, /*2*/ @javax.annotation.CheckForNull 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 +} + +@kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.CheckForNull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.FIELD}) public final annotation class FieldsAreNullable : kotlin.Annotation { + public constructor FieldsAreNullable() + 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/foreignAnnotations/tests/typeQualifierDefault/nullabilityFromOverridden.kt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/nullabilityFromOverridden.kt new file mode 100644 index 00000000000..afbd2c72be6 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/nullabilityFromOverridden.kt @@ -0,0 +1,158 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +// FILE: NonNullApi.java + +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.CLASS) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Nonnull +@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) +public @interface NonNullApi { +} + +// FILE: NullableApi.java + +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.CheckForNull; +import javax.annotation.meta.TypeQualifierDefault; + +@Target(ElementType.CLASS) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@CheckForNull +@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) +public @interface NullableApi { +} + +// FILE: A.java + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.Nonnull; + +@NonNullApi +public class A { + public String foo1(String x) { return ""; } + public String foo2(String x) { return ""; } + public String foo3(String x) { return ""; } + + + @Nullable + public String bar1(@Nullable String x) { return ""; } + @Nullable + public String bar2(@Nullable String x) { return ""; } + + public String baz(@Nonnull String x) { return ""; } +} + +// FILE: AInt.java + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.Nonnull; + +@NonNullApi +public interface AInt { + public CharSequence foo1(String x); + public CharSequence foo2(String x); + public CharSequence foo3(String x); + + + @Nullable + public CharSequence bar1(@Nullable String x); + @Nullable + public CharSequence bar2(@Nullable String x); + + public CharSequence baz(@Nonnull String x); +} + + +// FILE: B.java + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.Nonnull; + +@NullableApi +public class B extends A implements AInt { + // conflicts + public String foo1(String x) { return ""; } + + // no conflicts + @Nonnull + public String foo2(@Nonnull String x) { return ""; } + + // fake override for foo3 shouldn't have any conflicts + + // no conflicts + public String bar1(String x) { return ""; } + + // conflicts + public String baz(String x) { return ""; } +} + +// FILE: C.java + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.Nonnull; + +@NonNullApi +public class C extends A implements AInt { + // no conflicts + public String foo1(String x) { return ""; } + + // no conflicts + public String foo2(@Nonnull String x) { return ""; } + + // fake override for foo3 shouldn't have any conflicts + + // no conflicts, covariant override + public String bar1(String x) { return ""; } + // no conflicts + @Nullable + public String bar2(@Nullable String x) { return ""; } + + // no conflicts + public String baz(String x) { return ""; } +} + +// FILE: main.kt + +fun main(a: A, b: B, c: C) { + a.foo1(null).length + a.foo2(null).length + a.foo3(null).length + a.bar1(null).length + a.bar2(null).length + a.baz(null).length + + b.foo1(null).length + b.foo1(null)?.length + b.foo2(null).length + b.foo3(null).length + b.bar1(null).length + b.bar2(null).length + b.baz(null).length + b.baz(null)?.length + + c.foo1(null).length + c.foo2(null).length + c.foo3(null).length + c.bar1(null).length + c.bar1(null)?.length + c.bar2(null).length + c.baz(null).length +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/nullabilityFromOverridden.txt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/nullabilityFromOverridden.txt new file mode 100644 index 00000000000..1582b886fe4 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/nullabilityFromOverridden.txt @@ -0,0 +1,68 @@ +package + +public fun main(/*0*/ a: A, /*1*/ b: B, /*2*/ c: C): kotlin.Unit + +@NonNullApi public open class A { + public constructor A() + @javax.annotation.Nullable public open fun bar1(/*0*/ @javax.annotation.Nullable x: kotlin.String?): kotlin.String? + @javax.annotation.Nullable public open fun bar2(/*0*/ @javax.annotation.Nullable x: kotlin.String?): kotlin.String? + public open fun baz(/*0*/ @javax.annotation.Nonnull x: kotlin.String): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo1(/*0*/ x: kotlin.String): kotlin.String + public open fun foo2(/*0*/ x: kotlin.String): kotlin.String + public open fun foo3(/*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 +} + +@NonNullApi public interface AInt { + @javax.annotation.Nullable public abstract fun bar1(/*0*/ @javax.annotation.Nullable x: kotlin.String?): kotlin.CharSequence? + @javax.annotation.Nullable public abstract fun bar2(/*0*/ @javax.annotation.Nullable x: kotlin.String?): kotlin.CharSequence? + public abstract fun baz(/*0*/ @javax.annotation.Nonnull x: kotlin.String): kotlin.CharSequence + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo1(/*0*/ x: kotlin.String): kotlin.CharSequence + public abstract fun foo2(/*0*/ x: kotlin.String): kotlin.CharSequence + public abstract fun foo3(/*0*/ x: kotlin.String): kotlin.CharSequence + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@NullableApi public open class B : A, AInt { + public constructor B() + public open override /*2*/ fun bar1(/*0*/ x: kotlin.String?): kotlin.String? + @javax.annotation.Nullable public open override /*2*/ /*fake_override*/ fun bar2(/*0*/ @javax.annotation.Nullable x: kotlin.String?): kotlin.String? + public open override /*2*/ fun baz(/*0*/ x: kotlin.String!): kotlin.String! + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun foo1(/*0*/ x: kotlin.String!): kotlin.String! + @javax.annotation.Nonnull public open override /*2*/ fun foo2(/*0*/ @javax.annotation.Nonnull x: kotlin.String): kotlin.String + public open override /*2*/ /*fake_override*/ fun foo3(/*0*/ x: kotlin.String): kotlin.String + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +@NonNullApi public open class C : A, AInt { + public constructor C() + public open override /*2*/ fun bar1(/*0*/ x: kotlin.String!): kotlin.String + @javax.annotation.Nullable public open override /*2*/ fun bar2(/*0*/ @javax.annotation.Nullable x: kotlin.String?): kotlin.String? + public open override /*2*/ fun baz(/*0*/ x: kotlin.String): kotlin.String + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun foo1(/*0*/ x: kotlin.String): kotlin.String + public open override /*2*/ fun foo2(/*0*/ @javax.annotation.Nonnull x: kotlin.String): kotlin.String + public open override /*2*/ /*fake_override*/ fun foo3(/*0*/ x: kotlin.String): kotlin.String + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.annotation.Target(allowedTargets = {}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.Nonnull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 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 = {}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.CheckForNull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 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/foreignAnnotations/tests/typeQualifierDefault/overridingDefaultQualifier.kt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/overridingDefaultQualifier.kt new file mode 100644 index 00000000000..c606ae1acfa --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/overridingDefaultQualifier.kt @@ -0,0 +1,186 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +// FILE: NonNullApi.java + +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.METHOD, ElementType.PARAMETER, ElementType.FIELD}) +public @interface NonNullApi { +} + +// FILE: NullableApi.java + +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.CheckForNull; +import javax.annotation.meta.TypeQualifierDefault; + +@Target(ElementType.PACKAGE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@CheckForNull +@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) +public @interface NullableApi { +} + +// FILE: FieldsAreNullable.java +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.CheckForNull; +import javax.annotation.meta.TypeQualifierDefault; + +@Retention(RetentionPolicy.RUNTIME) +@Documented +@CheckForNull +@TypeQualifierDefault({ElementType.FIELD}) +public @interface FieldsAreNullable { +} + +// FILE: A.java + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.Nonnull; + +@NonNullApi +public class A { + public String field = null; + + public String foo(String x, @CheckForNull CharSequence y) { + return ""; + } + + @NullableApi + public String foobar(String x, @Nonnull CharSequence y) { + return ""; + } + + public String bar() { + return ""; + } + + @Nullable + public java.util.List baz() { + return null; + } + + @NullableApi + public class B { + public String field = null; + + public String foo(String x, @Nonnull CharSequence y) { + return ""; + } + + @NonNullApi + public String foobar(String x, @Nullable CharSequence y) { + return ""; + } + + public String bar() { + return ""; + } + + @Nullable + public java.util.List baz() { + return null; + } + } + + @FieldsAreNullable + public class C { + public String field = null; + + public String foo(String x, @Nullable CharSequence y) { + return ""; + } + + @NullableApi + public String foobar(String x, @Nullable CharSequence y) { + return ""; + } + + public String bar() { + return ""; + } + + @Nullable + public java.util.List baz() { + return null; + } + } +} + +// FILE: main.kt + +fun main(a: A, b: A.B, c: A.C) { + a.foo("", null)?.length + a.foo("", null).length + a.foo(null, "").length + + a.foobar(null, "").length + a.foobar("", null)?.length + + a.bar().length + a.bar()!!.length + + a.field?.length + a.field.length + + a.baz().get(0) + a.baz()!!.get(0).get(0) + a.baz()!!.get(0)?.get(0) + + // b + b.foo("", null)?.length + b.foo("", null).length + b.foo(null, "").length + + b.foobar(null, "").length + b.foobar("", null)?.length + + b.bar().length + b.bar()!!.length + + b.field?.length + b.field.length + + b.baz().get(0) + b.baz()!!.get(0).get(0) + b.baz()!!.get(0)?.get(0) + + // c + c.foo("", null)?.length + c.foo("", null).length + c.foo(null, "").length + + c.foobar(null, "").length + c.foobar("", null)?.length + + c.bar().length + c.bar()!!.length + + c.field?.length + c.field.length + + c.baz().get(0) + c.baz()!!.get(0).get(0) + c.baz()!!.get(0)?.get(0) +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/overridingDefaultQualifier.txt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/overridingDefaultQualifier.txt new file mode 100644 index 00000000000..d67706a2400 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/overridingDefaultQualifier.txt @@ -0,0 +1,60 @@ +package + +public fun main(/*0*/ a: A, /*1*/ b: A.B, /*2*/ c: A.C): kotlin.Unit + +@NonNullApi public open class A { + public constructor A() + public final var field: kotlin.String + public open fun bar(): kotlin.String + @javax.annotation.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*/ @javax.annotation.CheckForNull y: kotlin.CharSequence?): kotlin.String + @NullableApi public open fun foobar(/*0*/ x: kotlin.String?, /*1*/ @javax.annotation.Nonnull 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 + + @NullableApi public open inner class B { + public constructor B() + public final var field: kotlin.String? + public open fun bar(): kotlin.String? + @javax.annotation.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*/ @javax.annotation.Nonnull y: kotlin.CharSequence): kotlin.String? + @NonNullApi public open fun foobar(/*0*/ x: kotlin.String, /*1*/ @javax.annotation.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 + } + + @FieldsAreNullable public open inner class C { + public constructor C() + public final var field: kotlin.String? + public open fun bar(): kotlin.String + @javax.annotation.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*/ @javax.annotation.Nullable y: kotlin.CharSequence?): kotlin.String + @NullableApi public open fun foobar(/*0*/ x: kotlin.String?, /*1*/ @javax.annotation.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 + } +} + +@kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.CheckForNull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.FIELD}) public final annotation class FieldsAreNullable : kotlin.Annotation { + public constructor FieldsAreNullable() + 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 = {}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.Nonnull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 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 = {}) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) @kotlin.annotation.MustBeDocumented @javax.annotation.CheckForNull @javax.annotation.meta.TypeQualifierDefault(value = {ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 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/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefault.kt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefault.kt new file mode 100644 index 00000000000..b613006cb2f --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefault.kt @@ -0,0 +1,33 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER +// FILE: A.java + +import javax.annotation.*; + +@ParametersAreNonnullByDefault +public class A { + @Nullable public String field = null; + + public String foo(String q, @Nonnull String x, @CheckForNull CharSequence y) { + return ""; + } + + @Nonnull + public String bar() { + return ""; + } +} + +// FILE: main.kt + +fun main(a: A) { + // foo is platform + a.foo("", "", null)?.length + a.foo("", "", null).length + a.foo(null, null, "").length + + a.bar().length + a.bar()!!.length + + a.field?.length + a.field.length +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefault.txt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefault.txt new file mode 100644 index 00000000000..b1225b4032e --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefault.txt @@ -0,0 +1,13 @@ +package + +public fun main(/*0*/ a: A): kotlin.Unit + +@javax.annotation.ParametersAreNonnullByDefault public open class A { + public constructor A() + @javax.annotation.Nullable public final var field: kotlin.String? + @javax.annotation.Nonnull public open fun bar(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ q: kotlin.String, /*1*/ @javax.annotation.Nonnull x: kotlin.String, /*2*/ @javax.annotation.CheckForNull 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 +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefaultPackage.kt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefaultPackage.kt new file mode 100644 index 00000000000..84bb68a0baf --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefaultPackage.kt @@ -0,0 +1,73 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER +// RENDER_PACKAGE: test +// RENDER_PACKAGE: test2 + +// FILE: test/package-info.java + +@javax.annotation.ParametersAreNonnullByDefault() +package test; + +// FILE: test/A.java + +package test; + +import javax.annotation.*; + +public class A { + @Nullable public String field = null; + + public String foo(String q, @Nonnull String x, @CheckForNull CharSequence y) { + return ""; + } + + @Nonnull + public String bar() { + return ""; + } +} + +// FILE: test2/A2.java + +package test2; + +import javax.annotation.*; + +public class A2 { + @Nullable public String field = null; + + public String foo(String q, @Nonnull String x, @CheckForNull CharSequence y) { + return ""; + } + + @Nonnull + public String bar() { + return ""; + } +} + +// FILE: main.kt + +import test.A +import test2.A2 + +fun main(a: A, a2: A2) { + a.foo("", "", null)?.length + a.foo("", "", null).length + a.foo(null, null, "").length + + a.bar().length + a.bar()!!.length + + a.field?.length + a.field.length + + a2.foo("", "", null)?.length + a2.foo("", "", null).length + a2.foo(null, null, "").length + + a2.bar().length + a2.bar()!!.length + + a2.field?.length + a2.field.length +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefaultPackage.txt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefaultPackage.txt new file mode 100644 index 00000000000..6b6fb798d28 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefaultPackage.txt @@ -0,0 +1,27 @@ +package test + +public open class A { + public constructor A() + @javax.annotation.Nullable public final var field: kotlin.String? + @javax.annotation.Nonnull public open fun bar(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ q: kotlin.String, /*1*/ @javax.annotation.Nonnull x: kotlin.String, /*2*/ @javax.annotation.CheckForNull 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 test2 + +public open class A2 { + public constructor A2() + @javax.annotation.Nullable public final var field: kotlin.String? + @javax.annotation.Nonnull public open fun bar(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ q: kotlin.String!, /*1*/ @javax.annotation.Nonnull x: kotlin.String, /*2*/ @javax.annotation.CheckForNull 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 + +public fun main(/*0*/ a: test.A, /*1*/ a2: test2.A2): kotlin.Unit diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullable.kt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullable.kt new file mode 100644 index 00000000000..639f8e95e28 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullable.kt @@ -0,0 +1,82 @@ +// !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.CLASS) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Nonnull +@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER}) +public @interface NonNullApi { +} + +// FILE: A.java + +import spr.*; + +@NonNullApi +public class A { + public String field = null; + + public String foo(String x, @Nullable CharSequence y) { + return ""; + } + + public String bar() { + return ""; + } + + @Nullable + public java.util.List baz() { + return null; + } +} + +// FILE: main.kt + +fun main(a: A) { + a.foo("", null)?.length + a.foo("", null).length + a.foo(null, "").length + + a.bar().length + a.bar()!!.length + + a.field?.length + a.field.length + + a.baz().get(0) + a.baz()!!.get(0).get(0) + a.baz()!!.get(0)?.get(0) +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullable.txt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullable.txt new file mode 100644 index 00000000000..45b243c67ee --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullable.txt @@ -0,0 +1,14 @@ +package + +public fun main(/*0*/ a: A): kotlin.Unit + +@spr.NonNullApi public open class A { + 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? + 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 +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullablePackage.kt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullablePackage.kt new file mode 100644 index 00000000000..a78be048348 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullablePackage.kt @@ -0,0 +1,89 @@ +// !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.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.PACKAGE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Nonnull +@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER}) +public @interface NonNullApi { +} + +// FILE: test/package-info.java + +@spr.NonNullApi() +package test; + +// FILE: test/A.java + +package test; + +import spr.*; + +public class A { + public String field = null; + + public String foo(String x, @Nullable CharSequence y) { + return ""; + } + + public String bar() { + return ""; + } + + @Nullable + public java.util.List baz() { + return null; + } +} + +// FILE: main.kt + +fun main(a: test.A) { + a.foo("", null)?.length + a.foo("", null).length + a.foo(null, "").length + + a.bar().length + a.bar()!!.length + + a.field?.length + a.field.length + + a.baz().get(0) + a.baz()!!.get(0).get(0) + a.baz()!!.get(0)?.get(0) +} diff --git a/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullablePackage.txt b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullablePackage.txt new file mode 100644 index 00000000000..615020e4e92 --- /dev/null +++ b/compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullablePackage.txt @@ -0,0 +1,16 @@ +package test + +public open class A { + 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? + 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 + +public fun main(/*0*/ a: test.A): kotlin.Unit diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/CheckForNull.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/CheckForNull.java new file mode 100644 index 00000000000..6fe52005c26 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/CheckForNull.java @@ -0,0 +1,16 @@ +package 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.MAYBE) +@Retention(RetentionPolicy.RUNTIME) +public @interface CheckForNull { + +} diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/Nonnull.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/Nonnull.java new file mode 100644 index 00000000000..4b7aad97c70 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/Nonnull.java @@ -0,0 +1,26 @@ +package javax.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.annotation.meta.TypeQualifier; +import javax.annotation.meta.TypeQualifierValidator; +import javax.annotation.meta.When; + +@Documented +@TypeQualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Nonnull { + When when() default When.ALWAYS; + + static class Checker implements TypeQualifierValidator { + + public When forConstantValue(Nonnull qualifierqualifierArgument, + Object value) { + if (value == null) + return When.NEVER; + return When.ALWAYS; + } + } +} diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/Nullable.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/Nullable.java new file mode 100644 index 00000000000..d31993dbd77 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/Nullable.java @@ -0,0 +1,16 @@ +package 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 Nullable { + +} diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/ParametersAreNonnullByDefault.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/ParametersAreNonnullByDefault.java new file mode 100644 index 00000000000..a2402b5d98f --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/ParametersAreNonnullByDefault.java @@ -0,0 +1,28 @@ +package javax.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.annotation.meta.TypeQualifierDefault; + +/** + * This annotation can be applied to a package, class or method to indicate that + * the method parameters in that element are nonnull by default unless there is: + *
    + *
  • An explicit nullness annotation + *
  • The method overrides a method in a superclass (in which case the + * annotation of the corresponding parameter in the superclass applies) + *
  • There is a default parameter annotation (like {@link ParametersAreNullableByDefault}) + * applied to a more tightly nested element. + *
+ * + * @see Nonnull + */ +@Documented +@Nonnull +@TypeQualifierDefault(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface ParametersAreNonnullByDefault { +} diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifier.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifier.java new file mode 100644 index 00000000000..99f63127277 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifier.java @@ -0,0 +1,27 @@ +package javax.annotation.meta; + +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; + +/** + * This qualifier is applied to an annotation to denote that the annotation + * should be treated as a type qualifier. + */ + +@Documented +@Target(ElementType.ANNOTATION_TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface TypeQualifier { + + /** + * Describes the kinds of values the qualifier can be applied to. If a + * numeric class is provided (e.g., Number.class or Integer.class) then the + * annotation can also be applied to the corresponding primitive numeric + * types. + */ + Class applicableTo() default Object.class; + +} diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierDefault.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierDefault.java new file mode 100644 index 00000000000..0c8bd528fc4 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierDefault.java @@ -0,0 +1,20 @@ +package javax.annotation.meta; + +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; + +/** + * This qualifier is applied to an annotation to denote that the annotation + * defines a default type qualifier that is visible within the scope of the + * element it is applied to. + */ + +@Documented +@Target(ElementType.ANNOTATION_TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface TypeQualifierDefault { + ElementType[] value() default {}; +} diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierNickname.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierNickname.java new file mode 100644 index 00000000000..40c998382da --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierNickname.java @@ -0,0 +1,33 @@ +package javax.annotation.meta; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * + * This annotation is applied to a annotation, and marks the annotation as being + * a qualifier nickname. Applying a nickname annotation X to a element Y should + * be interpreted as having the same meaning as applying all of annotations of X + * (other than QualifierNickname) to Y. + * + *

+ * Thus, you might define a qualifier SocialSecurityNumber as follows: + *

+ * + * + * + @Documented + @TypeQualifierNickname @Pattern("[0-9]{3}-[0-9]{2}-[0-9]{4}") + @Retention(RetentionPolicy.RUNTIME) + public @interface SocialSecurityNumber { + } + + * + * + */ +@Documented +@Target(ElementType.ANNOTATION_TYPE) +public @interface TypeQualifierNickname { + +} diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierValidator.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierValidator.java new file mode 100644 index 00000000000..8053011296b --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/TypeQualifierValidator.java @@ -0,0 +1,21 @@ +package javax.annotation.meta; + +import java.lang.annotation.Annotation; + +import javax.annotation.Nonnull; + +public interface TypeQualifierValidator { + /** + * Given a type qualifier, check to see if a known specific constant value + * is an instance of the set of values denoted by the qualifier. + * + * @param annotation + * the type qualifier + * @param value + * the value to check + * @return a value indicating whether or not the value is an member of the + * values denoted by the type qualifier + */ + public @Nonnull + When forConstantValue(@Nonnull A annotation, Object value); +} diff --git a/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/When.java b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/When.java new file mode 100644 index 00000000000..ec8a1bc7037 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/annotations/javax/annotation/meta/When.java @@ -0,0 +1,23 @@ +package javax.annotation.meta; + +/** + * Used to describe the relationship between a qualifier T and the set of values + * S possible on an annotated element. + * + * In particular, an issues should be reported if an ALWAYS or MAYBE value is + * used where a NEVER value is required, or if a NEVER or MAYBE value is used + * where an ALWAYS value is required. + * + * + */ +public enum When { + /** S is a subset of T */ + ALWAYS, + /** nothing definitive is known about the relation between S and T */ + UNKNOWN, + /** S intersection T is non empty and S - T is nonempty */ + MAYBE, + /** S intersection T is empty */ + NEVER; + +} diff --git a/compiler/testData/foreignAnnotationsJava8/tests/springNullableWithTypeUse.kt b/compiler/testData/foreignAnnotationsJava8/tests/springNullableWithTypeUse.kt new file mode 100644 index 00000000000..c400c6ed766 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/tests/springNullableWithTypeUse.kt @@ -0,0 +1,82 @@ +// !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.CLASS) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Nonnull +@TypeQualifierDefault({ElementType.TYPE_USE}) +public @interface NonNullApi { +} + +// FILE: A.java + +import spr.*; + +@NonNullApi +public class A { + public String field = null; + + public String foo(String x, @Nullable CharSequence y) { + return ""; + } + + public String bar() { + return ""; + } + + @Nullable + public java.util.List baz() { + return null; + } +} + +// FILE: main.kt + +fun main(a: A) { + a.foo("", null)?.length + a.foo("", null).length + a.foo(null, "").length + + a.bar().length + a.bar()!!.length + + a.field?.length + a.field.length + + a.baz().get(0) + a.baz()!!.get(0).get(0) + a.baz()!!.get(0)?.get(0) +} diff --git a/compiler/testData/foreignAnnotationsJava8/tests/springNullableWithTypeUse.txt b/compiler/testData/foreignAnnotationsJava8/tests/springNullableWithTypeUse.txt new file mode 100644 index 00000000000..188ef5ec1f0 --- /dev/null +++ b/compiler/testData/foreignAnnotationsJava8/tests/springNullableWithTypeUse.txt @@ -0,0 +1,14 @@ +package + +public fun main(/*0*/ a: A): kotlin.Unit + +@spr.NonNullApi public open class A { + 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? + 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 +} diff --git a/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt b/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt index 1f435328de0..f716ad8d222 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt +++ b/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt @@ -66,6 +66,7 @@ import org.jetbrains.kotlin.test.util.DescriptorValidator import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE_ALL +import org.jetbrains.kotlin.utils.keysToMap import org.junit.Assert import java.io.File import java.util.* @@ -363,7 +364,15 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() { val comparator = RecursiveDescriptorComparator(createdAffectedPackagesConfiguration(testFiles, modules.values)) val isMultiModuleTest = modules.size != 1 - val rootPackageText = StringBuilder() + + val packages = + (testFiles.flatMap { + InTextDirectivesUtils.findListWithPrefixes(it.expectedText, "// RENDER_PACKAGE:").map { + FqName(it.trim()) + } + } + FqName.ROOT).toSet() + + val textByPackage = packages.keysToMap { StringBuilder() } val sortedModules = modules.keys.sortedWith(Comparator { x, y -> when { @@ -375,32 +384,37 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() { } }) - val module = sortedModules.iterator() - while (module.hasNext()) { - val moduleDescriptor = modules[module.next()]!! - val aPackage = moduleDescriptor.getPackage(FqName.ROOT) - assertFalse(aPackage.isEmpty()) + for ((packageName, packageText) in textByPackage.entries) { + val module = sortedModules.iterator() + while (module.hasNext()) { + val moduleDescriptor = modules[module.next()]!! - if (isMultiModuleTest) { - rootPackageText.append(String.format("// -- Module: %s --\n", moduleDescriptor.name)) - } + val aPackage = moduleDescriptor.getPackage(packageName) + assertFalse(aPackage.isEmpty()) - val actualSerialized = comparator.serializeRecursively(aPackage) - rootPackageText.append(actualSerialized) + if (isMultiModuleTest) { + packageText.append(String.format("// -- Module: %s --\n", moduleDescriptor.name)) + } - if (isMultiModuleTest && module.hasNext()) { - rootPackageText.append("\n\n") + val actualSerialized = comparator.serializeRecursively(aPackage) + packageText.append(actualSerialized) + + if (isMultiModuleTest && module.hasNext()) { + packageText.append("\n\n") + } } } - val lineCount = StringUtil.getLineBreakCount(rootPackageText) + val allPackagesText = textByPackage.values.joinToString("\n") + + val lineCount = StringUtil.getLineBreakCount(allPackagesText) assert(lineCount < 1000) { "Rendered descriptors of this test take up $lineCount lines. " + "Please ensure you don't render JRE contents to the .txt file. " + "Such tests are hard to maintain, take long time to execute and are subject to sudden unreviewed changes anyway." } - KotlinTestUtils.assertEqualsToFile(expectedFile, rootPackageText.toString()) + KotlinTestUtils.assertEqualsToFile(expectedFile, allPackagesText) } private fun createdAffectedPackagesConfiguration( 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 b8430cefc39..a4ec796498a 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/ForeignJava8AnnotationsTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/checkers/ForeignJava8AnnotationsTestGenerated.java @@ -48,6 +48,12 @@ public class ForeignJava8AnnotationsTestGenerated extends AbstractForeignJava8An doTest(fileName); } + @TestMetadata("springNullableWithTypeUse.kt") + public void testSpringNullableWithTypeUse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotationsJava8/tests/springNullableWithTypeUse.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/foreignAnnotationsJava8/tests/typeEnhancement") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/ForeignAnnotationsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/ForeignAnnotationsTestGenerated.java index 91ca10c2c55..a4f8c0a06c6 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/ForeignAnnotationsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/ForeignAnnotationsTestGenerated.java @@ -89,4 +89,55 @@ public class ForeignAnnotationsTestGenerated extends AbstractForeignAnnotationsT String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/rxjava.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/foreignAnnotations/tests/typeQualifierDefault") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeQualifierDefault extends AbstractForeignAnnotationsTest { + public void testAllFilesPresentInTypeQualifierDefault() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/foreignAnnotations/tests/typeQualifierDefault"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("fieldsAreNullable.kt") + public void testFieldsAreNullable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/typeQualifierDefault/fieldsAreNullable.kt"); + doTest(fileName); + } + + @TestMetadata("nullabilityFromOverridden.kt") + public void testNullabilityFromOverridden() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/typeQualifierDefault/nullabilityFromOverridden.kt"); + doTest(fileName); + } + + @TestMetadata("overridingDefaultQualifier.kt") + public void testOverridingDefaultQualifier() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/typeQualifierDefault/overridingDefaultQualifier.kt"); + doTest(fileName); + } + + @TestMetadata("parametersAreNonnullByDefault.kt") + public void testParametersAreNonnullByDefault() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefault.kt"); + doTest(fileName); + } + + @TestMetadata("parametersAreNonnullByDefaultPackage.kt") + public void testParametersAreNonnullByDefaultPackage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/typeQualifierDefault/parametersAreNonnullByDefaultPackage.kt"); + doTest(fileName); + } + + @TestMetadata("springNullable.kt") + public void testSpringNullable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullable.kt"); + doTest(fileName); + } + + @TestMetadata("springNullablePackage.kt") + public void testSpringNullablePackage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/foreignAnnotations/tests/typeQualifierDefault/springNullablePackage.kt"); + doTest(fileName); + } + } } diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaPackageAnnotationsTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaPackageAnnotationsTest.kt index 3f41f83d342..1c4251daff0 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaPackageAnnotationsTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaPackageAnnotationsTest.kt @@ -68,10 +68,10 @@ class LoadJavaPackageAnnotationsTest : KtUsefulTestCase() { it.getMemberScope().getContributedClassifier(Name.identifier("A"), NoLookupLocation.FROM_TEST) != null }.let { assertInstanceOf(it, LazyJavaPackageFragment::class.java) } - val annotation = packageFragmentDescriptor.getPackageAnnotations().findAnnotation(FqName("test.Ann")) + val annotation = packageFragmentDescriptor.annotations.findAnnotation(FqName("test.Ann")) assertNotNull(annotation) - val singleAnnotation = packageFragmentDescriptor.getPackageAnnotations().singleOrNull() + val singleAnnotation = packageFragmentDescriptor.annotations.singleOrNull() assertNotNull(singleAnnotation) val annotationFqName = singleAnnotation!!.type.constructor.declarationDescriptor?.fqNameSafe diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt index d449c8be01e..d1bfce4c17c 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt @@ -20,12 +20,16 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.constants.ArrayValue +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.resolve.constants.EnumValue import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult private val TYPE_QUALIFIER_NICKNAME_FQNAME = FqName("javax.annotation.meta.TypeQualifierNickname") private val TYPE_QUALIFIER_FQNAME = FqName("javax.annotation.meta.TypeQualifier") +private val TYPE_QUALIFIER_DEFAULT_FQNAME = FqName("javax.annotation.meta.TypeQualifierDefault") class AnnotationTypeQualifierResolver(storageManager: StorageManager) { private val resolvedNicknames = @@ -49,6 +53,58 @@ class AnnotationTypeQualifierResolver(storageManager: StorageManager) { return resolveTypeQualifierNickname(annotationClass) } + + enum class QualifierApplicabilityType { + METHOD_RETURN_TYPE, VALUE_PARAMETER, FIELD, TYPE_USE + } + + class TypeQualifierWithApplicability( + 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) + } + + fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? { + val typeQualifierDefaultAnnotatedClass = + annotationDescriptor.annotationClass?.takeIf { it.annotations.hasAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) } + ?: return null + + val elementTypesMask = + annotationDescriptor.annotationClass!! + .annotations.findAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME)!! + .allValueArguments + .flatMap { (parameter, argument) -> + if (parameter.name == JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME) + argument.mapConstantToQualifierApplicabilityTypes() + else + emptyList() + } + .fold(0) { acc: Int, applicabilityType -> acc or (1 shl applicabilityType.ordinal) } + + val typeQualifier = + typeQualifierDefaultAnnotatedClass.annotations.firstNotNullResult(this::resolveTypeQualifierAnnotation) + ?: return null + return TypeQualifierWithApplicability(typeQualifier, elementTypesMask) + } + + private fun ConstantValue<*>.mapConstantToQualifierApplicabilityTypes(): List = + when (this) { + is ArrayValue -> value.flatMap { it.mapConstantToQualifierApplicabilityTypes() } + is EnumValue -> listOfNotNull( + when (value.name.identifier) { + "METHOD" -> QualifierApplicabilityType.METHOD_RETURN_TYPE + "FIELD" -> QualifierApplicabilityType.FIELD + "PARAMETER" -> QualifierApplicabilityType.VALUE_PARAMETER + "TYPE_USE" -> QualifierApplicabilityType.TYPE_USE + else -> null + } + ) + else -> emptyList() + } } private val ClassDescriptor.isAnnotatedWithTypeQualifier: Boolean diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaPackageFragmentProvider.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaPackageFragmentProvider.kt index dc44bdb7395..da4351d97a3 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaPackageFragmentProvider.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaPackageFragmentProvider.kt @@ -26,7 +26,7 @@ class LazyJavaPackageFragmentProvider( components: JavaResolverComponents ) : PackageFragmentProvider { - private val c = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) + private val c = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) { null } private val packageFragments: MemoizedFunctionToNullable = c.storageManager.createMemoizedFunctionWithNullableValues { 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 1785155d63b..1dbb527d521 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 @@ -17,10 +17,8 @@ package org.jetbrains.kotlin.load.java.lazy import org.jetbrains.kotlin.builtins.ReflectionTypes -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.PackagePartProvider -import org.jetbrains.kotlin.descriptors.SupertypeLoopChecker +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver import org.jetbrains.kotlin.load.java.JavaClassFinder @@ -28,11 +26,14 @@ import org.jetbrains.kotlin.load.java.components.* import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner +import org.jetbrains.kotlin.load.java.typeEnhancement.JavaTypeQualifiers +import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter import org.jetbrains.kotlin.storage.StorageManager +import java.util.* class JavaResolverComponents( val storageManager: StorageManager, @@ -66,10 +67,33 @@ class JavaResolverComponents( ) } -class LazyJavaResolverContext( - val components: JavaResolverComponents, - val typeParameterResolver: TypeParameterResolver +private typealias QualifierByApplicabilityType = EnumMap + +class JavaTypeQualifiersByElementType( + internal val nullabilityQualifiers: QualifierByApplicabilityType ) { + operator fun get( + applicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType + ): JavaTypeQualifiers? = + ( + nullabilityQualifiers[applicabilityType] + ?: nullabilityQualifiers[AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE] + )?.let { JavaTypeQualifiers(it, null, isNotNullTypeParameter = false) } +} + +class LazyJavaResolverContext internal constructor( + val components: JavaResolverComponents, + val typeParameterResolver: TypeParameterResolver, + internal val delegateForDefaultTypeQualifiers: Lazy +) { + constructor( + components: JavaResolverComponents, + typeParameterResolver: TypeParameterResolver, + typeQualifiersComputation: () -> JavaTypeQualifiersByElementType? + ) : this(components, typeParameterResolver, lazy(LazyThreadSafetyMode.NONE, typeQualifiersComputation)) + + val defaultTypeQualifiers: JavaTypeQualifiersByElementType? by delegateForDefaultTypeQualifiers + val typeResolver = JavaTypeResolver(this, typeParameterResolver) val storageManager: StorageManager @@ -80,14 +104,59 @@ class LazyJavaResolverContext( fun LazyJavaResolverContext.child( typeParameterResolver: TypeParameterResolver -) = LazyJavaResolverContext(components, typeParameterResolver) +) = LazyJavaResolverContext(components, typeParameterResolver, delegateForDefaultTypeQualifiers) + +fun LazyJavaResolverContext.computeNewDefaultTypeQualifiers( + additionalAnnotations: Annotations +): JavaTypeQualifiersByElementType? { + val typeQualifierDefaults = + additionalAnnotations.mapNotNull(components.annotationTypeQualifierResolver::resolveTypeQualifierDefaultAnnotation) + + if (typeQualifierDefaults.isEmpty()) return defaultTypeQualifiers + + val nullabilityQualifiers = + defaultTypeQualifiers?.nullabilityQualifiers?.let(::QualifierByApplicabilityType) + ?: QualifierByApplicabilityType(AnnotationTypeQualifierResolver.QualifierApplicabilityType::class.java) + + var wasUpdate = false + for ((typeQualifier, applicableTo) in typeQualifierDefaults) { + val nullability = components.signatureEnhancement.extractNullability(typeQualifier) ?: continue + for (applicabilityType in applicableTo) { + nullabilityQualifiers[applicabilityType] = nullability + wasUpdate = true + } + } + + return if (!wasUpdate) defaultTypeQualifiers else JavaTypeQualifiersByElementType(nullabilityQualifiers) +} fun LazyJavaResolverContext.replaceComponents( components: JavaResolverComponents -) = LazyJavaResolverContext(components, typeParameterResolver) +) = LazyJavaResolverContext(components, typeParameterResolver, delegateForDefaultTypeQualifiers) -fun LazyJavaResolverContext.child( +private fun LazyJavaResolverContext.child( + containingDeclaration: DeclarationDescriptor, + typeParameterOwner: JavaTypeParameterListOwner?, + typeParametersIndexOffset: Int = 0, + delegateForTypeQualifiers: Lazy +) = LazyJavaResolverContext( + components, + typeParameterOwner?.let { LazyJavaTypeParameterResolver(this, containingDeclaration, it, typeParametersIndexOffset) } + ?: typeParameterResolver, + delegateForTypeQualifiers +) + +fun LazyJavaResolverContext.childForMethod( containingDeclaration: DeclarationDescriptor, typeParameterOwner: JavaTypeParameterListOwner, typeParametersIndexOffset: Int = 0 -) = this.child(LazyJavaTypeParameterResolver(this, containingDeclaration, typeParameterOwner, typeParametersIndexOffset)) +) = child(containingDeclaration, typeParameterOwner, typeParametersIndexOffset, delegateForDefaultTypeQualifiers) + +fun LazyJavaResolverContext.childForClassOrPackage( + containingDeclaration: ClassOrPackageFragmentDescriptor, + typeParameterOwner: JavaTypeParameterListOwner? = null, + typeParametersIndexOffset: Int = 0 +) = child( + containingDeclaration, typeParameterOwner, typeParametersIndexOffset, + lazy(LazyThreadSafetyMode.NONE) { computeNewDefaultTypeQualifiers(containingDeclaration.annotations) } +) diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt index ced8d9ed4f2..d61dd0d31b3 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.load.java.components.JavaResolverCache import org.jetbrains.kotlin.load.java.components.TypeUsage import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext -import org.jetbrains.kotlin.load.java.lazy.child +import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage import org.jetbrains.kotlin.load.java.lazy.replaceComponents import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations import org.jetbrains.kotlin.load.java.lazy.types.toAttributes @@ -64,7 +64,7 @@ class LazyJavaClassDescriptor( private val PUBLIC_METHOD_NAMES_IN_OBJECT = setOf("equals", "hashCode", "getClass", "wait", "notify", "notifyAll", "toString") } - private val c: LazyJavaResolverContext = outerContext.child(this, jClass) + private val c: LazyJavaResolverContext = outerContext.childForClassOrPackage(this, jClass) init { c.components.javaResolverCache.recordClass(jClass, this) diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt index 0a4bb9178bf..4cad381e9ee 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt @@ -37,7 +37,7 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor import org.jetbrains.kotlin.load.java.descriptors.copyValueParameters import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext -import org.jetbrains.kotlin.load.java.lazy.child +import org.jetbrains.kotlin.load.java.lazy.childForMethod import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations import org.jetbrains.kotlin.load.java.lazy.types.toAttributes import org.jetbrains.kotlin.load.java.structure.JavaArrayType @@ -86,6 +86,7 @@ class LazyJavaClassMemberScope( } c.components.signatureEnhancement.enhanceSignatures( + c, result.ifEmpty { listOfNotNull(createDefaultConstructor()) } ).toList() } @@ -424,7 +425,7 @@ class LazyJavaClassMemberScope( val getter = DescriptorFactory.createDefaultGetter(propertyDescriptor, Annotations.EMPTY) propertyDescriptor.initialize(getter, null) - val returnType = givenType ?: computeMethodReturnType(method, annotations, c.child(propertyDescriptor, method)) + val returnType = givenType ?: computeMethodReturnType(method, annotations, c.childForMethod(propertyDescriptor, method)) propertyDescriptor.setType(returnType, listOf(), getDispatchReceiverParameter(), null as KotlinType?) getter.initialize(returnType) @@ -511,7 +512,7 @@ class LazyJavaClassMemberScope( ) - val c = c.child(constructorDescriptor, constructor, typeParametersIndexOffset = classDescriptor.declaredTypeParameters.size) + val c = c.childForMethod(constructorDescriptor, constructor, typeParametersIndexOffset = classDescriptor.declaredTypeParameters.size) val valueParameters = resolveValueParameters(c, constructorDescriptor, constructor.valueParameters) val constructorTypeParameters = classDescriptor.declaredTypeParameters + diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageFragment.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageFragment.kt index f7fceb9bf81..9f7cc9af6b5 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageFragment.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageFragment.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext +import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.JavaPackage @@ -31,9 +32,11 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.storage.getValue class LazyJavaPackageFragment( - private val c: LazyJavaResolverContext, + outerContext: LazyJavaResolverContext, private val jPackage: JavaPackage -) : PackageFragmentDescriptorImpl(c.module, jPackage.fqName) { +) : PackageFragmentDescriptorImpl(outerContext.module, jPackage.fqName) { + private val c = outerContext.childForClassOrPackage(this) + internal val binaryClasses by c.storageManager.createLazyValue { c.components.packageMapper.findPackageParts(fqName.asString()).mapNotNull { partName -> val classId = ClassId(fqName, Name.identifier(partName)) @@ -49,10 +52,7 @@ class LazyJavaPackageFragment( onRecursiveCall = listOf() ) - private val packageAnnotations = c.resolveAnnotations(jPackage) - - // Test only method - fun getPackageAnnotations() = packageAnnotations + override val annotations = c.resolveAnnotations(jPackage) internal fun getSubPackageFqNames(): List = subPackages() diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt index 2e02a31f9e5..6895c9e6d45 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlin.load.java.components.TypeUsage 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.child +import org.jetbrains.kotlin.load.java.lazy.childForMethod import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations import org.jetbrains.kotlin.load.java.lazy.types.LazyJavaTypeAttributes import org.jetbrains.kotlin.load.java.structure.JavaArrayType @@ -87,7 +87,7 @@ abstract class LazyJavaScope(protected val c: LazyJavaResolverContext) : MemberS computeNonDeclaredFunctions(result, name) - c.components.signatureEnhancement.enhanceSignatures(result).toList() + c.components.signatureEnhancement.enhanceSignatures(c, result).toList() } open protected fun JavaMethodDescriptor.isVisibleAsFunction() = true @@ -114,7 +114,7 @@ abstract class LazyJavaScope(protected val c: LazyJavaResolverContext) : MemberS ownerDescriptor, annotations, method.name, c.components.sourceElementFactory.source(method) ) - val c = c.child(functionDescriptorImpl, method) + val c = c.childForMethod(functionDescriptorImpl, method) val methodTypeParameters = method.typeParameters.map { p -> c.typeParameterResolver.resolveTypeParameter(p)!! } val valueParameters = resolveValueParameters(c, functionDescriptorImpl, method.valueParameters) @@ -246,7 +246,7 @@ abstract class LazyJavaScope(protected val c: LazyJavaResolverContext) : MemberS if (DescriptorUtils.isAnnotationClass(ownerDescriptor)) properties.toList() else - c.components.signatureEnhancement.enhanceSignatures(properties).toList() + c.components.signatureEnhancement.enhanceSignatures(c, properties).toList() } private fun resolveProperty(field: JavaField): PropertyDescriptor { 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 08d73ea4ef8..19b404ca825 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 @@ -19,12 +19,17 @@ package org.jetbrains.kotlin.load.java.typeEnhancement import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.composeAnnotations import org.jetbrains.kotlin.load.java.* import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor +import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext +import org.jetbrains.kotlin.load.java.lazy.computeNewDefaultTypeQualifiers +import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor import org.jetbrains.kotlin.name.FqName @@ -36,17 +41,40 @@ import org.jetbrains.kotlin.types.asFlexibleType import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.isFlexible import org.jetbrains.kotlin.types.typeUtil.isTypeParameter +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.util.* class SignatureEnhancement(private val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver) { - fun enhanceSignatures(platformSignatures: Collection): Collection { + + fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifier? { + when (annotationDescriptor.annotationClass?.fqNameSafe) { + in NULLABLE_ANNOTATIONS -> return NullabilityQualifier.NULLABLE + in NOT_NULL_ANNOTATIONS -> return NullabilityQualifier.NOT_NULL + } + + val typeQualifier = + annotationTypeQualifierResolver + .resolveTypeQualifierAnnotation(annotationDescriptor) + ?.takeIf { it.annotationClass?.fqNameSafe == JAVAX_NONNULL_ANNOTATION } + ?: return null + + return typeQualifier.allValueArguments.values.singleOrNull()?.value?.let { + enumEntryDescriptor -> + if (enumEntryDescriptor !is ClassDescriptor) return@let null + if (enumEntryDescriptor.name.asString() == "ALWAYS") NullabilityQualifier.NOT_NULL else NullabilityQualifier.NULLABLE + } ?: NullabilityQualifier.NOT_NULL + } + + + fun enhanceSignatures(c: LazyJavaResolverContext, platformSignatures: Collection): Collection { return platformSignatures.map { - it.enhanceSignature() + it.enhanceSignature(c) } } - private fun D.enhanceSignature(): D { + private fun D.enhanceSignature(c: LazyJavaResolverContext): D { + val outerScopeQualifiers = c.computeNewDefaultTypeQualifiers(annotations) // TODO type parameters // TODO use new type parameters while enhancing other types // TODO Propagation into generic type arguments @@ -62,7 +90,10 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati typeContainer = this.safeAs() ?.getUserData(JavaMethodDescriptor.ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER), - isCovariant = false + isCovariant = false, + defaultTopLevelQualifiers = + outerScopeQualifiers + ?.get(AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER) ) { it.extensionReceiverParameter!!.type }.enhance() else null @@ -81,12 +112,28 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati val valueParameterEnhancements = valueParameters.map { p -> - parts(typeContainer = p, isCovariant = false) { it.valueParameters[p.index].type } + parts( + typeContainer = p, isCovariant = false, + defaultTopLevelQualifiers = + outerScopeQualifiers + ?.get(AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER) + ) { it.valueParameters[p.index].type } .enhance(predefinedEnhancementInfo?.parametersInfo?.getOrNull(p.index)) } val returnTypeEnhancement = - parts(typeContainer = this, isCovariant = true) { it.returnType!! }.enhance(predefinedEnhancementInfo?.returnTypeInfo) + parts( + typeContainer = this, isCovariant = true, + defaultTopLevelQualifiers = + outerScopeQualifiers?.get( + if (this.safeAs()?.isJavaField == true) + AnnotationTypeQualifierResolver.QualifierApplicabilityType.FIELD + else + AnnotationTypeQualifierResolver.QualifierApplicabilityType.METHOD_RETURN_TYPE + ) + + + ) { it.returnType!! }.enhance(predefinedEnhancementInfo?.returnTypeInfo) if ((receiverTypeEnhancement?.wereChanges ?: false) || returnTypeEnhancement.wereChanges || valueParameterEnhancements.any { it.wereChanges }) { @@ -101,7 +148,8 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati private val typeContainer: Annotated?, private val fromOverride: KotlinType, private val fromOverridden: Collection, - private val isCovariant: Boolean + private val isCovariant: Boolean, + private val defaultTopLevelQualifiers: JavaTypeQualifiers? ) { fun enhance(predefined: TypeEnhancementInfo? = null): PartEnhancementResult { val qualifiers = computeIndexedQualifiersForOverride() @@ -152,7 +200,10 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati fun uniqueNotNull(x: T?, y: T?) = if (x == null || y == null || x == y) x ?: y else null - val nullability = composedAnnotation.extractNullability() + val defaultTypeQualifier = defaultTopLevelQualifiers?.takeIf { isHeadTypeConstructor } + val nullability = + composedAnnotation.extractNullability() + ?: defaultTypeQualifier?.nullability return JavaTypeQualifiers( nullability, @@ -168,28 +219,8 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati ) } - private fun Annotations.extractNullability(): NullabilityQualifier? { - for (annotationDescriptor in this) { - when (annotationDescriptor.annotationClass?.fqNameSafe) { - in NULLABLE_ANNOTATIONS -> return NullabilityQualifier.NULLABLE - in NOT_NULL_ANNOTATIONS -> return NullabilityQualifier.NOT_NULL - } - - val typeQualifier = - annotationTypeQualifierResolver - .resolveTypeQualifierAnnotation(annotationDescriptor) - ?.takeIf { it.annotationClass?.fqNameSafe == JAVAX_NONNULL_ANNOTATION } - ?: continue - - return typeQualifier.allValueArguments.values.singleOrNull()?.value?.let { - enumEntryDescriptor -> - if (enumEntryDescriptor !is ClassDescriptor) return@let null - if (enumEntryDescriptor.name.asString() == "ALWAYS") NullabilityQualifier.NOT_NULL else NullabilityQualifier.NULLABLE - } ?: NullabilityQualifier.NOT_NULL - } - - return null - } + private fun Annotations.extractNullability(): NullabilityQualifier? = + this.firstNotNullResult(this@SignatureEnhancement::extractNullability) private fun computeIndexedQualifiersForOverride(): (Int) -> JavaTypeQualifiers { fun KotlinType.toIndexed(): List { @@ -292,6 +323,7 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati private fun D.parts( typeContainer: Annotated?, isCovariant: Boolean, + defaultTopLevelQualifiers: JavaTypeQualifiers?, collector: (D) -> KotlinType ): SignatureParts { return SignatureParts( @@ -301,7 +333,8 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati @Suppress("UNCHECKED_CAST") collector(it as D) }, - isCovariant + isCovariant, + defaultTopLevelQualifiers ) } diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/JdkAndMockLibraryProjectDescriptor.java b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/JdkAndMockLibraryProjectDescriptor.java index 227b79ea4ed..94a7d4bed9f 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/JdkAndMockLibraryProjectDescriptor.java +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/JdkAndMockLibraryProjectDescriptor.java @@ -66,7 +66,7 @@ public class JdkAndMockLibraryProjectDescriptor extends KotlinLightProjectDescri File libraryJar = isJsLibrary ? MockLibraryUtil.compileJsLibraryToJar(sourcesPath, LIBRARY_NAME, withSources) - : MockLibraryUtil.compileJvmLibraryToJar(sourcesPath, LIBRARY_NAME, withSources, extraOptions, classpath); + : MockLibraryUtil.compileJvmLibraryToJar(sourcesPath, LIBRARY_NAME, withSources, true, extraOptions, classpath); String jarUrl = getJarUrl(libraryJar); Library.ModifiableModel libraryModel = model.getModuleLibraryTable().getModifiableModel().createLibrary(LIBRARY_NAME).getModifiableModel();