[FIR JS] Report rtti-related diagnostics
This commit is contained in:
+21
@@ -4960,6 +4960,27 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJsErrors.CANNOT_CHECK_FOR_EXTERNAL_INTERFACE) { firDiagnostic ->
|
||||
CannotCheckForExternalInterfaceImpl(
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJsErrors.UNCHECKED_CAST_TO_EXTERNAL_INTERFACE) { firDiagnostic ->
|
||||
UncheckedCastToExternalInterfaceImpl(
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJsErrors.EXTERNAL_INTERFACE_AS_CLASS_LITERAL) { firDiagnostic ->
|
||||
ExternalInterfaceAsClassLiteralImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJsErrors.DELEGATION_BY_DYNAMIC) { firDiagnostic ->
|
||||
DelegationByDynamicImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
|
||||
+15
@@ -3447,6 +3447,21 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
abstract val type: KtType
|
||||
}
|
||||
|
||||
abstract class CannotCheckForExternalInterface : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = CannotCheckForExternalInterface::class
|
||||
abstract val targetType: KtType
|
||||
}
|
||||
|
||||
abstract class UncheckedCastToExternalInterface : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = UncheckedCastToExternalInterface::class
|
||||
abstract val sourceType: KtType
|
||||
abstract val targetType: KtType
|
||||
}
|
||||
|
||||
abstract class ExternalInterfaceAsClassLiteral : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = ExternalInterfaceAsClassLiteral::class
|
||||
}
|
||||
|
||||
abstract class DelegationByDynamic : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = DelegationByDynamic::class
|
||||
}
|
||||
|
||||
+18
@@ -4172,6 +4172,24 @@ internal class NonExternalDeclarationInInappropriateFileImpl(
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.NonExternalDeclarationInInappropriateFile(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class CannotCheckForExternalInterfaceImpl(
|
||||
override val targetType: KtType,
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.CannotCheckForExternalInterface(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class UncheckedCastToExternalInterfaceImpl(
|
||||
override val sourceType: KtType,
|
||||
override val targetType: KtType,
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.UncheckedCastToExternalInterface(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class ExternalInterfaceAsClassLiteralImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.ExternalInterfaceAsClassLiteral(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class DelegationByDynamicImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
|
||||
+8
@@ -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") {
|
||||
|
||||
+3
@@ -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>()
|
||||
|
||||
+15
@@ -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)
|
||||
}
|
||||
|
||||
+7
@@ -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)
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@ object JsExpressionCheckers : ExpressionCheckers() {
|
||||
|
||||
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
|
||||
get() = setOf(
|
||||
FirJsDefinedExternallyCallChecker
|
||||
FirJsDefinedExternallyCallChecker,
|
||||
FirJsNativeRttiChecker,
|
||||
)
|
||||
}
|
||||
|
||||
+60
@@ -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 -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
external interface I
|
||||
|
||||
fun box(a: Any, b: Any): Pair<I, I?> {
|
||||
return Pair(a as I, b as? I)
|
||||
}
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// FIR_IDENTICAL
|
||||
// DIAGNOSTICS: +UNCHECKED_CAST_TO_EXTERNAL_INTERFACE
|
||||
|
||||
external interface I
|
||||
|
||||
fun box(a: Any, b: Any): Pair<I, I?> {
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
external interface I
|
||||
|
||||
fun box(a: Any, b: Any): Boolean {
|
||||
return a is I && b !is I
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
external interface I
|
||||
|
||||
fun box(a: Any, b: Any): Boolean {
|
||||
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
external interface I
|
||||
|
||||
fun box() {
|
||||
println(I::class)
|
||||
}
|
||||
+3
@@ -1,5 +1,8 @@
|
||||
// FIR_IDENTICAL
|
||||
external interface I
|
||||
|
||||
typealias TA = I
|
||||
fun box() {
|
||||
println(<!EXTERNAL_INTERFACE_AS_CLASS_LITERAL!>I::class<!>)
|
||||
println(<!EXTERNAL_INTERFACE_AS_CLASS_LITERAL!>TA::class<!>)
|
||||
}
|
||||
Vendored
+2
@@ -7,3 +7,5 @@ public external interface I {
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
public typealias TA = I
|
||||
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
external interface I
|
||||
|
||||
external interface J
|
||||
|
||||
fun box(a: Any) = when (a) {
|
||||
is I -> 0
|
||||
!is J -> 1
|
||||
else -> 2
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
external interface I
|
||||
|
||||
external interface J
|
||||
|
||||
Reference in New Issue
Block a user