[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
@@ -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;