diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt index 527036570fd..82e87576ebf 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt @@ -7,13 +7,13 @@ package org.jetbrains.kotlin.resolve.jvm.checkers import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.JvmTarget -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.annotations.JVM_DEFAULT_FQ_NAME import org.jetbrains.kotlin.resolve.annotations.hasJvmDefaultAnnotation import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker @@ -55,6 +55,14 @@ class JvmDefaultChecker(val jvmTarget: JvmTarget) : DeclarationChecker { if (memberDescriptor.overriddenDescriptors.any { it.annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME) }) { context.trace.report(ErrorsJvm.JVM_DEFAULT_REQUIRED_FOR_OVERRIDE.on(declaration)) + } else if (enableJvmDefault) { + descriptor.overriddenDescriptors.flatMap { OverridingUtil.getOverriddenDeclarations(it) }.toSet().let { + for (realDescriptor in OverridingUtil.filterOutOverridden(it)) { + if (realDescriptor is JavaMethodDescriptor && realDescriptor.modality != Modality.ABSTRACT) { + return context.trace.report(ErrorsJvm.NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT.on(declaration)) + } + } + } } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index b419dda4409..7a3590f505f 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -140,6 +140,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(JVM_DEFAULT_IN_DECLARATION, "Usage of '@JvmDefault' is only allowed if the flag -Xenable-jvm-default is enabled"); MAP.put(JVM_DEFAULT_THROUGH_INHERITANCE, "Inheritance from an interface with '@JvmDefault' members is only allowed if the flag -Xenable-jvm-default is enabled"); MAP.put(USAGE_OF_JVM_DEFAULT_THROUGH_SUPER_CALL, "Super calls of '@JvmDefault' members are only allowed if the flag -Xenable-jvm-default is enabled"); + MAP.put(NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT, "Non-@JvmDefault interface method cannot override default Java method. Please annotate this method with @JvmDefault"); } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 1c97ef5e6bd..d09cef0d3f0 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -121,6 +121,8 @@ public interface ErrorsJvm { DiagnosticFactory0 JVM_DEFAULT_THROUGH_INHERITANCE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 USAGE_OF_JVM_DEFAULT_THROUGH_SUPER_CALL = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE); + enum NullabilityInformationSource { KOTLIN { @NotNull diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/javaOverride.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/javaOverride.kt new file mode 100644 index 00000000000..58e6b9a382a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/javaOverride.kt @@ -0,0 +1,73 @@ +// !API_VERSION: 1.3 +// !ENABLE_JVM_DEFAULT +// !JVM_TARGET: 1.8 + +// FILE: JavaInterface.java +public interface JavaInterface { + default void test() {} + + default void testForNonDefault() {} + + void testAbstract(); +} + +// FILE: 1.kt + +interface KotlinInterface : JavaInterface { + @JvmDefault + override fun test() {} + + override fun testForNonDefault() {} + + override fun testAbstract() {} +} + +interface KotlinInterface2 : JavaInterface, KotlinInterface { + @JvmDefault + override fun test() {} + + override fun testForNonDefault() {} + + override fun testAbstract() {} +} + + +interface KotlinInterfaceForIndirect : JavaInterface { + +} + +interface KotlinInterfaceInderectInheritance : KotlinInterfaceForIndirect { + + @JvmDefault + override fun test() {} + + override fun testForNonDefault() {} + + override fun testAbstract() {} +} + +open class KotlinClass : JavaInterface { + override fun test() {} + + override fun testForNonDefault() {} + + override fun testAbstract() {} +} + +interface KotlinInterfaceX { + + fun test() {} + + fun testForNonDefault() {} + + fun testAbstract() {} +} + +interface KotlinInterfaceManySuper: JavaInterface, KotlinInterfaceX { + @JvmDefault + override fun test() {} + + override fun testForNonDefault() {} + + override fun testAbstract() {} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/javaOverride.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/javaOverride.txt new file mode 100644 index 00000000000..026bc133cce --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/javaOverride.txt @@ -0,0 +1,58 @@ +package + +public interface JavaInterface { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open fun test(): kotlin.Unit + public open fun testForNonDefault(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class KotlinClass : JavaInterface { + public constructor KotlinClass() + 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*/ fun test(): kotlin.Unit + public open override /*1*/ fun testForNonDefault(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface KotlinInterface : JavaInterface { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + @kotlin.jvm.JvmDefault public open override /*1*/ fun test(): kotlin.Unit + public open override /*1*/ fun testForNonDefault(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface KotlinInterfaceForIndirect : JavaInterface { + 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 test(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun testForNonDefault(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface KotlinInterfaceInderectInheritance : KotlinInterfaceForIndirect { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + @kotlin.jvm.JvmDefault public open override /*1*/ fun test(): kotlin.Unit + public open override /*1*/ fun testForNonDefault(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface KotlinInterfaceManySuper : JavaInterface, KotlinInterfaceX { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + @kotlin.jvm.JvmDefault public open override /*2*/ fun test(): kotlin.Unit + public open override /*2*/ fun testForNonDefault(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface KotlinInterfaceX { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open fun test(): kotlin.Unit + public open fun testForNonDefault(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index aa7e70950b9..da4fb1107fe 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -482,6 +482,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW doTest(fileName); } + @TestMetadata("javaOverride.kt") + public void testJavaOverride() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/javaOverride.kt"); + doTest(fileName); + } + @TestMetadata("jvmDefaultInInheritance.kt") public void testJvmDefaultInInheritance() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/jvmDefaultInInheritance.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index 99ce0591698..e83b462b4b8 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -482,6 +482,12 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno doTest(fileName); } + @TestMetadata("javaOverride.kt") + public void testJavaOverride() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/javaOverride.kt"); + doTest(fileName); + } + @TestMetadata("jvmDefaultInInheritance.kt") public void testJvmDefaultInInheritance() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmDefault/jvmDefaultInInheritance.kt");