diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 16d2aecd66c..0348aea8411 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3779,6 +3779,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.DYNAMIC_NOT_ALLOWED) { firDiagnostic -> + DynamicNotAllowedImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.ENUM_ENTRY_AS_TYPE) { firDiagnostic -> EnumEntryAsTypeImpl( firDiagnostic as KtPsiDiagnostic, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 4942c532820..7272b5891c9 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2641,6 +2641,10 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = IsEnumEntry::class } + interface DynamicNotAllowed : KtFirDiagnostic { + override val diagnosticClass get() = DynamicNotAllowed::class + } + interface EnumEntryAsType : KtFirDiagnostic { override val diagnosticClass get() = EnumEntryAsType::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 3988386ff2b..d55f3f96ad7 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3183,6 +3183,11 @@ internal class IsEnumEntryImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.IsEnumEntry +internal class DynamicNotAllowedImpl( + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.DynamicNotAllowed + internal class EnumEntryAsTypeImpl( firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 369424f7506..87dfe446097 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1307,6 +1307,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("compileTimeCheckResult") } val IS_ENUM_ENTRY by error() + val DYNAMIC_NOT_ALLOWED by error() val ENUM_ENTRY_AS_TYPE by error(PositioningStrategy.SELECTOR_BY_QUALIFIED) } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 05fcf0420f4..d36da80b326 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -678,6 +678,7 @@ object FirErrors { val UNCHECKED_CAST by warning2(SourceElementPositioningStrategies.AS_TYPE) val USELESS_IS_CHECK by warning1() val IS_ENUM_ENTRY by error0() + val DYNAMIC_NOT_ALLOWED by error0() val ENUM_ENTRY_AS_TYPE by error0(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) // When expressions diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCastOperatorsChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCastOperatorsChecker.kt index d23f91d0d92..61d8e7908b3 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCastOperatorsChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCastOperatorsChecker.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.FirOperation import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.types.ConeDynamicType import org.jetbrains.kotlin.fir.types.coneType object FirCastOperatorsChecker : FirTypeOperatorCallChecker() { @@ -29,22 +30,33 @@ object FirCastOperatorsChecker : FirTypeOperatorCallChecker() { val isSafeAs = expression.operation == FirOperation.SAFE_AS if (expression.operation == FirOperation.AS || isSafeAs) { - 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) + if (targetType is ConeDynamicType) { + reporter.reportOn(conversionTypeRef.source, FirErrors.DYNAMIC_NOT_ALLOWED, context) + } else { + 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) { + if (targetType is ConeDynamicType) { + reporter.reportOn(conversionTypeRef.source, FirErrors.DYNAMIC_NOT_ALLOWED, context) + } if (!context.isContractBody && isCastErased(actualType, 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) + } } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index 5eeb4e5cfce..72b0a527695 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -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.EXPOSED_PROPERTY_TYPE_IN_CONSTRUCTOR 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_TAILREC_FUNCTION 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(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(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(ENUM_ENTRY_AS_TYPE, "Use of enum entry names as types is not allowed, use enum type instead") diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicCastTarget.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicCastTarget.fir.kt index 60eea0bca92..ac950599641 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicCastTarget.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicCastTarget.fir.kt @@ -1,23 +1,23 @@ // !DIAGNOSTICS: -REDUNDANT_NULLABLE fun test(d: Any, dl: Collection) { - d as dynamic - d as dynamic? + d as dynamic + d as dynamic? - d as? dynamic - d as? dynamic? + d as? dynamic + d as? dynamic? - d is dynamic - d is dynamic? + d is dynamic + d is dynamic? - d !is dynamic - d !is dynamic? + d !is dynamic + d !is dynamic? when (d) { - is dynamic -> {} - is dynamic? -> {} - !is dynamic -> {} - !is dynamic? -> {} + is dynamic -> {} + is dynamic? -> {} + !is dynamic -> {} + !is dynamic? -> {} } dl as List