Preserve type annotations on type parameter when substituting

Subsitute(@A T, T:@B String) = @B @A String

Nullability when loading Java:
Subsitute(@NotNull T, T:String) = @NotNull String
Subsitute(@NotNull T, T:String!) = @NotNull String
Subsitute(@NotNull T, T:String?) = @NotNull String

Subsitute(@Nullable T, T:String) = @Nullable String?
Subsitute(@Nullable T, T:String!) = @Nullable String?
Subsitute(@Nullable T, T:String?) = @Nullable String?
This commit is contained in:
Denis Zharkov
2015-07-14 14:20:39 +03:00
parent 2b541a560e
commit e23c7f457b
11 changed files with 128 additions and 10 deletions
@@ -19,6 +19,5 @@ public class Y extends X<String> {
fun main() {
checkSubtype<Any>(Y().fooN())
Y().barN(null);
Y().barN(<!NULL_FOR_NONNULL_TYPE!>null<!>);
}
@@ -20,9 +20,9 @@ public open class X</*0*/ T> {
public open class Y : X<kotlin.String!> {
public constructor Y()
public/*package*/ open override /*1*/ /*fake_override*/ fun barN(/*0*/ org.jetbrains.annotations.NotNull() a: kotlin.String!): kotlin.Unit
public/*package*/ open override /*1*/ /*fake_override*/ fun barN(/*0*/ org.jetbrains.annotations.NotNull() a: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
org.jetbrains.annotations.NotNull() public/*package*/ open override /*1*/ /*fake_override*/ fun fooN(): kotlin.String!
org.jetbrains.annotations.NotNull() public/*package*/ open override /*1*/ /*fake_override*/ fun fooN(): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -19,6 +19,5 @@ public class Y extends X<A> {
fun main() {
checkSubtype<Any>(Y().fooN())
Y().barN(null);
Y().barN(<!NULL_FOR_NONNULL_TYPE!>null<!>);
}
@@ -20,9 +20,9 @@ public open class X</*0*/ T> {
public open class Y : X<A!> {
public constructor Y()
public/*package*/ open override /*1*/ /*fake_override*/ fun barN(/*0*/ org.jetbrains.annotations.NotNull() a: A!): kotlin.Unit
public/*package*/ open override /*1*/ /*fake_override*/ fun barN(/*0*/ org.jetbrains.annotations.NotNull() a: A): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
org.jetbrains.annotations.NotNull() public/*package*/ open override /*1*/ /*fake_override*/ fun fooN(): A!
org.jetbrains.annotations.NotNull() public/*package*/ open override /*1*/ /*fake_override*/ fun fooN(): A
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,47 @@
// FILE: A.java
import org.jetbrains.annotations.*;
interface A<T> {
void foo(@NotNull T x, @Nullable T y);
}
// FILE: B1.java
// contains fake_override fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?)
interface B1 extends A<String> {}
// FILE: B2.java
import org.jetbrains.annotations.*;
interface B2 extends A<String> {
// Ok, consistent override
// override fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?)
void foo(@NotNull String x, @Nullable String y);
}
// FILE: B3.java
import org.jetbrains.annotations.*;
interface B3 extends A<String> {
// inconsistent override, second parameter type is platform
// TODO: first one should be platform too, but it's not because when substituting T -> String!,
// value parameter type becomes platform and it can be overridden with @NotNull String.
// Conceptual problem: we can use type parameter as T?, but there is no way to specify same semantics as in Java `@NotNull T`.
// override /*1*/ fun foo(/*0*/ org.jetbrains.annotations.Nullable() x: kotlin.String?, /*1*/ org.jetbrains.annotations.NotNull() y: kotlin.String!): kotlin.Unit
void foo(@Nullable String x, @NotNull String y);
}
// FILE: main.kt
// fake_override fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
interface C1 : A<String>
// fake_override fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String?, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
interface C2 : A<String?>
interface C3 : B1 {
// inconsistent override
<!NOTHING_TO_OVERRIDE!>override<!> fun foo(x: String?, y: String?);
}
@@ -0,0 +1,56 @@
package
public/*package*/ /*synthesized*/ fun </*0*/ T> A(/*0*/ function: (T!, T!) -> kotlin.Unit): A<T>
public/*package*/ /*synthesized*/ fun B1(/*0*/ function: (kotlin.String!, kotlin.String?) -> kotlin.Unit): B1
public/*package*/ /*synthesized*/ fun B2(/*0*/ function: (kotlin.String!, kotlin.String!) -> kotlin.Unit): B2
public/*package*/ /*synthesized*/ fun B3(/*0*/ function: (kotlin.String!, kotlin.String!) -> kotlin.Unit): B3
public/*package*/ interface A</*0*/ T> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: T, /*1*/ org.jetbrains.annotations.Nullable() y: T?): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public/*package*/ interface B1 : A<kotlin.String!> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ /*fake_override*/ fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public/*package*/ interface B2 : A<kotlin.String!> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public/*package*/ interface B3 : A<kotlin.String!> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun foo(/*0*/ org.jetbrains.annotations.Nullable() x: kotlin.String?, /*1*/ org.jetbrains.annotations.NotNull() y: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal interface C1 : A<kotlin.String> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ /*fake_override*/ fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal interface C2 : A<kotlin.String?> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ /*fake_override*/ fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String?, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal interface C3 : B1 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ /*fake_override*/ fun foo(/*0*/ org.jetbrains.annotations.NotNull() x: kotlin.String, /*1*/ org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
internal abstract fun foo(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -10463,6 +10463,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("saveAnnotationAfterSubstitution.kt")
public void testSaveAnnotationAfterSubstitution() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/saveAnnotationAfterSubstitution.kt");
doTest(fileName);
}
@TestMetadata("supertypeDifferentParameterNullability.kt")
public void testSupertypeDifferentParameterNullability() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/supertypeDifferentParameterNullability.kt");
@@ -69,6 +69,8 @@ class FilteredAnnotations(
class CompositeAnnotations(
private val delegates: List<Annotations>
) : Annotations {
constructor(vararg delegates: Annotations): this(delegates.toList())
override fun isEmpty() = delegates.all { it.isEmpty() }
override fun findAnnotation(fqName: FqName) = delegates.asSequence().map { it.findAnnotation(fqName) }.filterNotNull().firstOrNull()
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations;
import org.jetbrains.kotlin.resolve.calls.inference.InferencePackage;
import org.jetbrains.kotlin.resolve.scopes.SubstitutingScope;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilPackage;
@@ -221,6 +222,14 @@ public class TypeSubstitutor {
substitutedType = TypeUtils.makeNullableIfNeeded(replacement.getType(), type.isMarkedNullable());
}
// substitutionType.annotations = replacement.annotations ++ type.annotations
if (!type.getAnnotations().isEmpty()) {
substitutedType = TypeUtilPackage.replaceAnnotations(
substitutedType,
new CompositeAnnotations(substitutedType.getAnnotations(), type.getAnnotations())
);
}
Variance resultingProjectionKind = varianceConflict == VarianceConflictType.NO_CONFLICT
? combine(originalProjectionKind, replacement.getProjectionKind())
: originalProjectionKind;
@@ -95,7 +95,7 @@ public class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
return JetBundle.message("unused.receiver.parameter.remove")
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor?) {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
declaration.setReceiverTypeReference(null)
}
@@ -112,7 +112,7 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
override fun getFamilyName() = "Safe delete"
override fun applyFix(project: Project, descriptor: ProblemDescriptor?) {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
if (!FileModificationService.getInstance().prepareFileForWrite(declaration.getContainingFile())) return
SafeDeleteHandler.invoke(project, arrayOf(declaration), false)
}