[K/JS] Support companion objects in external and exported declarations

This commit is contained in:
Artem Kobzar
2024-02-27 16:30:13 +00:00
committed by Space Team
parent 5cda3fba12
commit 3429cbd321
51 changed files with 551 additions and 56 deletions
@@ -22,7 +22,7 @@ object JsPlatformConfigurator : PlatformConfiguratorBase(
additionalDeclarationCheckers = listOf(
NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
JsNameChecker, JsModuleChecker, JsExternalFileChecker,
JsExternalChecker, JsInheritanceChecker, JsMultipleInheritanceChecker,
JsInheritanceChecker, JsMultipleInheritanceChecker,
JsExternalInheritorOnlyChecker,
JsRuntimeAnnotationChecker,
JsDynamicDeclarationChecker,
@@ -51,7 +51,8 @@ object JsPlatformConfigurator : PlatformConfiguratorBase(
container.useInstance(ExtensionFunctionToExternalIsInlinable)
container.useInstance(JsQualifierChecker)
container.useInstance(JsNativeDiagnosticSuppressor)
container.useInstance(JsExportDeclarationChecker(includeUnsignedNumbers = false))
container.useInstance(JsExternalChecker(allowCompanionInInterface = true))
container.useInstance(JsExportDeclarationChecker(allowCompanionInInterface = true, includeUnsignedNumbers = false))
}
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {
@@ -34,6 +34,9 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
put(ErrorsJs.INLINE_CLASS_IN_EXTERNAL_DECLARATION_WARNING, "Using value classes as parameter type or return type of external declarations is experimental")
put(ErrorsJs.ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING, "Using enum classes with an `external` qualifier becomes deprecated and will be an error in future releases")
put(ErrorsJs.NAMED_COMPANION_IN_EXTERNAL_INTERFACE, "Named companions are not allowed inside external interfaces")
put(ErrorsJs.NAMED_COMPANION_IN_EXPORTED_INTERFACE, "Named companions are not allowed inside exported interfaces")
put(ErrorsJs.JS_NAME_CLASH, "JavaScript name ({0}) generated for this declaration clashes with another declaration: {1}",
STRING, Renderers.COMPACT)
put(ErrorsJs.JS_FAKE_NAME_CLASH, "JavaScript name {0} is generated for different inherited members: {1} and {2}",
@@ -36,6 +36,8 @@ public interface ErrorsJs {
DiagnosticFactory0<KtElement> INLINE_CLASS_IN_EXTERNAL_DECLARATION = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtElement> ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtElement> INLINE_CLASS_IN_EXTERNAL_DECLARATION_WARNING = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtElement> NAMED_COMPANION_IN_EXTERNAL_INTERFACE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtElement> NAMED_COMPANION_IN_EXPORTED_INTERFACE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory2<KtElement, String, DeclarationDescriptor> JS_NAME_CLASH = DiagnosticFactory2.create(
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.js.resolve.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.js.resolve.diagnostics.JsExternalChecker.DEFINED_EXTERNALLY_PROPERTY_NAMES
import org.jetbrains.kotlin.js.resolve.diagnostics.JsExternalChecker.Companion.DEFINED_EXTERNALLY_PROPERTY_NAMES
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.js.common.SPECIAL_KEYWORDS
import org.jetbrains.kotlin.js.naming.NameSuggestion
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedDeclaration
@@ -35,7 +36,10 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.types.typeUtil.*
class JsExportDeclarationChecker(private val includeUnsignedNumbers: Boolean) : DeclarationChecker {
class JsExportDeclarationChecker(
private val includeUnsignedNumbers: Boolean,
private val allowCompanionInInterface: Boolean
) : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
val trace = context.trace
val bindingContext = trace.bindingContext
@@ -130,11 +134,15 @@ class JsExportDeclarationChecker(private val includeUnsignedNumbers: Boolean) :
descriptor.isInlineClass() -> "${if (descriptor.isInline) "inline " else ""}${if (descriptor.isValue) "value " else ""}class"
else -> null
}
else -> if (descriptor.isInsideInterface) {
else -> if (descriptor.isInsideInterface && (!allowCompanionInInterface || !descriptor.isCompanionObject)) {
"${if (descriptor.isCompanionObject) "companion object" else "nested/inner declaration"} inside exported interface"
} else null
}
if (allowCompanionInInterface && descriptor.isCompanionObject && descriptor.isInsideInterface && descriptor.name != DEFAULT_NAME_FOR_COMPANION_OBJECT) {
trace.report(ErrorsJs.NAMED_COMPANION_IN_EXPORTED_INTERFACE.on(declaration))
}
if (wrongDeclaration != null) {
reportWrongExportedDeclaration(wrongDeclaration)
return
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.js.PredefinedAnnotation
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.JsStandardClassIds
import org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
import org.jetbrains.kotlin.platform.isWasm
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
@@ -28,9 +29,11 @@ import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
object JsExternalChecker : DeclarationChecker {
val DEFINED_EXTERNALLY_PROPERTY_NAMES = JsStandardClassIds.Callables.definedExternallyPropertyNames
.map { it.asSingleFqName().toUnsafe() }
class JsExternalChecker(private val allowCompanionInInterface: Boolean) : DeclarationChecker {
companion object {
val DEFINED_EXTERNALLY_PROPERTY_NAMES = JsStandardClassIds.Callables.definedExternallyPropertyNames
.map { it.asSingleFqName().toUnsafe() }
}
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (!AnnotationsUtils.isNativeObject(descriptor)) return
@@ -68,12 +71,16 @@ object JsExternalChecker : DeclarationChecker {
trace.report(ErrorsJs.WRONG_EXTERNAL_DECLARATION.on(declaration, "private member of class"))
}
if (descriptor is ClassDescriptor && descriptor.kind != ClassKind.INTERFACE &&
descriptor.containingDeclaration.let { it is ClassDescriptor && it.kind == ClassKind.INTERFACE }
) {
val containingDeclarationsIsInterface = descriptor.containingDeclaration.let { it is ClassDescriptor && it.kind == ClassKind.INTERFACE }
if (descriptor is ClassDescriptor && descriptor.kind != ClassKind.INTERFACE && (!allowCompanionInInterface || !descriptor.isCompanionObject) && containingDeclarationsIsInterface) {
trace.report(ErrorsJs.NESTED_CLASS_IN_EXTERNAL_INTERFACE.on(declaration))
}
if (allowCompanionInInterface && descriptor.isCompanionObject() && containingDeclarationsIsInterface && descriptor.name != DEFAULT_NAME_FOR_COMPANION_OBJECT) {
trace.report(ErrorsJs.NAMED_COMPANION_IN_EXTERNAL_INTERFACE.on(declaration))
}
if (descriptor !is PropertyAccessorDescriptor && descriptor.isExtension) {
val target = when (descriptor) {
is FunctionDescriptor -> "extension function"