diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirBuiltInTypes.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirBuiltInTypes.kt index 3687e28de11..64bc3bf29b0 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirBuiltInTypes.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirBuiltInTypes.kt @@ -36,7 +36,7 @@ internal class KtFirBuiltInTypes(builtinTypes: BuiltinTypes, builder: KtSymbolBy override val NOTHING: KtType by cachedBuiltin(builtinTypes.nothingType) override val ANY: KtType by cachedBuiltin(builtinTypes.anyType) - + override val THROWABLE: KtType by cachedBuiltin(builtinTypes.throwableType) override val NULLABLE_ANY: KtType by cachedBuiltin(builtinTypes.nullableAnyType) override val NULLABLE_NOTHING: KtType by cachedBuiltin(builtinTypes.nullableNothingType) 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 0cf81332038..d16b6665075 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 @@ -1255,6 +1255,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert TypeMismatchImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b), + firDiagnostic.c, firDiagnostic as FirPsiDiagnostic, token, ) @@ -1262,6 +1263,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert add(FirErrors.THROWABLE_TYPE_MISMATCH) { firDiagnostic -> ThrowableTypeMismatchImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firDiagnostic.b, firDiagnostic as FirPsiDiagnostic, token, ) @@ -1269,6 +1271,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert add(FirErrors.CONDITION_TYPE_MISMATCH) { firDiagnostic -> ConditionTypeMismatchImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firDiagnostic.b, firDiagnostic as FirPsiDiagnostic, token, ) @@ -1345,6 +1348,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert AssignmentTypeMismatchImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b), + firDiagnostic.c, firDiagnostic as FirPsiDiagnostic, token, ) @@ -1711,6 +1715,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b), firSymbolBuilder.buildSymbol(firDiagnostic.c), + firDiagnostic.d, firDiagnostic as FirPsiDiagnostic, token, ) @@ -1780,6 +1785,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), firDiagnostic.b.source!!.psi as KtExpression, firDiagnostic.c, + firDiagnostic.d, firDiagnostic as FirPsiDiagnostic, token, ) @@ -2416,6 +2422,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert InitializerTypeMismatchImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b), + firDiagnostic.c, firDiagnostic as FirPsiDiagnostic, token, ) 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 c02919f5f2d..8b96498cac3 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 @@ -904,16 +904,19 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = TypeMismatch::class abstract val expectedType: KtType abstract val actualType: KtType + abstract val isMismatchDueToNullability: Boolean } abstract class ThrowableTypeMismatch : KtFirDiagnostic() { override val diagnosticClass get() = ThrowableTypeMismatch::class abstract val actualType: KtType + abstract val isMismatchDueToNullability: Boolean } abstract class ConditionTypeMismatch : KtFirDiagnostic() { override val diagnosticClass get() = ConditionTypeMismatch::class abstract val actualType: KtType + abstract val isMismatchDueToNullability: Boolean } abstract class ArgumentTypeMismatch : KtFirDiagnostic() { @@ -968,6 +971,7 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = AssignmentTypeMismatch::class abstract val expectedType: KtType abstract val actualType: KtType + abstract val isMismatchDueToNullability: Boolean } abstract class ResultTypeMismatch : KtFirDiagnostic() { @@ -1216,6 +1220,7 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val expectedType: KtType abstract val actualType: KtType abstract val targetFunction: KtSymbol + abstract val isMismatchDueToNullability: Boolean } abstract class CyclicGenericUpperBound : KtFirDiagnostic() { @@ -1267,6 +1272,7 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val desiredType: KtType abstract val subject: KtExpression abstract val description: String + abstract val isCastToNotNull: Boolean } abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic() { @@ -1709,6 +1715,7 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = InitializerTypeMismatch::class abstract val expectedType: KtType abstract val actualType: KtType + abstract val isMismatchDueToNullability: Boolean } abstract class GetterVisibilityDiffersFromPropertyVisibility : KtFirDiagnostic() { 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 0633af6dbb7..262bc669673 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 @@ -1438,6 +1438,7 @@ internal class InapplicableCandidateImpl( internal class TypeMismatchImpl( override val expectedType: KtType, override val actualType: KtType, + override val isMismatchDueToNullability: Boolean, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.TypeMismatch(), KtAbstractFirDiagnostic { @@ -1446,6 +1447,7 @@ internal class TypeMismatchImpl( internal class ThrowableTypeMismatchImpl( override val actualType: KtType, + override val isMismatchDueToNullability: Boolean, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.ThrowableTypeMismatch(), KtAbstractFirDiagnostic { @@ -1454,6 +1456,7 @@ internal class ThrowableTypeMismatchImpl( internal class ConditionTypeMismatchImpl( override val actualType: KtType, + override val isMismatchDueToNullability: Boolean, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.ConditionTypeMismatch(), KtAbstractFirDiagnostic { @@ -1541,6 +1544,7 @@ internal class NamedParameterNotFoundImpl( internal class AssignmentTypeMismatchImpl( override val expectedType: KtType, override val actualType: KtType, + override val isMismatchDueToNullability: Boolean, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.AssignmentTypeMismatch(), KtAbstractFirDiagnostic { @@ -1951,6 +1955,7 @@ internal class ReturnTypeMismatchImpl( override val expectedType: KtType, override val actualType: KtType, override val targetFunction: KtSymbol, + override val isMismatchDueToNullability: Boolean, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.ReturnTypeMismatch(), KtAbstractFirDiagnostic { @@ -2029,6 +2034,7 @@ internal class SmartcastImpossibleImpl( override val desiredType: KtType, override val subject: KtExpression, override val description: String, + override val isCastToNotNull: Boolean, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.SmartcastImpossible(), KtAbstractFirDiagnostic { @@ -2747,6 +2753,7 @@ internal class WrongSetterParameterTypeImpl( internal class InitializerTypeMismatchImpl( override val expectedType: KtType, override val actualType: KtType, + override val isMismatchDueToNullability: Boolean, firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, ) : KtFirDiagnostic.InitializerTypeMismatch(), KtAbstractFirDiagnostic { diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtTypeProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtTypeProvider.kt index 42db7d10b4b..4acba62a683 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtTypeProvider.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtTypeProvider.kt @@ -96,6 +96,8 @@ public abstract class KtBuiltinTypes : ValidityTokenOwner { public abstract val NOTHING: KtType public abstract val ANY: KtType + public abstract val THROWABLE: KtType + public abstract val NULLABLE_ANY: KtType public abstract val NULLABLE_NOTHING: KtType } 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 b73cc77d8ff..8a7ff76ec51 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 @@ -401,14 +401,17 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val TYPE_MISMATCH by error { parameter("expectedType") parameter("actualType") + parameter("isMismatchDueToNullability") } val THROWABLE_TYPE_MISMATCH by error { parameter("actualType") + parameter("isMismatchDueToNullability") } val CONDITION_TYPE_MISMATCH by error { parameter("actualType") + parameter("isMismatchDueToNullability") } val ARGUMENT_TYPE_MISMATCH by error { @@ -446,6 +449,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ASSIGNMENT_TYPE_MISMATCH by error { parameter("expectedType") parameter("actualType") + parameter("isMismatchDueToNullability") } val RESULT_TYPE_MISMATCH by error { @@ -579,6 +583,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("expectedType") parameter("actualType") parameter("targetFunction") + parameter("isMismatchDueToNullability") } val CYCLIC_GENERIC_UPPER_BOUND by error() @@ -617,6 +622,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("desiredType") parameter("subject") parameter("description") + parameter("isCastToNotNull") } } @@ -874,6 +880,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val INITIALIZER_TYPE_MISMATCH by error(PositioningStrategy.PROPERTY_INITIALIZER) { parameter("expectedType") parameter("actualType") + parameter("isMismatchDueToNullability") } val GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY by error(PositioningStrategy.VISIBILITY_MODIFIER) val SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY by error(PositioningStrategy.VISIBILITY_MODIFIER) 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 cabc160aa2e..a49d55e09f9 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 @@ -300,9 +300,9 @@ object FirErrors { // Applicability val NONE_APPLICABLE by error1>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val INAPPLICABLE_CANDIDATE by error1>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) - val TYPE_MISMATCH by error2() - val THROWABLE_TYPE_MISMATCH by error1() - val CONDITION_TYPE_MISMATCH by error1() + val TYPE_MISMATCH by error3() + val THROWABLE_TYPE_MISMATCH by error2() + val CONDITION_TYPE_MISMATCH by error2() val ARGUMENT_TYPE_MISMATCH by error3() val NULL_FOR_NONNULL_TYPE by error0() val INAPPLICABLE_LATEINIT_MODIFIER by error1(SourceElementPositioningStrategies.LATEINIT_MODIFIER) @@ -313,7 +313,7 @@ object FirErrors { val TOO_MANY_ARGUMENTS by error1>() val NO_VALUE_FOR_PARAMETER by error1(SourceElementPositioningStrategies.VALUE_ARGUMENTS) val NAMED_PARAMETER_NOT_FOUND by error1(SourceElementPositioningStrategies.NAME_OF_NAMED_ARGUMENT) - val ASSIGNMENT_TYPE_MISMATCH by error2() + val ASSIGNMENT_TYPE_MISMATCH by error3() val RESULT_TYPE_MISMATCH by error2() val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error0() val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error1() @@ -368,7 +368,7 @@ object FirErrors { val REIFIED_TYPE_PARAMETER_NO_INLINE by error0(SourceElementPositioningStrategies.REIFIED_MODIFIER) val TYPE_PARAMETERS_NOT_ALLOWED by error0(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST) val TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER by error0() - val RETURN_TYPE_MISMATCH by error3(SourceElementPositioningStrategies.WHOLE_ELEMENT) + val RETURN_TYPE_MISMATCH by error4(SourceElementPositioningStrategies.WHOLE_ELEMENT) val CYCLIC_GENERIC_UPPER_BOUND by error0() val DEPRECATED_TYPE_PARAMETER_SYNTAX by error0(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST) val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning0() @@ -377,7 +377,7 @@ object FirErrors { val INCOMPATIBLE_TYPES_WARNING by warning2() val TYPE_VARIANCE_CONFLICT by error4(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE by error4(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) - val SMARTCAST_IMPOSSIBLE by error3() + val SMARTCAST_IMPOSSIBLE by error4() // Reflection val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) @@ -482,7 +482,7 @@ object FirErrors { val CONST_VAL_WITHOUT_INITIALIZER by error0(SourceElementPositioningStrategies.CONST_MODIFIER) val CONST_VAL_WITH_NON_CONST_INITIALIZER by error0() val WRONG_SETTER_PARAMETER_TYPE by error2() - val INITIALIZER_TYPE_MISMATCH by error2(SourceElementPositioningStrategies.PROPERTY_INITIALIZER) + val INITIALIZER_TYPE_MISMATCH by error3(SourceElementPositioningStrategies.PROPERTY_INITIALIZER) val GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY by error0(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) val SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY by error0(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) val WRONG_SETTER_RETURN_TYPE by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index 44b31f2298f..1123cf5e7ad 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -576,7 +576,14 @@ fun checkTypeMismatch( reporter.reportOn(rValue.source, FirErrors.NULL_FOR_NONNULL_TYPE, context) } isInitializer -> { - reporter.reportOn(source, FirErrors.INITIALIZER_TYPE_MISMATCH, lValueType, rValueType, context) + reporter.reportOn( + source, + FirErrors.INITIALIZER_TYPE_MISMATCH, + lValueType, + rValueType, + context.session.typeContext.isTypeMismatchDueToNullability(rValueType, lValueType), + context + ) } source.kind is FirFakeSourceElementKind.DesugaredIncrementOrDecrement -> { if (!lValueType.isNullable && rValueType.isNullable) { @@ -591,7 +598,14 @@ fun checkTypeMismatch( } } else -> { - reporter.reportOn(source, FirErrors.ASSIGNMENT_TYPE_MISMATCH, lValueType, rValueType, context) + reporter.reportOn( + source, + FirErrors.ASSIGNMENT_TYPE_MISMATCH, + lValueType, + rValueType, + context.session.typeContext.isTypeMismatchDueToNullability(rValueType, lValueType), + context + ) } } } @@ -603,7 +617,13 @@ internal fun checkCondition(condition: FirExpression, context: CheckerContext, r coneType !is ConeKotlinErrorType && !coneType.isSubtypeOf(context.session.typeContext, context.session.builtinTypes.booleanType.type) ) { - reporter.reportOn(condition.source, FirErrors.CONDITION_TYPE_MISMATCH, coneType, context) + reporter.reportOn( + condition.source, + FirErrors.CONDITION_TYPE_MISMATCH, + coneType, + coneType.isNullableBoolean, + context + ) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCatchParameterChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCatchParameterChecker.kt index d7d74c88aa6..905ec21dadd 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCatchParameterChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCatchParameterChecker.kt @@ -12,6 +12,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.expressions.FirTryExpression +import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents +import org.jetbrains.kotlin.fir.resolve.isTypeMismatchDueToNullability import org.jetbrains.kotlin.fir.types.ConeTypeParameterType import org.jetbrains.kotlin.fir.types.coneType @@ -42,7 +44,16 @@ object FirCatchParameterChecker : FirTryExpressionChecker() { val session = context.session if (!coneType.isSubtypeOfThrowable(session)) { - reporter.reportOn(source, FirErrors.THROWABLE_TYPE_MISMATCH, coneType, context) + reporter.reportOn( + source, + FirErrors.THROWABLE_TYPE_MISMATCH, + coneType, + context.session.inferenceComponents.ctx.isTypeMismatchDueToNullability( + coneType, + session.builtinTypes.throwableType.type + ), + context + ) } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt index 70f186af3be..bfa65bf46e3 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt @@ -17,9 +17,9 @@ import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast import org.jetbrains.kotlin.fir.expressions.FirReturnExpression import org.jetbrains.kotlin.fir.expressions.FirWhenExpression import org.jetbrains.kotlin.fir.expressions.isExhaustive +import org.jetbrains.kotlin.fir.resolve.isTypeMismatchDueToNullability import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.types.SmartcastStability object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { override fun check(expression: FirReturnExpression, context: CheckerContext, reporter: DiagnosticReporter) { @@ -38,7 +38,9 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { if (resultExpression.isNullLiteral && functionReturnType.nullability == ConeNullability.NOT_NULL) { reporter.reportOn(resultExpression.source, NULL_FOR_NONNULL_TYPE, context) } else { - if (resultExpression is FirExpressionWithSmartcast && resultExpression.smartcastStability != SmartcastStability.STABLE_VALUE && + val isDueToNullability = + context.session.typeContext.isTypeMismatchDueToNullability(returnExpressionType, functionReturnType) + if (resultExpression is FirExpressionWithSmartcast && !resultExpression.isStable && isSubtypeForTypeMismatch(typeContext, subtype = resultExpression.smartcastType.coneType, supertype = functionReturnType) ) { reporter.reportOn( @@ -47,6 +49,7 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { functionReturnType, resultExpression, resultExpression.smartcastStability.description, + isDueToNullability, context ) } else { @@ -56,6 +59,7 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { functionReturnType, returnExpressionType, targetElement, + isDueToNullability, context ) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 121753e0c8b..10e3e4c0a8e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -812,9 +812,9 @@ class FirDefaultErrorMessages { map.put(REDUNDANT_SPREAD_OPERATOR_IN_NAMED_FORM_IN_ANNOTATION, "Redundant spread (*) operator"); map.put(REDUNDANT_SPREAD_OPERATOR_IN_NAMED_FORM_IN_FUNCTION, "Redundant spread (*) operator"); - map.put(TYPE_MISMATCH, "Type mismatch: inferred type is {1} but {0} was expected", TO_STRING, TO_STRING) - map.put(THROWABLE_TYPE_MISMATCH, "Throwable type mismatch: actual type is {0}", TO_STRING) - map.put(CONDITION_TYPE_MISMATCH, "Condition type mismatch: inferred type is {0} but Boolean was expected", TO_STRING) + map.put(TYPE_MISMATCH, "Type mismatch: inferred type is {1} but {0} was expected", TO_STRING, TO_STRING, NOT_RENDERED) + map.put(THROWABLE_TYPE_MISMATCH, "Throwable type mismatch: actual type is {0}", TO_STRING, NOT_RENDERED) + map.put(CONDITION_TYPE_MISMATCH, "Condition type mismatch: inferred type is {0} but Boolean was expected", TO_STRING, NOT_RENDERED) map.put( ARGUMENT_TYPE_MISMATCH, "Argument type mismatch: actual type is {1} but {0} was expected", @@ -822,12 +822,12 @@ class FirDefaultErrorMessages { RENDER_TYPE, NOT_RENDERED ) - map.put(ASSIGNMENT_TYPE_MISMATCH, "Assignment type mismatch: actual type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE) + map.put(ASSIGNMENT_TYPE_MISMATCH, "Assignment type mismatch: actual type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE, NOT_RENDERED) map.put( RESULT_TYPE_MISMATCH, "Function return type mismatch: actual type is {1} but {0} was expected", RENDER_TYPE, - RENDER_TYPE + RENDER_TYPE, ) // Ambiguity @@ -921,7 +921,8 @@ class FirDefaultErrorMessages { "Smart cast to ''{0}'' is impossible, because ''{1}'' is a {2}", RENDER_TYPE, FIR, - TO_STRING + TO_STRING, + NOT_RENDERED ) map.put( @@ -966,7 +967,7 @@ class FirDefaultErrorMessages { map.put(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, "A 'return' expression required in a function with a block body ('{...}')") - map.put(RETURN_TYPE_MISMATCH, "Return type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE, NOT_RENDERED) + map.put(RETURN_TYPE_MISMATCH, "Return type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE, NOT_RENDERED, NOT_RENDERED) map.put(CYCLIC_GENERIC_UPPER_BOUND, "Type parameter has cyclic upper bounds") @@ -1279,7 +1280,7 @@ class FirDefaultErrorMessages { RENDER_TYPE, RENDER_TYPE ) - map.put(INITIALIZER_TYPE_MISMATCH, "Initializer type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE) + map.put(INITIALIZER_TYPE_MISMATCH, "Initializer type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE, NOT_RENDERED) map.put(GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY, "Getter visibility must be the same as property visibility") map.put( SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index 08c051e3615..73ba3a4a72e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeVariableForLambdaRetur import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExpectedTypeConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition +import org.jetbrains.kotlin.fir.resolve.isTypeMismatchDueToNullability import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.typeContext @@ -60,7 +61,8 @@ private fun ConeDiagnostic.toFirDiagnostic( unstableSmartcast.argument.source, unstableSmartcast.targetType, unstableSmartcast.argument, - unstableSmartcast.argument.smartcastStability.description + unstableSmartcast.argument.smartcastStability.description, + unstableSmartcast.isCastToNotNull ) } else -> FirErrors.NONE_APPLICABLE.createOn(source, this.candidates.map { it.symbol }) @@ -203,7 +205,8 @@ private fun mapInapplicableCandidateError( rootCause.argument.source, rootCause.targetType, rootCause.argument, - rootCause.argument.smartcastStability.description + rootCause.argument.smartcastStability.description, + rootCause.isCastToNotNull ) else -> genericDiagnostic } @@ -278,12 +281,13 @@ private fun ConstraintSystemError.toDiagnostic( else -> null } + val typeMismatchDueToNullability = typeContext.isTypeMismatchDueToNullability(lowerConeType, upperConeType) argument?.let { return FirErrors.ARGUMENT_TYPE_MISMATCH.createOn( it.source ?: source, lowerConeType, upperConeType, - isArgumentTypeMismatchDueToNullability(lowerConeType, upperConeType, typeContext) + typeMismatchDueToNullability ) } @@ -299,7 +303,12 @@ private fun ConstraintSystemError.toDiagnostic( else upperConeType.withNullability(ConeNullability.NULLABLE, typeContext) - FirErrors.TYPE_MISMATCH.createOn(qualifiedAccessSource ?: source, upperConeType, inferredType) + FirErrors.TYPE_MISMATCH.createOn( + qualifiedAccessSource ?: source, + upperConeType, + inferredType, + typeMismatchDueToNullability + ) } is ExplicitTypeParameterConstraintPosition<*>, is DelegatedPropertyConstraintPosition<*> -> { @@ -418,3 +427,14 @@ private fun FirDiagnosticFactory3.createOn( ): FirDiagnosticWithParameters3? { return element?.let { on(it, a, b, c, positioningStrategy = null) } } + +@OptIn(InternalDiagnosticFactoryMethod::class) +private fun FirDiagnosticFactory4.createOn( + element: FirSourceElement?, + a: A, + b: B, + c: C, + d: D +): FirDiagnosticWithParameters4? { + return element?.let { on(it, a, b, c, d, positioningStrategy = null) } +} diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt index da908fc1b5a..82ac5255398 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt @@ -63,6 +63,7 @@ val ConeKotlinType.isNothing: Boolean get() = isBuiltinType(StandardClassIds.Not val ConeKotlinType.isNullableNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, true) val ConeKotlinType.isUnit: Boolean get() = isBuiltinType(StandardClassIds.Unit, false) val ConeKotlinType.isBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, false) +val ConeKotlinType.isNullableBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, true) val ConeKotlinType.isBooleanOrNullableBoolean: Boolean get() = isAnyOfBuiltinType(setOf(StandardClassIds.Boolean)) val ConeKotlinType.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false) val ConeKotlinType.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index ef25e78e223..309810503e5 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget +import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.SmartcastStability import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -549,3 +550,15 @@ fun FirClassLikeDeclaration.getContainingDeclaration(session: FirSession): FirCl return null } + +fun ConeTypeContext.isTypeMismatchDueToNullability( + actualType: ConeKotlinType, + expectedType: ConeKotlinType +): Boolean { + return actualType.isNullableType() && !expectedType.isNullableType() && AbstractTypeChecker.isSubtypeOf( + this, + actualType, + expectedType.withNullability(ConeNullability.NULLABLE, this) + ) +} + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index c51e30cd5eb..0fdf0aa812f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.createFunctionalType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.inference.* +import org.jetbrains.kotlin.fir.resolve.isTypeMismatchDueToNullability import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedTypeDeclaration import org.jetbrains.kotlin.fir.returnExpressions @@ -340,18 +341,6 @@ private fun Candidate.captureTypeFromExpressionOrNull(argumentType: ConeKotlinTy ) as? ConeKotlinType } -fun isArgumentTypeMismatchDueToNullability( - argumentType: ConeKotlinType, - actualExpectedType: ConeKotlinType, - typeContext: ConeTypeContext -): Boolean { - return AbstractTypeChecker.isSubtypeOf( - typeContext, - argumentType, - actualExpectedType.withNullability(ConeNullability.NULLABLE, typeContext) - ) -} - private fun checkApplicabilityForArgumentType( csBuilder: ConstraintSystemBuilder, argument: FirExpression, @@ -394,7 +383,7 @@ private fun checkApplicabilityForArgumentType( argument, // Reaching here means argument types mismatch, and we want to record whether it's due to the nullability by checking a subtype // relation with nullable expected type. - isArgumentTypeMismatchDueToNullability(argumentType, actualExpectedType, context.session.typeContext) + context.session.typeContext.isTypeMismatchDueToNullability(argumentType, actualExpectedType) ) } @@ -410,7 +399,16 @@ private fun checkApplicabilityForArgumentType( if (smartcastExpression != null && !smartcastExpression.isStable) { val unstableType = smartcastExpression.smartcastType.coneType if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) { - sink.reportDiagnostic(UnstableSmartCast(smartcastExpression, expectedType)) + sink.reportDiagnostic( + UnstableSmartCast( + smartcastExpression, + expectedType, + context.session.typeContext.isTypeMismatchDueToNullability( + argumentType, + expectedType + ) + ) + ) return } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index b4347a4afb8..a0a8a44d3b8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -78,7 +78,7 @@ object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RES object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED) -class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType) : ResolutionDiagnostic(UNSTABLE_SMARTCAST) +class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType, val isCastToNotNull: Boolean) : ResolutionDiagnostic(UNSTABLE_SMARTCAST) class ArgumentTypeMismatch( val expectedType: ConeKotlinType, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index 39bede7a30b..1f39aaf6608 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.modality import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.resolve.inference.* +import org.jetbrains.kotlin.fir.resolve.isTypeMismatchDueToNullability import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol @@ -121,7 +122,16 @@ object CheckDispatchReceiver : ResolutionStage() { !explicitReceiverExpression.isStable && (isCandidateFromUnstableSmartcast || (isReceiverNullable && !explicitReceiverExpression.smartcastType.canBeNull)) ) { - sink.yieldDiagnostic(UnstableSmartCast(explicitReceiverExpression, explicitReceiverExpression.smartcastType.coneType)) + sink.yieldDiagnostic( + UnstableSmartCast( + explicitReceiverExpression, + explicitReceiverExpression.smartcastType.coneType, + context.session.typeContext.isTypeMismatchDueToNullability( + explicitReceiverExpression.originalType.coneType, + explicitReceiverExpression.smartcastType.coneType + ) + ) + ) } else if (isReceiverNullable) { sink.yieldDiagnostic(UnsafeCall(dispatchReceiverValueType)) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt index a0cb78d9fae..b2f3f9c0abd 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt @@ -9,9 +9,13 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.expressions.FirAnonymousFunctionExpression import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess -import org.jetbrains.kotlin.fir.resolve.calls.* +import org.jetbrains.kotlin.fir.resolve.calls.ArgumentTypeMismatch +import org.jetbrains.kotlin.fir.resolve.calls.Candidate +import org.jetbrains.kotlin.fir.resolve.calls.CheckerSink +import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext import org.jetbrains.kotlin.fir.resolve.createFunctionalType import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition +import org.jetbrains.kotlin.fir.resolve.isTypeMismatchDueToNullability import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder @@ -63,7 +67,7 @@ fun Candidate.preprocessLambdaArgument( lambdaType, expectedType, argument, - isArgumentTypeMismatchDueToNullability(lambdaType, expectedType, context.session.typeContext) + context.session.typeContext.isTypeMismatchDueToNullability(lambdaType, expectedType) ) ) }