From df34e69d7dcfb20d7705dccb38122ae68d498c4f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 8 Dec 2016 15:07:19 +0300 Subject: [PATCH] JS: prohibit inheritance of external classes from non-external (see KT-15130) --- .../testsWithJsStdLib/native/inheritance.kt | 23 ++++++ .../testsWithJsStdLib/native/inheritance.txt | 82 +++++++++++++++++++ .../DiagnosticsTestWithJsStdLibGenerated.java | 6 ++ .../diagnostics/DefaultErrorMessagesJs.kt | 1 + .../js/resolve/diagnostics/ErrorsJs.java | 2 + .../resolve/diagnostics/JsExternalChecker.kt | 14 +++- 6 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.kt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.txt diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.kt b/compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.kt new file mode 100644 index 00000000000..a53f2814a24 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.kt @@ -0,0 +1,23 @@ +// !DIAGNOSTICS: -DEPRECATION + +open class A + +interface I + +external open class B + +external class C : A() + +external class D : B(), I + +@native class N : A() + +external interface K : I + +external enum class E { + X +} + +external enum class F : I { + X +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.txt b/compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.txt new file mode 100644 index 00000000000..c62083a1d1f --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.txt @@ -0,0 +1,82 @@ +package + +public open class A { + public constructor A() + 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 +} + +public external open class B { + public constructor B() + 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 +} + +public external final class C : A { + public constructor C() + 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 +} + +public external final class D : B, I { + public constructor D() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public external final enum class E : kotlin.Enum { + enum entry X + + private constructor E() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E + public final /*synthesized*/ fun values(): kotlin.Array +} + +public external final enum class F : kotlin.Enum, I { + enum entry X + + private constructor F() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: F): kotlin.Int + public final override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): F + public final /*synthesized*/ fun values(): kotlin.Array +} + +public interface I { + 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 +} + +public external interface K : I { + 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.js.native public final class N : A { + public constructor N() + 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/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index e3d1d3f1a7d..33a04a81b96 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -539,6 +539,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("inheritance.kt") + public void testInheritance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/inheritance.kt"); + doTest(fileName); + } + @TestMetadata("innerClass.kt") public void testInnerClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/innerClass.kt"); diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt index 2a742b6cf83..01f745bf9e5 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt @@ -63,6 +63,7 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsJs.CANNOT_CHECK_FOR_NATIVE_INTERFACE, "Cannot check for native interface: {0}", RENDER_TYPE) put(ErrorsJs.UNCHECKED_CAST_TO_NATIVE_INTERFACE, "Unchecked cast to native interface: {0} to {1}", RENDER_TYPE, RENDER_TYPE) put(ErrorsJs.NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT, "Cannot pass native interface {0} for reified type parameter", RENDER_TYPE) + put(ErrorsJs.EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE, "External type extends non-external type") put(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC, "Wrong operation with dynamic value: {0}", Renderers.STRING) this diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java index ca83ad072e2..b4f8b7130ed 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java @@ -62,6 +62,8 @@ public interface ErrorsJs { DiagnosticFactory1 CANNOT_CHECK_FOR_NATIVE_INTERFACE = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 UNCHECKED_CAST_TO_NATIVE_INTERFACE = DiagnosticFactory2.create(WARNING); DiagnosticFactory1 NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE = DiagnosticFactory0.create( + ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT); DiagnosticFactory1 WRONG_OPERATION_WITH_DYNAMIC = DiagnosticFactory1.create(ERROR); @SuppressWarnings("UnusedDeclaration") diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalChecker.kt index 947d2e62146..12f23585e92 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalChecker.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.js.resolve.diagnostics +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors @@ -27,7 +28,8 @@ import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.checkers.SimpleDeclarationChecker -import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import org.jetbrains.kotlin.resolve.descriptorUtil.* +import org.jetbrains.kotlin.utils.singletonOrEmptyList class JsExternalChecker : SimpleDeclarationChecker { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink, @@ -61,6 +63,16 @@ class JsExternalChecker : SimpleDeclarationChecker { } diagnosticHolder.report(ErrorsJs.WRONG_EXTERNAL_DECLARATION.on(declaration, target)) } + + if (descriptor is ClassDescriptor && descriptor.kind != ClassKind.ANNOTATION_CLASS) { + val superClasses = (descriptor.getSuperClassNotAny().singletonOrEmptyList() + descriptor.getSuperInterfaces()).toMutableSet() + if (descriptor.kind == ClassKind.ENUM_CLASS || descriptor.kind == ClassKind.ENUM_ENTRY) { + superClasses.removeAll { it.fqNameUnsafe == KotlinBuiltIns.FQ_NAMES._enum } + } + if (superClasses.any { !AnnotationsUtils.isNativeObject(it) }) { + diagnosticHolder.report(ErrorsJs.EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE.on(declaration)) + } + } } private fun isDirectlyExternal(declaration: KtDeclaration, descriptor: DeclarationDescriptor): Boolean {