[Wasm] Port external declaration checker to K2 (KT-56849)

Share common code with FirJsExternalChecker using
FirWebCommonExternalChecker
This commit is contained in:
Svyatoslav Kuzmich
2023-10-26 11:46:22 +02:00
committed by Space Team
parent 83aa014d81
commit 62ebb9932f
35 changed files with 948 additions and 547 deletions
@@ -15,12 +15,10 @@ 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
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtParameter
/**
* Generated from: [org.jetbrains.kotlin.fir.checkers.generator.diagnostics.JS_DIAGNOSTICS_LIST]
@@ -63,24 +61,12 @@ object FirJsErrors {
val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE by error1<KtElement, FirNamedFunctionSymbol>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION by error0<PsiElement>()
val EXTERNAL_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER by error0<KtParameter>()
val EXTERNAL_ENUM_ENTRY_WITH_BODY by error0<KtElement>()
val EXTERNAL_ANONYMOUS_INITIALIZER by error0<KtAnonymousInitializer>()
val EXTERNAL_DELEGATION by error0<KtElement>()
val EXTERNAL_DELEGATED_CONSTRUCTOR_CALL by error0<KtElement>()
val WRONG_BODY_OF_EXTERNAL_DECLARATION by error0<KtElement>()
val WRONG_INITIALIZER_OF_EXTERNAL_DECLARATION by error0<KtElement>()
val WRONG_DEFAULT_VALUE_FOR_EXTERNAL_FUN_PARAMETER by error0<KtElement>()
val NESTED_EXTERNAL_DECLARATION by error0<KtExpression>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val WRONG_EXTERNAL_DECLARATION by error1<KtExpression, String>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NESTED_CLASS_IN_EXTERNAL_INTERFACE by error0<KtExpression>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val INLINE_EXTERNAL_DECLARATION by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING by warning0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val INLINE_CLASS_IN_EXTERNAL_DECLARATION_WARNING by warning0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val INLINE_CLASS_IN_EXTERNAL_DECLARATION by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val EXTENSION_FUNCTION_IN_EXTERNAL_DECLARATION by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE by error0<KtExpression>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE by error1<KtElement, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val CANNOT_CHECK_FOR_EXTERNAL_INTERFACE by error1<KtElement, ConeKotlinType>()
val UNCHECKED_CAST_TO_EXTERNAL_INTERFACE by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
@@ -15,30 +15,23 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_TO_DEFI
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CANNOT_CHECK_FOR_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTENSION_FUNCTION_IN_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_ANONYMOUS_INITIALIZER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_DELEGATED_CONSTRUCTOR_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_DELEGATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_ENUM_ENTRY_WITH_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.DELEGATION_BY_DYNAMIC
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.PROPERTY_DELEGATION_BY_DYNAMIC
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTENSION_FUNCTION_IN_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_ENUM_ENTRY_WITH_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_INTERFACE_AS_CLASS_LITERAL
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_INTERFACE_AS_REIFIED_TYPE_ARGUMENT
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE
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.EXTERNAL_INTERFACE_AS_REIFIED_TYPE_ARGUMENT
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.JSCODE_ARGUMENT_NON_CONST_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_BUILTIN_NAME_CLASH
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_EXTERNAL_ARGUMENT
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_EXTERNAL_INHERITORS_ONLY
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_FAKE_NAME_CLASH
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
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_JS_MODULE_PROHIBITED
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_FAKE_NAME_CLASH
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_NAME_CLASH
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_NAME_IS_NOT_ON_ALL_ACCESSORS
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_NAME_ON_ACCESSOR_AND_PROPERTY
@@ -53,24 +46,19 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_INDEX
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_INDEXER_WRONG_PARAMETER_COUNT
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_SETTER_WRONG_RETURN_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_CLASS_IN_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_JS_EXPORT
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_JS_MODULE_PROHIBITED
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_CONSUMABLE_EXPORTED_IDENTIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_EXPORTABLE_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.PROPERTY_DELEGATION_BY_DYNAMIC
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.SPREAD_OPERATOR_IN_DYNAMIC_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.UNCHECKED_CAST_TO_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_BODY_OF_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_DEFAULT_VALUE_FOR_EXTERNAL_FUN_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_EXPORTED_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_INITIALIZER_OF_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_JS_QUALIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_MULTIPLE_INHERITANCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_OPERATION_WITH_DYNAMIC
@@ -119,25 +107,8 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
RUNTIME_ANNOTATION_NOT_SUPPORTED,
"Reflection is not supported in JavaScript target; therefore, you won't be able to read this annotation at runtime."
)
map.put(EXTERNAL_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER, "External class constructor cannot have a property parameter.")
map.put(EXTERNAL_ENUM_ENTRY_WITH_BODY, "Entry of external enum class cannot have a body.")
map.put(EXTERNAL_ANONYMOUS_INITIALIZER, "Anonymous initializers in external classes are prohibited.")
map.put(EXTERNAL_DELEGATION, "Cannot use delegate on external declaration.")
map.put(EXTERNAL_DELEGATED_CONSTRUCTOR_CALL, "Delegated constructor call in external class is prohibited.")
map.put(
WRONG_BODY_OF_EXTERNAL_DECLARATION,
"Wrong body of external declaration. Must be either ' = definedExternally' or '{ definedExternally }'."
)
map.put(WRONG_INITIALIZER_OF_EXTERNAL_DECLARATION, "Wrong initializer of external declaration. Must be ' = definedExternally'.")
map.put(
WRONG_DEFAULT_VALUE_FOR_EXTERNAL_FUN_PARAMETER,
"Wrong default value for parameter of external function. Must be ' = definedExternally'."
)
map.put(NESTED_EXTERNAL_DECLARATION, "Non-top-level 'external' declaration.")
map.put(WRONG_EXTERNAL_DECLARATION, "Declaration of such kind ({0}) cannot be external.", CommonRenderers.STRING)
map.put(NESTED_CLASS_IN_EXTERNAL_INTERFACE, "Interface cannot contain nested classes and objects.")
map.put(EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE, "External type extends non-external type.")
map.put(INLINE_EXTERNAL_DECLARATION, "Inline external declaration.")
map.put(
INLINE_CLASS_IN_EXTERNAL_DECLARATION_WARNING,
"Using value classes as parameter type or return type of external declarations is experimental."
@@ -151,10 +122,6 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"Using value classes as parameter type or return type of external declarations is not supported."
)
map.put(EXTENSION_FUNCTION_IN_EXTERNAL_DECLARATION, "Function types with receivers are prohibited in external declarations.")
map.put(
NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE,
"Only nullable properties of external interfaces are allowed to be non-abstract."
)
map.put(
NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN,
"Annotation ''{0}'' is only allowed on member functions of declarations annotated with ''kotlin.js.native'' or on top-level extension functions.",
@@ -9,97 +9,39 @@ import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory0
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyBackingField
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject
import org.jetbrains.kotlin.fir.analysis.js.checkers.superClassNotAny
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.analysis.web.common.FirWebCommonExternalChecker
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.JsStandardClassIds
import org.jetbrains.kotlin.name.JsStandardClassIds.Annotations.JsNative
import org.jetbrains.kotlin.name.WasmStandardClassIds
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
object FirJsExternalChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.symbol.isNativeObject(context)) return
object FirJsExternalChecker : FirWebCommonExternalChecker() {
override fun isNativeOrEffectivelyExternal(symbol: FirBasedSymbol<*>, session: FirSession): Boolean {
return symbol.isNativeObject(session)
}
if (!context.isTopLevel) {
if (declaration !is FirPropertyAccessor && declaration.isDirectlyExternal(context.session)) {
reporter.reportOn(declaration.source, FirJsErrors.NESTED_EXTERNAL_DECLARATION, context)
}
}
if (declaration is FirClass) {
// TODO: KT-55600: Stop generating diagnostic
// messages inside checkers
val classKind = when {
declaration.status.isData -> "data class"
declaration.status.isInner -> "inner class"
declaration.status.isInline -> "value class"
declaration.status.isFun -> "fun interface"
declaration.classKind == ClassKind.ANNOTATION_CLASS -> "annotation class"
else -> null
}
if (classKind != null) {
reporter.reportOn(declaration.source, FirJsErrors.WRONG_EXTERNAL_DECLARATION, classKind, context)
}
if (declaration.isEnumClass) {
reporter.reportOn(declaration.source, FirJsErrors.ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING, context)
}
}
if (declaration is FirPropertyAccessor && declaration.isDirectlyExternal(context.session)) {
reporter.reportOn(declaration.source, FirJsErrors.WRONG_EXTERNAL_DECLARATION, "property accessor", context)
} else if (
declaration !is FirPrimaryConstructor &&
declaration !is FirField &&
declaration.isPrivateMemberOfExternalClass(context.session)
) {
reporter.reportOn(declaration.source, FirJsErrors.WRONG_EXTERNAL_DECLARATION, "private member of class", context)
}
val container = context.containingDeclarations.lastOrNull()
if (
declaration is FirClass &&
declaration.classKind != ClassKind.INTERFACE &&
container is FirClass && container.classKind == ClassKind.INTERFACE
) {
reporter.reportOn(declaration.source, FirJsErrors.NESTED_CLASS_IN_EXTERNAL_INTERFACE, context)
}
if (declaration !is FirPropertyAccessor && declaration is FirCallableDeclaration && declaration.isExtension) {
val target = when (declaration) {
is FirFunction -> "extension function"
is FirProperty -> "extension property"
else -> "extension member"
}
reporter.reportOn(declaration.source, FirJsErrors.WRONG_EXTERNAL_DECLARATION, target, context)
}
override fun reportExternalEnum(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
reporter.reportOn(declaration.source, FirJsErrors.ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING, context)
}
override fun additionalCheck(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration is FirClass && declaration.classKind != ClassKind.ANNOTATION_CLASS) {
val superClasses = declaration.superInterfaces(context.session).toMutableList()
declaration.superClassNotAny(context.session)?.let {
@@ -115,7 +57,7 @@ object FirJsExternalChecker : FirBasicDeclarationChecker() {
}
if (declaration is FirFunction && declaration.isInline) {
reporter.reportOn(declaration.source, FirJsErrors.INLINE_EXTERNAL_DECLARATION, context)
reporter.reportOn(declaration.source, FirWebCommonErrors.INLINE_EXTERNAL_DECLARATION, context)
}
fun reportOnParametersAndReturnTypesIf(
@@ -180,20 +122,16 @@ object FirJsExternalChecker : FirBasicDeclarationChecker() {
)
}
if (
declaration is FirCallableDeclaration &&
declaration.isNonAbstractMemberIfInterface(context.session) &&
!declaration.isNullableProperty()
) {
reporter.reportOn(declaration.source, FirJsErrors.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE, context)
}
declaration.checkBody(context, reporter)
declaration.checkDelegation(context, reporter)
declaration.checkAnonymousInitializer(context, reporter)
declaration.checkEnumEntry(context, reporter)
declaration.checkConstructorPropertyParam(context, reporter)
}
}
override fun isDefinedExternallyCallableId(callableId: CallableId): Boolean {
return callableId in JsStandardClassIds.Callables.definedExternallyPropertyNames
}
override fun hasExternalLikeAnnotations(declaration: FirDeclaration, session: FirSession): Boolean {
return declaration.hasAnnotation(JsNative, session)
}
private val KtSourceElement.allowsReporting
get() = kind !is KtFakeSourceElementKind || kind == KtFakeSourceElementKind.PropertyFromParameter
@@ -208,146 +146,12 @@ object FirJsExternalChecker : FirBasicDeclarationChecker() {
.filterNot { it.isAny || it.isNullableAny }
.filter { it.toSymbol(session)?.classKind == ClassKind.INTERFACE }
private fun FirDeclaration.checkBody(context: CheckerContext, reporter: DiagnosticReporter) {
if (this is FirDefaultPropertyAccessor) return
val body = when (this) {
is FirFunction -> body
is FirAnonymousInitializer -> body
else -> null
}
val initializer = when {
this is FirEnumEntry -> null
source?.kind == KtFakeSourceElementKind.PropertyFromParameter -> null
this is FirVariable -> initializer
body is FirSingleExpressionBlock -> (body.statement as? FirReturnExpression)?.result
else -> null
}
// we shouldn't check such things as the
// copy() function of a data class
if (source?.kind !is KtRealSourceElementKind) {
return
}
val isWrong = body !is FirSingleExpressionBlock && !hasValidExternalBody()
|| initializer != null && !initializer.isDefinedExternallyExpression()
if (isWrong && body != null) {
reporter.reportOn(body.source, FirJsErrors.WRONG_BODY_OF_EXTERNAL_DECLARATION, context)
} else if (isWrong && initializer != null) {
reporter.reportOn(initializer.source, FirJsErrors.WRONG_INITIALIZER_OF_EXTERNAL_DECLARATION, context)
}
if (this is FirFunction) {
for (defaultValue in valueParameters.mapNotNull { it.defaultValue }) {
if (!defaultValue.isDefinedExternallyExpression()) {
reporter.reportOn(defaultValue.source, FirJsErrors.WRONG_DEFAULT_VALUE_FOR_EXTERNAL_FUN_PARAMETER, context)
}
}
}
}
private fun FirDeclaration.checkDelegation(context: CheckerContext, reporter: DiagnosticReporter) {
if (this !is FirMemberDeclaration || !symbol.isEffectivelyExternal(context)) return
if (this is FirClass) {
declarations.firstIsInstanceOrNull<FirPrimaryConstructor>()?.let {
val constructorCall = it.delegatedConstructor
if (constructorCall?.source?.kind is KtRealSourceElementKind) {
reporter.reportOn(constructorCall.source, FirJsErrors.EXTERNAL_DELEGATED_CONSTRUCTOR_CALL, context)
}
}
for ((superType, delegate) in collectSupertypesWithDelegates()) {
when {
delegate != null -> {
reporter.reportOn(superType.source, FirJsErrors.EXTERNAL_DELEGATION, context)
}
}
}
} else if (this is FirConstructor && !isPrimary) {
val delegationCall = delegatedConstructor
if (delegationCall?.source?.kind is KtRealSourceElementKind) {
reporter.reportOn(delegationCall.source, FirJsErrors.EXTERNAL_DELEGATED_CONSTRUCTOR_CALL, context)
}
} else if (this is FirProperty) {
delegate?.let {
reporter.reportOn(it.source, FirJsErrors.EXTERNAL_DELEGATION, context)
}
}
}
private fun FirDeclaration.checkAnonymousInitializer(context: CheckerContext, reporter: DiagnosticReporter) {
if (this !is FirClass) return
for (anonymousInitializer in anonymousInitializers) {
reporter.reportOn(anonymousInitializer.source, FirJsErrors.EXTERNAL_ANONYMOUS_INITIALIZER, context)
}
}
private fun FirDeclaration.checkEnumEntry(context: CheckerContext, reporter: DiagnosticReporter) {
if (this !is FirEnumEntry) return
initializer?.let {
reporter.reportOn(it.source, FirJsErrors.EXTERNAL_ENUM_ENTRY_WITH_BODY, context)
}
}
private fun FirDeclaration.checkConstructorPropertyParam(context: CheckerContext, reporter: DiagnosticReporter) {
if (this !is FirProperty || source?.kind != KtFakeSourceElementKind.PropertyFromParameter) return
val containingClass = getContainingClassSymbol(context.session) as? FirClassSymbol<*> ?: return
if (containingClass.isData || containingClass.classKind == ClassKind.ANNOTATION_CLASS) return
reporter.reportOn(source, FirJsErrors.EXTERNAL_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER, context)
}
private fun FirDeclaration.isDirectlyExternal(session: FirSession): Boolean {
// source kind is checked, otherwise this function
// may return true for a primary constructor of an external class
if (this is FirDefaultPropertyAccessor || this.source?.kind !is KtRealSourceElementKind) return false
return hasModifier(KtTokens.EXTERNAL_KEYWORD) || hasAnnotation(JsNative, session)
}
private fun FirDeclaration.isPrivateMemberOfExternalClass(session: FirSession): Boolean {
if (this is FirPropertyAccessor && visibility == propertySymbol.visibility) return false
if (this !is FirMemberDeclaration || visibility != Visibilities.Private) return false
val containingDeclaration = getContainingClassSymbol(session) ?: return false
return containingDeclaration.isNativeObject(session)
}
private fun FirDeclaration.isNonAbstractMemberIfInterface(session: FirSession): Boolean {
return this is FirCallableDeclaration
&& modality != Modality.ABSTRACT
&& (getContainingClassSymbol(session) as? FirClassSymbol<*>)?.classKind == ClassKind.INTERFACE
&& this !is FirPropertyAccessor
}
private fun FirCallableDeclaration.isNullableProperty() = this is FirProperty && returnTypeRef.coneType.isNullable
private fun FirDeclaration.hasValidExternalBody(): Boolean {
val body = when (this) {
is FirFunction -> body
is FirAnonymousInitializer -> body
else -> return true
}
return when {
body is FirSingleExpressionBlock -> body.isDefinedExternallyExpression()
body != null -> {
val statement = body.statements.singleOrNull() ?: return false
statement.isDefinedExternallyExpression()
}
else -> false
}
}
private fun FirElement.isDefinedExternallyExpression(): Boolean {
val declaration = (this as? FirPropertyAccessExpression)
?.calleeReference?.toResolvedPropertySymbol() ?: return false
return declaration.callableId in JsStandardClassIds.Callables.definedExternallyPropertyNames
}
}