[Wasm] Port external declaration checker to K2 (KT-56849)
Share common code with FirJsExternalChecker using FirWebCommonExternalChecker
This commit is contained in:
committed by
Space Team
parent
83aa014d81
commit
62ebb9932f
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.diagnostics.web.common
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.*
|
||||
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
|
||||
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.WEB_COMMON_DIAGNOSTICS_LIST]
|
||||
*/
|
||||
object FirWebCommonErrors {
|
||||
// Externals
|
||||
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 INLINE_EXTERNAL_DECLARATION by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE by error0<KtExpression>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val EXTERNAL_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER by error0<KtParameter>()
|
||||
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>()
|
||||
|
||||
init {
|
||||
RootDiagnosticRendererFactory.registerFactory(FirWebCommonErrorsDefaultMessages)
|
||||
}
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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.web.common
|
||||
|
||||
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.diagnostics.DiagnosticReporter
|
||||
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.web.common.FirWebCommonErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||
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.references.toResolvedPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
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.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
abstract class FirWebCommonExternalChecker : FirBasicDeclarationChecker() {
|
||||
abstract fun isNativeOrEffectivelyExternal(symbol: FirBasedSymbol<*>, session: FirSession): Boolean
|
||||
|
||||
abstract fun reportExternalEnum(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter)
|
||||
|
||||
abstract fun additionalCheck(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter)
|
||||
|
||||
abstract fun isDefinedExternallyCallableId(callableId: CallableId): Boolean
|
||||
|
||||
abstract fun hasExternalLikeAnnotations(declaration: FirDeclaration, session: FirSession): Boolean
|
||||
|
||||
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (!isNativeOrEffectivelyExternal(declaration.symbol, context.session)) return
|
||||
|
||||
if (!context.isTopLevel) {
|
||||
if (declaration !is FirPropertyAccessor && declaration.isDirectlyExternal(context.session)) {
|
||||
reporter.reportOn(declaration.source, FirWebCommonErrors.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, FirWebCommonErrors.WRONG_EXTERNAL_DECLARATION, classKind, context)
|
||||
}
|
||||
|
||||
if (declaration.isEnumClass) {
|
||||
reportExternalEnum(declaration, context, reporter)
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration is FirPropertyAccessor && declaration.isDirectlyExternal(context.session)) {
|
||||
reporter.reportOn(declaration.source, FirWebCommonErrors.WRONG_EXTERNAL_DECLARATION, "property accessor", context)
|
||||
} else if (
|
||||
declaration !is FirPrimaryConstructor &&
|
||||
declaration !is FirField &&
|
||||
declaration.isPrivateMemberOfExternalClass(context.session)
|
||||
) {
|
||||
reporter.reportOn(declaration.source, FirWebCommonErrors.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, FirWebCommonErrors.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, FirWebCommonErrors.WRONG_EXTERNAL_DECLARATION, target, context)
|
||||
}
|
||||
|
||||
if (
|
||||
declaration is FirCallableDeclaration &&
|
||||
declaration.isNonAbstractMemberIfInterface(context.session) &&
|
||||
!declaration.isNullableProperty()
|
||||
) {
|
||||
reporter.reportOn(declaration.source, FirWebCommonErrors.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE, context)
|
||||
}
|
||||
|
||||
declaration.checkBody(context, reporter)
|
||||
declaration.checkDelegation(context, reporter)
|
||||
declaration.checkAnonymousInitializer(context, reporter)
|
||||
declaration.checkConstructorPropertyParam(context, reporter)
|
||||
|
||||
additionalCheck(declaration, context, reporter)
|
||||
}
|
||||
|
||||
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, FirWebCommonErrors.WRONG_BODY_OF_EXTERNAL_DECLARATION, context)
|
||||
} else if (isWrong && initializer != null) {
|
||||
reporter.reportOn(initializer.source, FirWebCommonErrors.WRONG_INITIALIZER_OF_EXTERNAL_DECLARATION, context)
|
||||
}
|
||||
|
||||
if (this is FirFunction) {
|
||||
for (defaultValue in valueParameters.mapNotNull { it.defaultValue }) {
|
||||
if (!defaultValue.isDefinedExternallyExpression()) {
|
||||
reporter.reportOn(defaultValue.source, FirWebCommonErrors.WRONG_DEFAULT_VALUE_FOR_EXTERNAL_FUN_PARAMETER, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirDeclaration.checkDelegation(context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (this !is FirMemberDeclaration || !isNativeOrEffectivelyExternal(symbol, context.session)) return
|
||||
|
||||
if (this is FirClass) {
|
||||
declarations.firstIsInstanceOrNull<FirPrimaryConstructor>()?.let {
|
||||
val constructorCall = it.delegatedConstructor
|
||||
|
||||
if (constructorCall?.source?.kind is KtRealSourceElementKind) {
|
||||
reporter.reportOn(constructorCall.source, FirWebCommonErrors.EXTERNAL_DELEGATED_CONSTRUCTOR_CALL, context)
|
||||
}
|
||||
}
|
||||
|
||||
for ((superType, delegate) in collectSupertypesWithDelegates()) {
|
||||
when {
|
||||
delegate != null -> {
|
||||
reporter.reportOn(superType.source, FirWebCommonErrors.EXTERNAL_DELEGATION, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (this is FirConstructor && !isPrimary) {
|
||||
val delegationCall = delegatedConstructor
|
||||
|
||||
if (delegationCall?.source?.kind is KtRealSourceElementKind) {
|
||||
reporter.reportOn(delegationCall.source, FirWebCommonErrors.EXTERNAL_DELEGATED_CONSTRUCTOR_CALL, context)
|
||||
}
|
||||
} else if (this is FirProperty) {
|
||||
delegate?.let {
|
||||
reporter.reportOn(it.source, FirWebCommonErrors.EXTERNAL_DELEGATION, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirDeclaration.checkAnonymousInitializer(context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (this !is FirClass) return
|
||||
|
||||
for (anonymousInitializer in anonymousInitializers) {
|
||||
reporter.reportOn(anonymousInitializer.source, FirWebCommonErrors.EXTERNAL_ANONYMOUS_INITIALIZER, 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, FirWebCommonErrors.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) || hasExternalLikeAnnotations(this, 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 isNativeOrEffectivelyExternal(containingDeclaration, 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 isDefinedExternallyCallableId(declaration.callableId)
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.diagnostics.web.common
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.EXTERNAL_ANONYMOUS_INITIALIZER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.EXTERNAL_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.EXTERNAL_DELEGATED_CONSTRUCTOR_CALL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.EXTERNAL_DELEGATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.INLINE_EXTERNAL_DECLARATION
|
||||
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.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.WRONG_BODY_OF_EXTERNAL_DECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.WRONG_DEFAULT_VALUE_FOR_EXTERNAL_FUN_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.WRONG_EXTERNAL_DECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.web.common.FirWebCommonErrors.WRONG_INITIALIZER_OF_EXTERNAL_DECLARATION
|
||||
|
||||
@Suppress("unused")
|
||||
object FirWebCommonErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
override val MAP = KtDiagnosticFactoryToRendererMap("FIR").also { map ->
|
||||
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(INLINE_EXTERNAL_DECLARATION, "Inline external declaration.")
|
||||
map.put(
|
||||
NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE,
|
||||
"Only nullable properties of external interfaces are allowed to be non-abstract."
|
||||
)
|
||||
map.put(EXTERNAL_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER, "External class constructor cannot have a property parameter.")
|
||||
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'."
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user