[JS IR] Control an inheritance of non-external entity from external
Add a special annotation @JsExternalInheritorsOnly for marking external interfaces and classes. The marked interface or class can’t be a parent for non external interfaces, classes or objects. ^KT-57423 Fixed
This commit is contained in:
committed by
Space Team
parent
6e7b078873
commit
4813b659ab
@@ -22,6 +22,7 @@ object JsPlatformConfigurator : PlatformConfiguratorBase(
|
||||
NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
|
||||
JsNameChecker, JsModuleChecker, JsExternalFileChecker,
|
||||
JsExternalChecker, JsInheritanceChecker, JsMultipleInheritanceChecker,
|
||||
JsExternalInheritorOnlyChecker,
|
||||
JsRuntimeAnnotationChecker,
|
||||
JsDynamicDeclarationChecker,
|
||||
JsExportAnnotationChecker,
|
||||
|
||||
+5
@@ -109,6 +109,11 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
||||
|
||||
put(ErrorsJs.NON_CONSUMABLE_EXPORTED_IDENTIFIER, "Exported declaration contains non-consumable identifier '${0}', that can't be represented inside TS definitions and ESM", STRING)
|
||||
|
||||
put(ErrorsJs.JS_EXTERNAL_INHERITORS_ONLY,
|
||||
"External {0} can''t be a parent of non-external {1}",
|
||||
Renderers.DECLARATION_NAME_WITH_KIND,
|
||||
Renderers.DECLARATION_NAME_WITH_KIND)
|
||||
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,9 @@ public interface ErrorsJs {
|
||||
|
||||
DiagnosticFactory1<PsiElement, String> NON_CONSUMABLE_EXPORTED_IDENTIFIER = DiagnosticFactory1.create(WARNING, DEFAULT);
|
||||
|
||||
DiagnosticFactory2<PsiElement, DeclarationDescriptor, DeclarationDescriptor> JS_EXTERNAL_INHERITORS_ONLY = DiagnosticFactory2.create(
|
||||
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
Object _initializer = new Object() {
|
||||
{
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.js.resolve.diagnostics
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
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.getAllSuperClassifiers
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
||||
|
||||
object JsExternalInheritorOnlyChecker : DeclarationChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (descriptor is ClassDescriptor && !descriptor.isEffectivelyExternal()) {
|
||||
descriptor.getAllSuperClassifiers().forEach { parent ->
|
||||
if (parent is ClassDescriptor && AnnotationsUtils.isJsExternalInheritorsOnly(parent)) {
|
||||
context.trace.report(ErrorsJs.JS_EXTERNAL_INHERITORS_ONLY.on(declaration, parent, descriptor))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ public final class AnnotationsUtils {
|
||||
private static final FqName JS_MODULE_ANNOTATION = Annotations.JsModule.asSingleFqName();
|
||||
private static final FqName JS_NON_MODULE_ANNOTATION = Annotations.JsNonModule.asSingleFqName();
|
||||
private static final FqName JS_QUALIFIER_ANNOTATION = Annotations.JsQualifier.asSingleFqName();
|
||||
private static final FqName JS_EXTERNAL_INHERITORS_ONLY = Annotations.JsExternalInheritorsOnly.asSingleFqName();
|
||||
|
||||
private AnnotationsUtils() {
|
||||
}
|
||||
@@ -258,6 +259,10 @@ public final class AnnotationsUtils {
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean isJsExternalInheritorsOnly(@NotNull ClassDescriptor declaration) {
|
||||
return declaration.getAnnotations().hasAnnotation(JS_EXTERNAL_INHERITORS_ONLY);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String extractSingleStringArgument(@NotNull AnnotationDescriptor annotation) {
|
||||
if (annotation.getAllValueArguments().isEmpty()) return null;
|
||||
|
||||
+6
@@ -2457,6 +2457,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
|
||||
runTest("js/js.translator/testData/box/esModules/jsModule/interfaces.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jsExternalInheritorsOnly.kt")
|
||||
public void testJsExternalInheritorsOnly() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/jsModule/jsExternalInheritorsOnly.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("topLevelVarargFun.kt")
|
||||
public void testTopLevelVarargFun() throws Exception {
|
||||
|
||||
+6
@@ -37,6 +37,12 @@ public class FirPsiJsOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiJ
|
||||
runTest("compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jsExternalInheritorsOnly.kt")
|
||||
public void testJsExternalInheritorsOnly() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithJsStdLib/jsExternalInheritorsOnly.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localClassMetadata.kt")
|
||||
public void testLocalClassMetadata() throws Exception {
|
||||
|
||||
+6
@@ -2563,6 +2563,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
||||
runTest("js/js.translator/testData/box/esModules/jsModule/interfaces.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jsExternalInheritorsOnly.kt")
|
||||
public void testJsExternalInheritorsOnly() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/jsModule/jsExternalInheritorsOnly.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("topLevelVarargFun.kt")
|
||||
public void testTopLevelVarargFun() throws Exception {
|
||||
|
||||
+6
@@ -2457,6 +2457,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/esModules/jsModule/interfaces.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jsExternalInheritorsOnly.kt")
|
||||
public void testJsExternalInheritorsOnly() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/jsModule/jsExternalInheritorsOnly.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("topLevelVarargFun.kt")
|
||||
public void testTopLevelVarargFun() throws Exception {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||
// ES_MODULES
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@JsExternalInheritorsOnly
|
||||
external interface ExternalInterfaceX {
|
||||
val x: String
|
||||
}
|
||||
|
||||
external interface ExternalInterfaceXY : ExternalInterfaceX {
|
||||
val y: String
|
||||
}
|
||||
|
||||
external interface ExternalInterfaceXYZ : ExternalInterfaceXY {
|
||||
val z: String
|
||||
}
|
||||
|
||||
external class ExternalXYZ() : ExternalInterfaceXYZ {
|
||||
override val x: String
|
||||
override val y: String
|
||||
override val z: String
|
||||
}
|
||||
|
||||
external class ExternalClassNameSpace {
|
||||
interface NestedInterfaceXYZ : ExternalInterfaceXYZ {
|
||||
override val x: String
|
||||
override val y: String
|
||||
override val z: String
|
||||
}
|
||||
}
|
||||
|
||||
@JsModule("./jsExternalInheritorsOnly.mjs")
|
||||
external object Creator: ExternalInterfaceXYZ {
|
||||
fun createX(): ExternalInterfaceX
|
||||
fun createXY(): ExternalInterfaceXY
|
||||
fun createXYZ(): ExternalInterfaceXYZ
|
||||
fun createClassXYZ(): ExternalXYZ
|
||||
|
||||
fun createNestedInterfaceXYZ(): ExternalClassNameSpace.NestedInterfaceXYZ
|
||||
|
||||
override val x: String
|
||||
override val y: String
|
||||
override val z: String
|
||||
}
|
||||
|
||||
fun checkX(x: ExternalInterfaceX, id: Int) = x.x == "X$id"
|
||||
fun checkXY(xy: ExternalInterfaceXY, id: Int) = checkX(xy, id) && xy.y == "Y$id"
|
||||
fun checkXYZ(xyz: ExternalInterfaceXYZ, id: Int) = checkXY(xyz, id) && xyz.z == "Z$id"
|
||||
|
||||
fun box(): String {
|
||||
if (!checkX(Creator.createX(), 1)) return "Fail interface X"
|
||||
if (!checkXY(Creator.createXY(), 2)) return "Fail interface XY"
|
||||
if (!checkXYZ(Creator.createXYZ(), 3)) return "Fail interface XYZ"
|
||||
|
||||
if (!checkXYZ(Creator.createClassXYZ(), 4)) return "Fail class XYZ"
|
||||
|
||||
if (!checkXYZ(Creator.createNestedInterfaceXYZ(), 5)) return "Fail nested interface XYZ"
|
||||
|
||||
if (!checkXYZ(Creator, 6)) return "Fail object XYZ"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
createX: function () {
|
||||
return {x: "X1"};
|
||||
},
|
||||
createXY: function () {
|
||||
return {x: "X2", y: "Y2"};
|
||||
},
|
||||
createXYZ: function () {
|
||||
return {x: "X3", y: "Y3", z: "Z3"};
|
||||
},
|
||||
createClassXYZ: function () {
|
||||
return {x: "X4", y: "Y4", z: "Z4"};
|
||||
},
|
||||
createNestedInterfaceXYZ: function () {
|
||||
return {x: "X5", y: "Y5", z: "Z5"};
|
||||
},
|
||||
|
||||
x: "X6",
|
||||
y: "Y6",
|
||||
z: "Z6"
|
||||
};
|
||||
Reference in New Issue
Block a user