FIR checker: record whether type mismatch is due to nullability

This is useful for downstream quickfixes.
This commit is contained in:
Tianyu Geng
2021-08-03 14:59:52 -07:00
committed by teamcityserver
parent d3e8cc577c
commit 951812f130
18 changed files with 156 additions and 44 deletions
@@ -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)
@@ -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,
)
@@ -904,16 +904,19 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = TypeMismatch::class
abstract val expectedType: KtType
abstract val actualType: KtType
abstract val isMismatchDueToNullability: Boolean
}
abstract class ThrowableTypeMismatch : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ThrowableTypeMismatch::class
abstract val actualType: KtType
abstract val isMismatchDueToNullability: Boolean
}
abstract class ConditionTypeMismatch : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ConditionTypeMismatch::class
abstract val actualType: KtType
abstract val isMismatchDueToNullability: Boolean
}
abstract class ArgumentTypeMismatch : KtFirDiagnostic<PsiElement>() {
@@ -968,6 +971,7 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = AssignmentTypeMismatch::class
abstract val expectedType: KtType
abstract val actualType: KtType
abstract val isMismatchDueToNullability: Boolean
}
abstract class ResultTypeMismatch : KtFirDiagnostic<KtExpression>() {
@@ -1216,6 +1220,7 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val expectedType: KtType
abstract val actualType: KtType
abstract val targetFunction: KtSymbol
abstract val isMismatchDueToNullability: Boolean
}
abstract class CyclicGenericUpperBound : KtFirDiagnostic<PsiElement>() {
@@ -1267,6 +1272,7 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val desiredType: KtType
abstract val subject: KtExpression
abstract val description: String
abstract val isCastToNotNull: Boolean
}
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
@@ -1709,6 +1715,7 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = InitializerTypeMismatch::class
abstract val expectedType: KtType
abstract val actualType: KtType
abstract val isMismatchDueToNullability: Boolean
}
abstract class GetterVisibilityDiffersFromPropertyVisibility : KtFirDiagnostic<KtModifierListOwner>() {
@@ -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<PsiElement> {
@@ -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<PsiElement> {
@@ -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<PsiElement> {
@@ -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<KtExpression> {
@@ -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<KtExpression> {
@@ -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<KtExpression> {
@@ -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<KtProperty> {
@@ -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
}
@@ -401,14 +401,17 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val TYPE_MISMATCH by error<PsiElement> {
parameter<ConeKotlinType>("expectedType")
parameter<ConeKotlinType>("actualType")
parameter<Boolean>("isMismatchDueToNullability")
}
val THROWABLE_TYPE_MISMATCH by error<PsiElement> {
parameter<ConeKotlinType>("actualType")
parameter<Boolean>("isMismatchDueToNullability")
}
val CONDITION_TYPE_MISMATCH by error<PsiElement> {
parameter<ConeKotlinType>("actualType")
parameter<Boolean>("isMismatchDueToNullability")
}
val ARGUMENT_TYPE_MISMATCH by error<PsiElement> {
@@ -446,6 +449,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val ASSIGNMENT_TYPE_MISMATCH by error<KtExpression> {
parameter<ConeKotlinType>("expectedType")
parameter<ConeKotlinType>("actualType")
parameter<Boolean>("isMismatchDueToNullability")
}
val RESULT_TYPE_MISMATCH by error<KtExpression> {
@@ -579,6 +583,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<ConeKotlinType>("expectedType")
parameter<ConeKotlinType>("actualType")
parameter<FirSimpleFunction>("targetFunction")
parameter<Boolean>("isMismatchDueToNullability")
}
val CYCLIC_GENERIC_UPPER_BOUND by error<PsiElement>()
@@ -617,6 +622,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<ConeKotlinType>("desiredType")
parameter<FirExpression>("subject")
parameter<String>("description")
parameter<Boolean>("isCastToNotNull")
}
}
@@ -874,6 +880,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val INITIALIZER_TYPE_MISMATCH by error<KtProperty>(PositioningStrategy.PROPERTY_INITIALIZER) {
parameter<ConeKotlinType>("expectedType")
parameter<ConeKotlinType>("actualType")
parameter<Boolean>("isMismatchDueToNullability")
}
val GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY by error<KtModifierListOwner>(PositioningStrategy.VISIBILITY_MODIFIER)
val SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY by error<KtModifierListOwner>(PositioningStrategy.VISIBILITY_MODIFIER)
@@ -300,9 +300,9 @@ object FirErrors {
// Applicability
val NONE_APPLICABLE by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val INAPPLICABLE_CANDIDATE by error1<PsiElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val TYPE_MISMATCH by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
val THROWABLE_TYPE_MISMATCH by error1<PsiElement, ConeKotlinType>()
val CONDITION_TYPE_MISMATCH by error1<PsiElement, ConeKotlinType>()
val TYPE_MISMATCH by error3<PsiElement, ConeKotlinType, ConeKotlinType, Boolean>()
val THROWABLE_TYPE_MISMATCH by error2<PsiElement, ConeKotlinType, Boolean>()
val CONDITION_TYPE_MISMATCH by error2<PsiElement, ConeKotlinType, Boolean>()
val ARGUMENT_TYPE_MISMATCH by error3<PsiElement, ConeKotlinType, ConeKotlinType, Boolean>()
val NULL_FOR_NONNULL_TYPE by error0<PsiElement>()
val INAPPLICABLE_LATEINIT_MODIFIER by error1<KtModifierListOwner, String>(SourceElementPositioningStrategies.LATEINIT_MODIFIER)
@@ -313,7 +313,7 @@ object FirErrors {
val TOO_MANY_ARGUMENTS by error1<PsiElement, FirCallableSymbol<*>>()
val NO_VALUE_FOR_PARAMETER by error1<KtElement, FirValueParameterSymbol>(SourceElementPositioningStrategies.VALUE_ARGUMENTS)
val NAMED_PARAMETER_NOT_FOUND by error1<KtValueArgument, String>(SourceElementPositioningStrategies.NAME_OF_NAMED_ARGUMENT)
val ASSIGNMENT_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>()
val ASSIGNMENT_TYPE_MISMATCH by error3<KtExpression, ConeKotlinType, ConeKotlinType, Boolean>()
val RESULT_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>()
val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error0<KtValueArgument>()
val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error1<KtElement, String>()
@@ -368,7 +368,7 @@ object FirErrors {
val REIFIED_TYPE_PARAMETER_NO_INLINE by error0<KtTypeParameter>(SourceElementPositioningStrategies.REIFIED_MODIFIER)
val TYPE_PARAMETERS_NOT_ALLOWED by error0<KtDeclaration>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
val TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER by error0<KtTypeParameter>()
val RETURN_TYPE_MISMATCH by error3<KtExpression, ConeKotlinType, ConeKotlinType, FirSimpleFunction>(SourceElementPositioningStrategies.WHOLE_ELEMENT)
val RETURN_TYPE_MISMATCH by error4<KtExpression, ConeKotlinType, ConeKotlinType, FirSimpleFunction, Boolean>(SourceElementPositioningStrategies.WHOLE_ELEMENT)
val CYCLIC_GENERIC_UPPER_BOUND by error0<PsiElement>()
val DEPRECATED_TYPE_PARAMETER_SYNTAX by error0<KtDeclaration>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning0<KtTypeParameter>()
@@ -377,7 +377,7 @@ object FirErrors {
val INCOMPATIBLE_TYPES_WARNING by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
val TYPE_VARIANCE_CONFLICT by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val SMARTCAST_IMPOSSIBLE by error3<KtExpression, ConeKotlinType, FirExpression, String>()
val SMARTCAST_IMPOSSIBLE by error4<KtExpression, ConeKotlinType, FirExpression, String, Boolean>()
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<KtExpression, FirCallableSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -482,7 +482,7 @@ object FirErrors {
val CONST_VAL_WITHOUT_INITIALIZER by error0<KtProperty>(SourceElementPositioningStrategies.CONST_MODIFIER)
val CONST_VAL_WITH_NON_CONST_INITIALIZER by error0<KtExpression>()
val WRONG_SETTER_PARAMETER_TYPE by error2<KtTypeReference, ConeKotlinType, ConeKotlinType>()
val INITIALIZER_TYPE_MISMATCH by error2<KtProperty, ConeKotlinType, ConeKotlinType>(SourceElementPositioningStrategies.PROPERTY_INITIALIZER)
val INITIALIZER_TYPE_MISMATCH by error3<KtProperty, ConeKotlinType, ConeKotlinType, Boolean>(SourceElementPositioningStrategies.PROPERTY_INITIALIZER)
val GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY by error0<KtModifierListOwner>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
val SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY by error0<KtModifierListOwner>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
val WRONG_SETTER_RETURN_TYPE by error0<KtTypeReference>()
@@ -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
)
}
}
@@ -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
)
}
}
}
@@ -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
)
}
@@ -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,
@@ -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 <A, B, C> FirDiagnosticFactory3<A, B, C>.createOn(
): FirDiagnosticWithParameters3<A, B, C>? {
return element?.let { on(it, a, b, c, positioningStrategy = null) }
}
@OptIn(InternalDiagnosticFactoryMethod::class)
private fun <A, B, C, D> FirDiagnosticFactory4<A, B, C, D>.createOn(
element: FirSourceElement?,
a: A,
b: B,
c: C,
d: D
): FirDiagnosticWithParameters4<A, B, C, D>? {
return element?.let { on(it, a, b, c, d, positioningStrategy = null) }
}
@@ -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)
@@ -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)
)
}
@@ -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
}
}
@@ -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,
@@ -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))
}
@@ -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)
)
)
}