[JS FIR] Support DYNAMIC_NOT_ALLOWED diagnostic

^KT-59384 Fixed
This commit is contained in:
Alexander Korepanov
2023-07-13 17:29:23 +02:00
committed by Space Team
parent 39fba56de9
commit 6034d32d7c
8 changed files with 53 additions and 22 deletions
@@ -3779,6 +3779,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.DYNAMIC_NOT_ALLOWED) { firDiagnostic ->
DynamicNotAllowedImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.ENUM_ENTRY_AS_TYPE) { firDiagnostic -> add(FirErrors.ENUM_ENTRY_AS_TYPE) { firDiagnostic ->
EnumEntryAsTypeImpl( EnumEntryAsTypeImpl(
firDiagnostic as KtPsiDiagnostic, firDiagnostic as KtPsiDiagnostic,
@@ -2641,6 +2641,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = IsEnumEntry::class override val diagnosticClass get() = IsEnumEntry::class
} }
interface DynamicNotAllowed : KtFirDiagnostic<KtTypeReference> {
override val diagnosticClass get() = DynamicNotAllowed::class
}
interface EnumEntryAsType : KtFirDiagnostic<KtTypeReference> { interface EnumEntryAsType : KtFirDiagnostic<KtTypeReference> {
override val diagnosticClass get() = EnumEntryAsType::class override val diagnosticClass get() = EnumEntryAsType::class
} }
@@ -3183,6 +3183,11 @@ internal class IsEnumEntryImpl(
token: KtLifetimeToken, token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtTypeReference>(firDiagnostic, token), KtFirDiagnostic.IsEnumEntry ) : KtAbstractFirDiagnostic<KtTypeReference>(firDiagnostic, token), KtFirDiagnostic.IsEnumEntry
internal class DynamicNotAllowedImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtTypeReference>(firDiagnostic, token), KtFirDiagnostic.DynamicNotAllowed
internal class EnumEntryAsTypeImpl( internal class EnumEntryAsTypeImpl(
firDiagnostic: KtPsiDiagnostic, firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken, token: KtLifetimeToken,
@@ -1307,6 +1307,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<Boolean>("compileTimeCheckResult") parameter<Boolean>("compileTimeCheckResult")
} }
val IS_ENUM_ENTRY by error<KtTypeReference>() val IS_ENUM_ENTRY by error<KtTypeReference>()
val DYNAMIC_NOT_ALLOWED by error<KtTypeReference>()
val ENUM_ENTRY_AS_TYPE by error<KtTypeReference>(PositioningStrategy.SELECTOR_BY_QUALIFIED) val ENUM_ENTRY_AS_TYPE by error<KtTypeReference>(PositioningStrategy.SELECTOR_BY_QUALIFIED)
} }
@@ -678,6 +678,7 @@ object FirErrors {
val UNCHECKED_CAST by warning2<KtBinaryExpressionWithTypeRHS, ConeKotlinType, ConeKotlinType>(SourceElementPositioningStrategies.AS_TYPE) val UNCHECKED_CAST by warning2<KtBinaryExpressionWithTypeRHS, ConeKotlinType, ConeKotlinType>(SourceElementPositioningStrategies.AS_TYPE)
val USELESS_IS_CHECK by warning1<KtElement, Boolean>() val USELESS_IS_CHECK by warning1<KtElement, Boolean>()
val IS_ENUM_ENTRY by error0<KtTypeReference>() val IS_ENUM_ENTRY by error0<KtTypeReference>()
val DYNAMIC_NOT_ALLOWED by error0<KtTypeReference>()
val ENUM_ENTRY_AS_TYPE by error0<KtTypeReference>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) val ENUM_ENTRY_AS_TYPE by error0<KtTypeReference>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
// When expressions // When expressions
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.FirOperation
import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall
import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.types.ConeDynamicType
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
object FirCastOperatorsChecker : FirTypeOperatorCallChecker() { object FirCastOperatorsChecker : FirTypeOperatorCallChecker() {
@@ -29,22 +30,33 @@ object FirCastOperatorsChecker : FirTypeOperatorCallChecker() {
val isSafeAs = expression.operation == FirOperation.SAFE_AS val isSafeAs = expression.operation == FirOperation.SAFE_AS
if (expression.operation == FirOperation.AS || isSafeAs) { if (expression.operation == FirOperation.AS || isSafeAs) {
val castType = checkCasting(actualType, targetType, isSafeAs, context) if (targetType is ConeDynamicType) {
if (castType == CastingType.Impossible) { reporter.reportOn(conversionTypeRef.source, FirErrors.DYNAMIC_NOT_ALLOWED, context)
if (context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2)) { } else {
reporter.reportOn(expression.source, FirErrors.CAST_NEVER_SUCCEEDS, context) val castType = checkCasting(actualType, targetType, isSafeAs, context)
if (castType == CastingType.Impossible) {
if (context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2)) {
reporter.reportOn(expression.source, FirErrors.CAST_NEVER_SUCCEEDS, context)
}
} else if (castType == CastingType.Always) {
if (context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2)) {
reporter.reportOn(expression.source, FirErrors.USELESS_CAST, context)
}
} else if (isCastErased(actualType, targetType, context)) {
reporter.reportOn(expression.source, FirErrors.UNCHECKED_CAST, actualType, targetType, context)
} }
} else if (castType == CastingType.Always) {
if (context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2)) {
reporter.reportOn(expression.source, FirErrors.USELESS_CAST, context)
}
} else if (isCastErased(actualType, targetType, context)) {
reporter.reportOn(expression.source, FirErrors.UNCHECKED_CAST, actualType, targetType, context)
} }
} else if (expression.operation == FirOperation.IS) { } else if (expression.operation == FirOperation.IS) {
if (targetType is ConeDynamicType) {
reporter.reportOn(conversionTypeRef.source, FirErrors.DYNAMIC_NOT_ALLOWED, context)
}
if (!context.isContractBody && isCastErased(actualType, targetType, context)) { if (!context.isContractBody && isCastErased(actualType, targetType, context)) {
reporter.reportOn(conversionTypeRef.source, FirErrors.CANNOT_CHECK_FOR_ERASED, targetType, context) reporter.reportOn(conversionTypeRef.source, FirErrors.CANNOT_CHECK_FOR_ERASED, targetType, context)
} }
} else if (expression.operation == FirOperation.NOT_IS) {
if (targetType is ConeDynamicType) {
reporter.reportOn(conversionTypeRef.source, FirErrors.DYNAMIC_NOT_ALLOWED, context)
}
} }
} }
} }
@@ -229,6 +229,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FLOAT_LITERAL_OUT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_BINARY_MOD import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_BINARY_MOD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_PROPERTY_TYPE_IN_CONSTRUCTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_PROPERTY_TYPE_IN_CONSTRUCTOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_IDENTITY_EQUALS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_IDENTITY_EQUALS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_EXTERNAL_DECLARATION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_TAILREC_FUNCTION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_TAILREC_FUNCTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECT_ACTUAL_OPT_IN_ANNOTATION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECT_ACTUAL_OPT_IN_ANNOTATION
@@ -1966,6 +1967,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(USELESS_CAST, "No cast needed") map.put(USELESS_CAST, "No cast needed")
map.put(UNCHECKED_CAST, "Unchecked cast: ''{0}'' to ''{1}''", RENDER_TYPE, RENDER_TYPE) map.put(UNCHECKED_CAST, "Unchecked cast: ''{0}'' to ''{1}''", RENDER_TYPE, RENDER_TYPE)
map.put(USELESS_IS_CHECK, "Check for instance is always ''{0}''", TO_STRING) map.put(USELESS_IS_CHECK, "Check for instance is always ''{0}''", TO_STRING)
map.put(DYNAMIC_NOT_ALLOWED, "Dynamic types are not allowed in this position");
map.put(IS_ENUM_ENTRY, "'is' over enum entry is not allowed, use comparison instead") map.put(IS_ENUM_ENTRY, "'is' over enum entry is not allowed, use comparison instead")
map.put(ENUM_ENTRY_AS_TYPE, "Use of enum entry names as types is not allowed, use enum type instead") map.put(ENUM_ENTRY_AS_TYPE, "Use of enum entry names as types is not allowed, use enum type instead")
@@ -1,23 +1,23 @@
// !DIAGNOSTICS: -REDUNDANT_NULLABLE // !DIAGNOSTICS: -REDUNDANT_NULLABLE
fun test(d: Any, dl: Collection<dynamic>) { fun test(d: Any, dl: Collection<dynamic>) {
d as dynamic d as <!DYNAMIC_NOT_ALLOWED!>dynamic<!>
d as dynamic? d as <!DYNAMIC_NOT_ALLOWED!>dynamic?<!>
d as? dynamic d as? <!DYNAMIC_NOT_ALLOWED!>dynamic<!>
d as? dynamic? d as? <!DYNAMIC_NOT_ALLOWED!>dynamic?<!>
d is dynamic d is <!DYNAMIC_NOT_ALLOWED!>dynamic<!>
d is dynamic? d is <!DYNAMIC_NOT_ALLOWED!>dynamic?<!>
d !is dynamic d !is <!DYNAMIC_NOT_ALLOWED!>dynamic<!>
d !is dynamic? d !is <!DYNAMIC_NOT_ALLOWED!>dynamic?<!>
when (d) { when (d) {
is dynamic -> {} is <!DYNAMIC_NOT_ALLOWED!>dynamic<!> -> {}
is dynamic? -> {} is <!DYNAMIC_NOT_ALLOWED!>dynamic?<!> -> {}
!is dynamic -> {} !is <!DYNAMIC_NOT_ALLOWED!>dynamic<!> -> {}
!is dynamic? -> {} !is <!DYNAMIC_NOT_ALLOWED!>dynamic?<!> -> {}
} }
dl as List<dynamic> dl as List<dynamic>