[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
@@ -113,6 +113,7 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
val NON_CONSUMABLE_EXPORTED_IDENTIFIER by warning<KtElement>(PositioningStrategy.DEFAULT) {
parameter<String>("name")
}
val NAMED_COMPANION_IN_EXPORTED_INTERFACE by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
}
val DYNAMICS by object : DiagnosticGroup("Dynamics") {
@@ -44,6 +44,7 @@ object WEB_COMMON_DIAGNOSTICS_LIST : DiagnosticList("FirWebCommonErrors") {
val EXTERNAL_INTERFACE_AS_REIFIED_TYPE_ARGUMENT by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<ConeKotlinType>("typeArgument")
}
val NAMED_COMPANION_IN_EXTERNAL_INTERFACE by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
}
val EXPORT by object : DiagnosticGroup("Export") {
@@ -78,6 +78,7 @@ object FirJsErrors {
val WRONG_EXPORTED_DECLARATION: KtDiagnosticFactory1<String> by error1<KtElement, String>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NON_EXPORTABLE_TYPE: KtDiagnosticFactory2<String, ConeKotlinType> by warning2<KtElement, String, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NON_CONSUMABLE_EXPORTED_IDENTIFIER: KtDiagnosticFactory1<String> by warning1<KtElement, String>()
val NAMED_COMPANION_IN_EXPORTED_INTERFACE: KtDiagnosticFactory0 by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
// Dynamics
val DELEGATION_BY_DYNAMIC: KtDiagnosticFactory0 by error0<KtElement>()
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_NAME_ON_P
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_NAME_PROHIBITED_FOR_EXTENSION_PROPERTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_NAME_PROHIBITED_FOR_NAMED_NATIVE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_NAME_PROHIBITED_FOR_OVERRIDE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NAMED_COMPANION_IN_EXPORTED_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NAME_CONTAINS_ILLEGAL_CHARS
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE
@@ -189,5 +190,6 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"Exported declaration contains non-consumable identifier ''{0}'', which cannot be represented inside TS definitions and ESM.",
CommonRenderers.STRING,
)
map.put(NAMED_COMPANION_IN_EXPORTED_INTERFACE, "Named companions are not allowed inside exported interfaces.")
}
}
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.js.common.RESERVED_KEYWORDS
import org.jetbrains.kotlin.js.common.SPECIAL_KEYWORDS
import org.jetbrains.kotlin.name.JsStandardClassIds
import org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
object FirJsExportDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKind.Common) {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -135,11 +136,15 @@ object FirJsExportDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKind
declaration.isInline -> "value class"
else -> null
}
else -> if (context.isInsideInterface) {
"${if (declaration.status.isCompanion) "companion object" else "nested/inner declaration"} inside exported interface"
else -> if (context.isInsideInterface && !declaration.status.isCompanion) {
"nested/inner declaration inside exported interface"
} else null
}
if (context.isInsideInterface && declaration.status.isCompanion && declaration.nameOrSpecialName != DEFAULT_NAME_FOR_COMPANION_OBJECT) {
reporter.reportOn(declaration.source, FirJsErrors.NAMED_COMPANION_IN_EXPORTED_INTERFACE, context)
}
if (wrongDeclaration != null) {
reportWrongExportedDeclaration(wrongDeclaration)
}
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.name.JsStandardClassIds
import org.jetbrains.kotlin.name.JsStandardClassIds.Annotations.JsNative
import org.jetbrains.kotlin.psi.KtParameter
object FirJsExternalChecker : FirWebCommonExternalChecker() {
object FirJsExternalChecker : FirWebCommonExternalChecker(allowCompanionInInterface = true) {
override fun isNativeOrEffectivelyExternal(symbol: FirBasedSymbol<*>, session: FirSession): Boolean {
return symbol.isNativeObject(session)
}
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.WebCommonStandardClassIds
object FirWasmExternalChecker : FirWebCommonExternalChecker() {
object FirWasmExternalChecker : FirWebCommonExternalChecker(allowCompanionInInterface = false) {
override fun isNativeOrEffectivelyExternal(symbol: FirBasedSymbol<*>, session: FirSession): Boolean {
return symbol.isEffectivelyExternal(session)
}
@@ -43,6 +43,7 @@ object FirWebCommonErrors {
val UNCHECKED_CAST_TO_EXTERNAL_INTERFACE: KtDiagnosticFactory2<ConeKotlinType, ConeKotlinType> by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
val EXTERNAL_INTERFACE_AS_CLASS_LITERAL: KtDiagnosticFactory0 by error0<KtElement>()
val EXTERNAL_INTERFACE_AS_REIFIED_TYPE_ARGUMENT: KtDiagnosticFactory1<ConeKotlinType> by error1<KtElement, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NAMED_COMPANION_IN_EXTERNAL_INTERFACE: KtDiagnosticFactory0 by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
// Export
val NESTED_JS_EXPORT: KtDiagnosticFactory0 by error0<KtElement>()
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErro
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.EXTERNAL_INTERFACE_AS_REIFIED_TYPE_ARGUMENT
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.INLINE_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.JSCODE_ARGUMENT_NON_CONST_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.NAMED_COMPANION_IN_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.NESTED_CLASS_IN_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.NESTED_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.NESTED_JS_EXPORT
@@ -76,5 +77,6 @@ object FirWebCommonErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(NESTED_JS_EXPORT, "'@JsExport' is only allowed on files and top-level declarations.")
map.put(JSCODE_ARGUMENT_NON_CONST_EXPRESSION, "An argument for the 'js()' function must be a constant string expression.")
map.put(NAMED_COMPANION_IN_EXTERNAL_INTERFACE, "Named companions are not allowed inside external interfaces.")
}
}
@@ -6,9 +6,7 @@
package org.jetbrains.kotlin.fir.analysis.web.common.checkers.declaration
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirElement
@@ -30,9 +28,10 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
abstract class FirWebCommonExternalChecker : FirBasicDeclarationChecker(MppCheckerKind.Common) {
abstract class FirWebCommonExternalChecker(private val allowCompanionInInterface: Boolean) : FirBasicDeclarationChecker(MppCheckerKind.Common) {
abstract fun isNativeOrEffectivelyExternal(symbol: FirBasedSymbol<*>, session: FirSession): Boolean
abstract fun reportExternalEnum(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter)
@@ -87,12 +86,24 @@ abstract class FirWebCommonExternalChecker : FirBasicDeclarationChecker(MppCheck
if (
declaration is FirClass &&
declaration.classKind != ClassKind.INTERFACE &&
container is FirClass && container.classKind == ClassKind.INTERFACE
!declaration.classKind.isInterface && (!allowCompanionInInterface || !declaration.status.isCompanion) &&
container is FirClass && container.classKind.isInterface
) {
reporter.reportOn(declaration.source, FirWebCommonErrors.NESTED_CLASS_IN_EXTERNAL_INTERFACE, context)
}
if (
allowCompanionInInterface &&
declaration is FirClass &&
declaration.status.isCompanion &&
container is FirClass &&
container.isInterface &&
declaration.nameOrSpecialName != DEFAULT_NAME_FOR_COMPANION_OBJECT
) {
reporter.reportOn(declaration.source, FirWebCommonErrors.NAMED_COMPANION_IN_EXTERNAL_INTERFACE, context)
}
if (declaration !is FirPropertyAccessor && declaration is FirCallableDeclaration && declaration.isExtension) {
val target = when (declaration) {
is FirFunction -> "extension function"
@@ -645,6 +645,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
"JS_EXTERNAL_INHERITORS_ONLY",
"JS_EXTERNAL_ARGUMENT",
"WRONG_EXPORTED_DECLARATION",
"NAMED_COMPANION_IN_EXPORTED_INTERFACE",
"NESTED_JS_EXPORT",
"DELEGATION_BY_DYNAMIC",
"PROPERTY_DELEGATION_BY_DYNAMIC",
@@ -700,6 +701,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
"CANNOT_CHECK_FOR_EXTERNAL_INTERFACE",
"EXTERNAL_INTERFACE_AS_CLASS_LITERAL",
"EXTERNAL_INTERFACE_AS_REIFIED_TYPE_ARGUMENT",
"NAMED_COMPANION_IN_EXTERNAL_INTERFACE",
"JSCODE_ARGUMENT_NON_CONST_EXPRESSION",
"SYNTAX",
)