[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:
Alexander Korepanov
2023-03-21 11:45:04 +01:00
committed by Space Team
parent 6e7b078873
commit 4813b659ab
26 changed files with 677 additions and 1 deletions
@@ -5021,6 +5021,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirJsErrors.JS_EXTERNAL_INHERITORS_ONLY) { firDiagnostic ->
JsExternalInheritorsOnlyImpl(
firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.a),
firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.b),
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirJsErrors.NESTED_JS_EXPORT) { firDiagnostic ->
NestedJsExportImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -3493,6 +3493,12 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ExternalInterfaceAsClassLiteral::class
}
abstract class JsExternalInheritorsOnly : KtFirDiagnostic<KtDeclaration>() {
override val diagnosticClass get() = JsExternalInheritorsOnly::class
abstract val parent: KtClassLikeSymbol
abstract val kid: KtClassLikeSymbol
}
abstract class NestedJsExport : KtFirDiagnostic<KtElement>() {
override val diagnosticClass get() = NestedJsExport::class
}
@@ -4226,6 +4226,13 @@ internal class ExternalInterfaceAsClassLiteralImpl(
override val token: KtLifetimeToken,
) : KtFirDiagnostic.ExternalInterfaceAsClassLiteral(), KtAbstractFirDiagnostic<KtElement>
internal class JsExternalInheritorsOnlyImpl(
override val parent: KtClassLikeSymbol,
override val kid: KtClassLikeSymbol,
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.JsExternalInheritorsOnly(), KtAbstractFirDiagnostic<KtDeclaration>
internal class NestedJsExportImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.psi.*
@@ -94,6 +95,10 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
parameter<ConeKotlinType>("targetType")
}
val EXTERNAL_INTERFACE_AS_CLASS_LITERAL by error<KtElement>()
val JS_EXTERNAL_INHERITORS_ONLY by error<KtDeclaration>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<FirClassLikeSymbol<*>>("parent")
parameter<FirClassLikeSymbol<*>>("kid")
}
}
val EXPORT by object : DiagnosticGroup("Export") {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
@@ -78,6 +79,7 @@ object FirJsErrors {
val CANNOT_CHECK_FOR_EXTERNAL_INTERFACE by error1<KtElement, ConeKotlinType>()
val UNCHECKED_CAST_TO_EXTERNAL_INTERFACE by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
val EXTERNAL_INTERFACE_AS_CLASS_LITERAL by error0<KtElement>()
val JS_EXTERNAL_INHERITORS_ONLY by error2<KtDeclaration, FirClassLikeSymbol<*>, FirClassLikeSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
// Export
val NESTED_JS_EXPORT by error0<KtElement>()
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.IMPLEMENTING
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.INLINE_CLASS_IN_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.INLINE_CLASS_IN_EXTERNAL_DECLARATION_WARNING
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_EXTERNAL_INHERITORS_ONLY
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.INLINE_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PROHIBITED_ON_NON_NATIVE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PROHIBITED_ON_VAR
@@ -153,6 +154,12 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"Can''t put non-external declarations in file marked with {0} annotation",
FirDiagnosticRenderers.RENDER_TYPE
)
map.put(
JS_EXTERNAL_INHERITORS_ONLY,
"External {0} can''t be a parent of non-external {1}",
FirDiagnosticRenderers.RENDER_CLASS_OR_OBJECT_NAME,
FirDiagnosticRenderers.RENDER_CLASS_OR_OBJECT_NAME
)
map.put(JS_NAME_PROHIBITED_FOR_EXTENSION_PROPERTY, "@JsName is prohibited for extension properties")
map.put(JS_NAME_IS_NOT_ON_ALL_ACCESSORS, "@JsName should be on all the property accessors")
map.put(JS_NAME_PROHIBITED_FOR_NAMED_NATIVE, "@JsName is prohibited for external declaration with explicit name")
@@ -36,6 +36,7 @@ object JsDeclarationCheckers : DeclarationCheckers() {
FirJsMultipleInheritanceChecker,
FirJsDynamicDeclarationChecker,
FirJsInheritanceClassChecker,
FirJsExternalInheritorOnlyChecker
)
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
@@ -0,0 +1,53 @@
/*
* 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.fir.analysis.js.checkers.declaration
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.name.JsStandardClassIds.Annotations.JsExternalInheritorsOnly
import org.jetbrains.kotlin.utils.addToStdlib.popLast
object FirJsExternalInheritorOnlyChecker : FirClassChecker() {
private fun FirClass.forEachParents(context: CheckerContext, f: (FirRegularClassSymbol) -> Unit) {
val todo = superConeTypes.toMutableList()
val done = hashSetOf<FirRegularClassSymbol>()
while (todo.isNotEmpty()) {
val classSymbol = todo.popLast().toRegularClassSymbol(context.session) ?: continue
if (done.add(classSymbol)) {
f(classSymbol)
classSymbol.resolvedSuperTypeRefs.mapNotNullTo(todo) { it.type as? ConeClassLikeType }
}
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.symbol.isEffectivelyExternal(context)) {
declaration.forEachParents(context) { parent ->
if (parent.hasAnnotation(JsExternalInheritorsOnly, context.session)) {
reporter.reportOn(
declaration.source,
FirJsErrors.JS_EXTERNAL_INHERITORS_ONLY,
parent,
declaration.symbol,
context
)
}
}
}
}
}
@@ -0,0 +1,135 @@
// FIR_IDENTICAL
@OptIn(ExperimentalStdlibApi::class)
@JsExternalInheritorsOnly
external interface ExternalInterfaceX {
val x: String
}
@OptIn(ExperimentalStdlibApi::class)
@JsExternalInheritorsOnly
external open class ExternalOpenClassX {
val x: String
}
// check interfaces
external interface ExternalInterfaceXY : ExternalInterfaceX {
val y: String
}
interface <!JS_EXTERNAL_INHERITORS_ONLY!>InterfaceXY<!> : ExternalInterfaceX {
val y: String
}
interface <!JS_EXTERNAL_INHERITORS_ONLY!>InterfaceXYZ<!> : ExternalInterfaceXY {
val z: String
}
// check objects
external object ExternalObjectXY : ExternalInterfaceX {
override val x: String
val y: String
}
external object ExternalObjectXYZ : ExternalInterfaceXY {
override val x: String
override val y: String
val z: String
}
external object ExternalObjectXZ : ExternalOpenClassX {
val z: String
}
<!JS_EXTERNAL_INHERITORS_ONLY!>object ObjectXY<!> : ExternalInterfaceX {
override val x: String = "X"
val y: String = "Y"
}
<!JS_EXTERNAL_INHERITORS_ONLY!>object ObjectXYZ<!> : ExternalInterfaceXY {
override val x: String = "X"
override val y: String = "Y"
val z: String = "Z"
}
<!JS_EXTERNAL_INHERITORS_ONLY!>object ObjectXZ<!> : ExternalOpenClassX() {
val z: String = "Z"
}
// check classes
external class ExternalClassXY : ExternalInterfaceX {
override val x: String
val y: String
}
external class ExternalClassXYZ : ExternalInterfaceXY {
override val x: String
override val y: String
val z: String
}
external open class ExternalOpenClassXZ : ExternalOpenClassX {
val z: String
}
class <!JS_EXTERNAL_INHERITORS_ONLY!>ClassXY<!> : ExternalInterfaceX {
override val x: String = "X"
val y: String = "Y"
}
class <!JS_EXTERNAL_INHERITORS_ONLY!>ClassXYZ<!> : ExternalInterfaceXY {
override val x: String = "X"
override val y: String = "Y"
val z: String = "Z"
}
class <!JS_EXTERNAL_INHERITORS_ONLY!>ClassXZ<!> : ExternalOpenClassX() {
val z: String = "Z"
}
class <!JS_EXTERNAL_INHERITORS_ONLY!>ClassXZY<!> : ExternalOpenClassXZ() {
val y: String = "Y"
}
// check nested
external class ExternalClassNameSpace {
@OptIn(ExperimentalStdlibApi::class)
@JsExternalInheritorsOnly
interface NestedInterfaceX {
val x: String
}
interface NestedInterfaceXY : NestedInterfaceX {
val y: String
}
}
external interface ExternalInterfaceXY2 : ExternalClassNameSpace.NestedInterfaceX {
val y: String
}
external interface ExternalInterfaceXYZ2 : ExternalClassNameSpace.NestedInterfaceXY {
val z: String
}
interface <!JS_EXTERNAL_INHERITORS_ONLY!>InterfaceXY2<!> : ExternalClassNameSpace.NestedInterfaceX {
val y: String
}
interface <!JS_EXTERNAL_INHERITORS_ONLY!>InterfaceXYZ2<!> : ExternalClassNameSpace.NestedInterfaceXY {
val z: String
}
// multiple inheritance
external class ExternalClassXY2 : ExternalInterfaceX, ExternalOpenClassX {
val y: String
}
class <!JS_EXTERNAL_INHERITORS_ONLY, JS_EXTERNAL_INHERITORS_ONLY!>ClassXY2<!> : ExternalInterfaceX, ExternalOpenClassX() {
val y: String = "Y"
}
@@ -0,0 +1,238 @@
package
public final class ClassXY : ExternalInterfaceX {
public constructor ClassXY()
public open override /*1*/ val x: kotlin.String = "X"
public final val y: kotlin.String = "Y"
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 class ClassXY2 : ExternalInterfaceX, ExternalOpenClassX {
public constructor ClassXY2()
public final override /*2*/ /*fake_override*/ val x: kotlin.String
public final val y: kotlin.String = "Y"
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 final class ClassXYZ : ExternalInterfaceXY {
public constructor ClassXYZ()
public open override /*1*/ val x: kotlin.String = "X"
public open override /*1*/ val y: kotlin.String = "Y"
public final val z: kotlin.String = "Z"
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 class ClassXZ : ExternalOpenClassX {
public constructor ClassXZ()
public final override /*1*/ /*fake_override*/ val x: kotlin.String
public final val z: kotlin.String = "Z"
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 class ClassXZY : ExternalOpenClassXZ {
public constructor ClassXZY()
public final override /*1*/ /*fake_override*/ val x: kotlin.String
public final val y: kotlin.String = "Y"
public final override /*1*/ /*fake_override*/ val z: kotlin.String
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 ExternalClassNameSpace {
public constructor ExternalClassNameSpace()
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.OptIn(markerClass = {kotlin.ExperimentalStdlibApi::class}) @kotlin.js.JsExternalInheritorsOnly public interface NestedInterfaceX {
public abstract val x: kotlin.String
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 NestedInterfaceXY : ExternalClassNameSpace.NestedInterfaceX {
public abstract override /*1*/ /*fake_override*/ val x: kotlin.String
public abstract val y: kotlin.String
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 ExternalClassXY : ExternalInterfaceX {
public constructor ExternalClassXY()
public open override /*1*/ val x: kotlin.String
public final val y: kotlin.String
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 ExternalClassXY2 : ExternalInterfaceX, ExternalOpenClassX {
public constructor ExternalClassXY2()
public final override /*2*/ /*fake_override*/ val x: kotlin.String
public final val y: kotlin.String
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 final external class ExternalClassXYZ : ExternalInterfaceXY {
public constructor ExternalClassXYZ()
public open override /*1*/ val x: kotlin.String
public open override /*1*/ val y: kotlin.String
public final val z: kotlin.String
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.OptIn(markerClass = {kotlin.ExperimentalStdlibApi::class}) @kotlin.js.JsExternalInheritorsOnly public external interface ExternalInterfaceX {
public abstract val x: kotlin.String
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 ExternalInterfaceXY : ExternalInterfaceX {
public abstract override /*1*/ /*fake_override*/ val x: kotlin.String
public abstract val y: kotlin.String
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 ExternalInterfaceXY2 : ExternalClassNameSpace.NestedInterfaceX {
public abstract override /*1*/ /*fake_override*/ val x: kotlin.String
public abstract val y: kotlin.String
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 ExternalInterfaceXYZ2 : ExternalClassNameSpace.NestedInterfaceXY {
public abstract override /*1*/ /*fake_override*/ val x: kotlin.String
public abstract override /*1*/ /*fake_override*/ val y: kotlin.String
public abstract val z: kotlin.String
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 object ExternalObjectXY : ExternalInterfaceX {
private constructor ExternalObjectXY()
public open override /*1*/ val x: kotlin.String
public final val y: kotlin.String
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 object ExternalObjectXYZ : ExternalInterfaceXY {
private constructor ExternalObjectXYZ()
public open override /*1*/ val x: kotlin.String
public open override /*1*/ val y: kotlin.String
public final val z: kotlin.String
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 object ExternalObjectXZ : ExternalOpenClassX {
private constructor ExternalObjectXZ()
public final override /*1*/ /*fake_override*/ val x: kotlin.String
public final val z: kotlin.String
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.OptIn(markerClass = {kotlin.ExperimentalStdlibApi::class}) @kotlin.js.JsExternalInheritorsOnly public open external class ExternalOpenClassX {
public constructor ExternalOpenClassX()
public final val x: kotlin.String
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 open external class ExternalOpenClassXZ : ExternalOpenClassX {
public constructor ExternalOpenClassXZ()
public final override /*1*/ /*fake_override*/ val x: kotlin.String
public final val z: kotlin.String
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 InterfaceXY : ExternalInterfaceX {
public abstract override /*1*/ /*fake_override*/ val x: kotlin.String
public abstract val y: kotlin.String
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 InterfaceXY2 : ExternalClassNameSpace.NestedInterfaceX {
public abstract override /*1*/ /*fake_override*/ val x: kotlin.String
public abstract val y: kotlin.String
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 InterfaceXYZ : ExternalInterfaceXY {
public abstract override /*1*/ /*fake_override*/ val x: kotlin.String
public abstract override /*1*/ /*fake_override*/ val y: kotlin.String
public abstract val z: kotlin.String
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 InterfaceXYZ2 : ExternalClassNameSpace.NestedInterfaceXY {
public abstract override /*1*/ /*fake_override*/ val x: kotlin.String
public abstract override /*1*/ /*fake_override*/ val y: kotlin.String
public abstract val z: kotlin.String
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 ObjectXY : ExternalInterfaceX {
private constructor ObjectXY()
public open override /*1*/ val x: kotlin.String = "X"
public final val y: kotlin.String = "Y"
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 ObjectXYZ : ExternalInterfaceXY {
private constructor ObjectXYZ()
public open override /*1*/ val x: kotlin.String = "X"
public open override /*1*/ val y: kotlin.String = "Y"
public final val z: kotlin.String = "Z"
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 ObjectXZ : ExternalOpenClassX {
private constructor ObjectXZ()
public final override /*1*/ /*fake_override*/ val x: kotlin.String
public final val z: kotlin.String = "Z"
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
}
@@ -36,6 +36,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes
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 {
@@ -41,6 +41,9 @@ object JsStandardClassIds {
@JvmField
val JsExport = "JsExport".jsId()
@JvmField
val JsExternalInheritorsOnly = "JsExternalInheritorsOnly".jsId()
@JvmField
val JsExportIgnore = JsExport.createNestedClassId(Name.identifier("Ignore"))
@@ -22,6 +22,7 @@ object JsPlatformConfigurator : PlatformConfiguratorBase(
NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
JsNameChecker, JsModuleChecker, JsExternalFileChecker,
JsExternalChecker, JsInheritanceChecker, JsMultipleInheritanceChecker,
JsExternalInheritorOnlyChecker,
JsRuntimeAnnotationChecker,
JsDynamicDeclarationChecker,
JsExportAnnotationChecker,
@@ -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() {
{
@@ -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;
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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"
}
@@ -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"
};
+8
View File
@@ -250,6 +250,14 @@ public final annotation class JsExport : kotlin.Annotation {
}
}
@kotlin.ExperimentalStdlibApi
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS})
@kotlin.SinceKotlin(version = "1.9")
public final annotation class JsExternalInheritorsOnly : kotlin.Annotation {
public constructor JsExternalInheritorsOnly()
}
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.FILE})
public final annotation class JsModule : kotlin.Annotation {
+8
View File
@@ -249,6 +249,14 @@ public final annotation class JsExport : kotlin.Annotation {
}
}
@kotlin.ExperimentalStdlibApi
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS})
@kotlin.SinceKotlin(version = "1.9")
public final annotation class JsExternalInheritorsOnly : kotlin.Annotation {
public constructor JsExternalInheritorsOnly()
}
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.FILE})
public final annotation class JsModule : kotlin.Annotation {
+41 -1
View File
@@ -211,4 +211,44 @@ public actual annotation class JsExport {
@Target(AnnotationTarget.PROPERTY)
@SinceKotlin("1.6")
@Deprecated("This annotation is a temporal migration assistance and may be removed in the future releases, please consider filing an issue about the case where it is needed")
public annotation class EagerInitialization
public annotation class EagerInitialization
/**
* When placed on an external interface or class, requires all its child
* interfaces, classes, and objects to be external as well.
*
* The compiler mangles identifiers of functions and properties from non-external interfaces and classes,
* and doesn't mangle from external ones. Requiring external interfaces and classes inheritors being external
* is necessary to avoid non-obvious bugs when identifier naming in an inheritor doesn't correspond to
* naming in the base class or interface.
*
* Example:
*
* ```kotlin
* // From kotlin-wrappers
* external interface Props {
* var key: Key?
* }
*
* // User code
* @OptIn(ExperimentalStdlibApi::class)
* @JsExternalInheritorsOnly
* external interface LayoutProps : Props {
* var layout: Layout?
* }
*
* external interface ComponentProps : LayoutProps // OK
*
* external interface ComponentA : ComponentProps // OK
*
* interface ComponentB : ComponentProps // Compilation error!
* ```
*
* This annotation is experimental, meaning that the restrictions mentioned above are subject to change.
*/
@ExperimentalStdlibApi
@Retention(AnnotationRetention.BINARY)
@Target(CLASS)
@SinceKotlin("1.9")
public annotation class JsExternalInheritorsOnly