[FIR JS] Report rtti-related diagnostics

This commit is contained in:
Nikolay Lunyak
2023-01-26 10:52:33 +02:00
parent 351588f0a7
commit 2b5d2dc539
18 changed files with 159 additions and 25 deletions
@@ -86,6 +86,14 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
val NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<ConeKotlinType>("type")
}
val CANNOT_CHECK_FOR_EXTERNAL_INTERFACE by error<KtElement> {
parameter<ConeKotlinType>("targetType")
}
val UNCHECKED_CAST_TO_EXTERNAL_INTERFACE by warning<KtElement> {
parameter<ConeKotlinType>("sourceType")
parameter<ConeKotlinType>("targetType")
}
val EXTERNAL_INTERFACE_AS_CLASS_LITERAL by error<KtElement>()
}
val DYNAMICS by object : DiagnosticGroup("Dynamics") {
@@ -75,6 +75,9 @@ object FirJsErrors {
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>()
val EXTERNAL_INTERFACE_AS_CLASS_LITERAL by error0<KtElement>()
// Dynamics
val DELEGATION_BY_DYNAMIC by error0<KtElement>()
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers
import org.jetbrains.kotlin.fir.analysis.diagnostics.checkMissingMessages
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION
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
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_DEL
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.EXTERNAL_INTERFACE_AS_CLASS_LITERAL
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
@@ -48,6 +50,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_E
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.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.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_EXTERNAL_DECLARATION
@@ -152,6 +155,18 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(JS_NAME_PROHIBITED_FOR_OVERRIDE, "@JsName is prohibited for overridden members")
map.put(JS_NAME_ON_PRIMARY_CONSTRUCTOR_PROHIBITED, "@JsName annotation is prohibited for primary constructors")
map.put(JS_NAME_ON_ACCESSOR_AND_PROPERTY, "@JsName can be either on a property or its accessors, not both of them")
map.put(
CANNOT_CHECK_FOR_EXTERNAL_INTERFACE,
"Cannot check for external interface: {0}",
FirDiagnosticRenderers.RENDER_TYPE,
)
map.put(
UNCHECKED_CAST_TO_EXTERNAL_INTERFACE,
"Unchecked cast to external interface: {0} to {1}",
FirDiagnosticRenderers.RENDER_TYPE,
FirDiagnosticRenderers.RENDER_TYPE,
)
map.put(EXTERNAL_INTERFACE_AS_CLASS_LITERAL, "Can't refer to external interface from class literal")
map.checkMissingMessages(FirJsErrors)
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.hasAnnotationOrInsideAnnotated
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -84,6 +85,10 @@ fun FirBasedSymbol<*>.isNativeObject(session: FirSession): Boolean {
return false
}
fun FirBasedSymbol<*>.isNativeInterface(session: FirSession): Boolean {
return isNativeObject(session) && (fir as? FirClass)?.isInterface == true
}
private val FirBasedSymbol<*>.isExpect
get() = when (this) {
is FirCallableSymbol<*> -> isExpect
@@ -106,4 +111,6 @@ fun FirBasedSymbol<*>.isPredefinedObject(session: FirSession): Boolean {
fun FirBasedSymbol<*>.isNativeObject(context: CheckerContext) = isNativeObject(context.session)
fun FirBasedSymbol<*>.isNativeInterface(context: CheckerContext) = isNativeInterface(context.session)
fun FirBasedSymbol<*>.isPredefinedObject(context: CheckerContext) = isPredefinedObject(context.session)
@@ -16,6 +16,7 @@ object JsExpressionCheckers : ExpressionCheckers() {
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
get() = setOf(
FirJsDefinedExternallyCallChecker
FirJsDefinedExternallyCallChecker,
FirJsNativeRttiChecker,
)
}
@@ -0,0 +1,60 @@
/*
* 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.js.checkers.expression
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpressionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeInterface
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.types.coneType
object FirJsNativeRttiChecker : FirBasicExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
when (expression) {
is FirGetClassCall -> checkGetClassCall(expression, context, reporter)
is FirTypeOperatorCall -> checkTypeOperatorCall(expression, context, reporter)
else -> {}
}
}
private fun checkGetClassCall(expression: FirGetClassCall, context: CheckerContext, reporter: DiagnosticReporter) {
val declarationToCheck = expression.argument.typeRef.toRegularClassSymbol(context.session) ?: return
if (declarationToCheck.isNativeInterface(context)) {
reporter.reportOn(expression.source, FirJsErrors.EXTERNAL_INTERFACE_AS_CLASS_LITERAL, context)
}
}
private fun checkTypeOperatorCall(expression: FirTypeOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
val targetTypeRef = expression.conversionTypeRef
val declarationToCheck = targetTypeRef.toRegularClassSymbol(context.session) ?: return
if (!declarationToCheck.isNativeInterface(context)) {
return
}
when (expression.operation) {
FirOperation.AS, FirOperation.SAFE_AS -> reporter.reportOn(
expression.source,
FirJsErrors.UNCHECKED_CAST_TO_EXTERNAL_INTERFACE,
expression.argument.typeRef.coneType,
targetTypeRef.coneType,
context,
)
FirOperation.IS, FirOperation.NOT_IS -> reporter.reportOn(
expression.source,
FirJsErrors.CANNOT_CHECK_FOR_EXTERNAL_INTERFACE,
targetTypeRef.coneType,
context,
)
else -> {}
}
}
}