[Wasm] External inheritance checker
Check that non-external types can't extend external type
diff --git a/compiler/cli/resources/META-INF/services/org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages$Extension b/compiler/cli/resources/META-INF/services/org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages$Extension
index a7a0ec3..d9c11f9 100644
--- a/compiler/cli/resources/META-INF/services/org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages$Extension
+++ b/compiler/cli/resources/META-INF/services/org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages$Extension
@@ -2,3 +2,4 @@
org.jetbrains.kotlin.resolve.jvm.diagnostics.DefaultErrorMessagesJvm
org.jetbrains.kotlin.js.resolve.diagnostics.DefaultErrorMessagesJs
org.jetbrains.kotlin.resolve.konan.diagnostics.DefaultErrorMessagesNative
+org.jetbrains.kotlin.wasm.resolve.diagnostics.DefaultErrorMessagesWasm
diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt b/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt
index 56d3ed9..a6eaa28 100644
--- a/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt
+++ b/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.kt
@@ -1,11 +1,22 @@
-open class A
+open class C1
-interface I
+interface I1
-external open class B
+external open class EC1
-external class <!EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE!>C<!> : A
+external class <!EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE!>EC2<!> : C1
-external class <!EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE!>D<!> : B, I
+external class <!EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE!>EC3<!> : I1, C1
-external interface <!EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE!>K<!> : I
\ No newline at end of file
+external interface <!EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE!>EI1<!> : I1
+
+interface <!NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE!>I2<!> : EI1
+
+class <!NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE!>C3<!> : EI1
+
+class <!NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE!>C4<!> : EI1, EC1()
+
+<!NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE!>object O1<!> : EC1()
+
+val x1: Any = <!NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE!>object<!> : EI1 {}
+val x2: Any = <!NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE!>object<!> : EC1() {}
diff --git a/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.txt b/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.txt
index 217300a..f15e07c 100644
--- a/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.txt
+++ b/compiler/testData/diagnostics/wasmTests/jsInterop/inheritance.txt
@@ -1,7 +1,10 @@
package
-public open class A {
- public constructor A()
+public val x1: kotlin.Any
+public val x2: kotlin.Any
+
+public open class C1 {
+ public constructor C1()
invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@@ -9,8 +12,8 @@
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
-public open external class B {
- public constructor B()
+public final class C3 : EI1 {
+ public constructor C3()
invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@@ -18,17 +21,8 @@
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
-public final external class C : A {
- public constructor C()
- invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
- invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
- 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 final external class D : B, I {
- public constructor D()
+public final class C4 : EI1, EC1 {
+ public constructor C4()
invisible_fake final override /*2*/ /*fake_override*/ var _hashCode: kotlin.Int
invisible_fake final override /*2*/ /*fake_override*/ var typeInfo: kotlin.Int
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@@ -36,45 +30,34 @@
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
-public final external enum class E : kotlin.Enum<E> {
- enum entry X
-
- private constructor E()
+public open external class EC1 {
+ public constructor EC1()
invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
- public final override /*1*/ /*fake_override*/ val name: kotlin.String
- public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
- public open override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E): kotlin.Int
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
-
- // Static members
- public final /*synthesized*/ val entries: kotlin.enums.EnumEntries<E>
- public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E
- public final /*synthesized*/ fun values(): kotlin.Array<E>
}
-public final external enum class F : kotlin.Enum<F>, I {
- enum entry X
+public final external class EC2 : C1 {
+ public constructor EC2()
+ invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
+ invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
+ 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
+}
- private constructor F()
+public final external class EC3 : I1, C1 {
+ public constructor EC3()
invisible_fake final override /*2*/ /*fake_override*/ var _hashCode: kotlin.Int
- public final override /*1*/ /*fake_override*/ val name: kotlin.String
- public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
invisible_fake final override /*2*/ /*fake_override*/ var typeInfo: kotlin.Int
- public open override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: F): kotlin.Int
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
-
- // Static members
- public final /*synthesized*/ val entries: kotlin.enums.EnumEntries<F>
- public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): F
- public final /*synthesized*/ fun values(): kotlin.Array<F>
}
-public interface I {
+public external interface EI1 : I1 {
invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@@ -82,7 +65,24 @@
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
-public external interface K : I {
+public interface I1 {
+ invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
+ invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
+ 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 interface I2 : EI1 {
+ invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
+ invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
+ 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 object O1 : EC1 {
+ private constructor O1()
invisible_fake final override /*1*/ /*fake_override*/ var _hashCode: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var typeInfo: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/WasmPlatformConfigurator.kt b/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/WasmPlatformConfigurator.kt
index 534c7eb..b79d425 100644
--- a/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/WasmPlatformConfigurator.kt
+++ b/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/WasmPlatformConfigurator.kt
@@ -15,11 +15,12 @@
import org.jetbrains.kotlin.resolve.PlatformConfiguratorBase
import org.jetbrains.kotlin.resolve.calls.checkers.LateinitIntrinsicApplicabilityChecker
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
+import org.jetbrains.kotlin.wasm.resolve.diagnostics.WasmExternalInheritanceChecker
object WasmPlatformConfigurator : PlatformConfiguratorBase(
additionalDeclarationCheckers = listOf(
JsNameChecker, JsModuleChecker, JsExternalFileChecker,
- JsExternalChecker,
+ JsExternalChecker, WasmExternalInheritanceChecker,
JsRuntimeAnnotationChecker,
JsExportAnnotationChecker,
JsExportDeclarationChecker,
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/DefaultErrorMessagesWasm.kt b/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/DefaultErrorMessagesWasm.kt
new file mode 100644
index 0000000..e3d9daf
--- /dev/null
+++ b/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/DefaultErrorMessagesWasm.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.wasm.resolve.diagnostics
+
+import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
+import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
+import org.jetbrains.kotlin.diagnostics.rendering.Renderers
+
+private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
+ with(DiagnosticFactoryToRendererMap("Wasm")) {
+ put(
+ ErrorsWasm.NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE,
+ "Non-external type extends external type {0}",
+ Renderers.RENDER_TYPE
+ )
+ this
+ }
+}
+
+class DefaultErrorMessagesWasm : DefaultErrorMessages.Extension {
+ override fun getMap(): DiagnosticFactoryToRendererMap = DIAGNOSTIC_FACTORY_TO_RENDERER
+}
\ No newline at end of file
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/ErrorsWasm.java b/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/ErrorsWasm.java
new file mode 100644
index 0000000..245a25b
--- /dev/null
+++ b/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/ErrorsWasm.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.wasm.resolve.diagnostics;
+
+import com.intellij.psi.PsiElement;
+import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
+import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
+import org.jetbrains.kotlin.diagnostics.Errors;
+import org.jetbrains.kotlin.diagnostics.PositioningStrategies;
+import org.jetbrains.kotlin.psi.KtElement;
+import org.jetbrains.kotlin.types.KotlinType;
+
+import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
+
+public interface ErrorsWasm {
+ DiagnosticFactory1<KtElement, KotlinType> NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE =
+ DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
+
+ @SuppressWarnings("UnusedDeclaration")
+ Object _initializer = new Object() {
+ {
+ Errors.Initializer.initializeFactoryNames(ErrorsWasm.class);
+ }
+ };
+}
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/WasmExternalInheritanceChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/WasmExternalInheritanceChecker.kt
new file mode 100644
index 0000000..a8b914f
--- /dev/null
+++ b/js/js.frontend/src/org/jetbrains/kotlin/wasm/resolve/diagnostics/WasmExternalInheritanceChecker.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.wasm.resolve.diagnostics
+
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
+import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
+import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
+import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
+import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
+
+object WasmExternalInheritanceChecker : DeclarationChecker {
+ override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
+ if (descriptor is ClassDescriptor && !descriptor.isEffectivelyExternal()) {
+ val superClasses = listOfNotNull(descriptor.getSuperClassNotAny()) + descriptor.getSuperInterfaces()
+ for (superClass in superClasses) {
+ if (superClass.isEffectivelyExternal()) {
+ context.trace.report(
+ ErrorsWasm.NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE.on(
+ declaration as KtClassOrObject,
+ superClass.defaultType
+ )
+ )
+ break
+ }
+ }
+ }
+ }
+}