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 b4aa78328c0..d7ef5594ecd 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 @@ -811,6 +811,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val CAPTURED_MEMBER_VAL_INITIALIZATION by error { parameter("property") } + val SETTER_PROJECTED_OUT by error(PositioningStrategy.ASSIGNMENT_LHS) { + parameter("property") + } val WRONG_INVOCATION_KIND by warning { parameter("declaration") parameter("requiredRange") 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 b35273a1143..bdd0ce96083 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 @@ -451,6 +451,7 @@ object FirErrors { val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error1() val CAPTURED_VAL_INITIALIZATION by error1() val CAPTURED_MEMBER_VAL_INITIALIZATION by error1() + val SETTER_PROJECTED_OUT by error1(SourceElementPositioningStrategies.ASSIGNMENT_LHS) val WRONG_INVOCATION_KIND by warning3, EventOccurrencesRange, EventOccurrencesRange>() val LEAKED_IN_PLACE_LAMBDA by error1>() val WRONG_IMPLIES_CONDITION by warning0() 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 f4073da075d..e7333fdeec8 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 @@ -15,9 +15,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.* import org.jetbrains.kotlin.fir.analysis.getChild import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.* -import org.jetbrains.kotlin.fir.expressions.FirExpression -import org.jetbrains.kotlin.fir.expressions.FirFunctionCall -import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.resolve.SessionHolder @@ -535,6 +533,7 @@ val ConeTypeProjection.isConflictingOrNotInvariant: Boolean get() = kind != Proj fun checkTypeMismatch( lValueOriginalType: ConeKotlinType, + assignment: FirVariableAssignment?, rValue: FirExpression, context: CheckerContext, source: FirSourceElement, @@ -543,25 +542,16 @@ fun checkTypeMismatch( ) { var lValueType = lValueOriginalType var rValueType = rValue.typeRef.coneType - val typeContext = context.session.typeContext - - val diagnosticFactory = when { - isInitializer -> { - FirErrors.INITIALIZER_TYPE_MISMATCH - } - source.kind is FirFakeSourceElementKind.DesugaredIncrementOrDecrement -> { - if (!lValueType.isNullable && rValueType.isNullable) { - val tempType = rValueType - rValueType = lValueType - lValueType = tempType - } - FirErrors.RESULT_TYPE_MISMATCH - } - else -> { - FirErrors.ASSIGNMENT_TYPE_MISMATCH + if (source.kind is FirFakeSourceElementKind.DesugaredIncrementOrDecrement) { + if (!lValueType.isNullable && rValueType.isNullable) { + val tempType = rValueType + rValueType = lValueType + lValueType = tempType } } + val typeContext = context.session.typeContext + if (!isSubtypeForTypeMismatch(typeContext, subtype = rValueType, supertype = lValueType)) { if (rValueType is ConeClassLikeType && rValueType.lookupTag.classId == StandardClassIds.Int && @@ -576,10 +566,30 @@ fun checkTypeMismatch( // TODO: remove after fix of KT-45989 return } - if (rValue.isNullLiteral && lValueType.nullability == ConeNullability.NOT_NULL) { - reporter.reportOn(rValue.source, FirErrors.NULL_FOR_NONNULL_TYPE, context) - } else { - reporter.reportOn(source, diagnosticFactory, lValueType, rValueType, context) + val resolvedSymbol = assignment?.calleeReference?.toResolvedCallableSymbol() as? FirPropertySymbol + when { + resolvedSymbol != null && lValueType is ConeCapturedType && lValueType.constructor.projection.kind.let { + it == ProjectionKind.STAR || it == ProjectionKind.OUT + } -> { + reporter.reportOn(assignment.source, FirErrors.SETTER_PROJECTED_OUT, resolvedSymbol, context) + } + rValue.isNullLiteral && lValueType.nullability == ConeNullability.NOT_NULL -> { + reporter.reportOn(rValue.source, FirErrors.NULL_FOR_NONNULL_TYPE, context) + } + isInitializer -> { + reporter.reportOn(source, FirErrors.INITIALIZER_TYPE_MISMATCH, lValueType, rValueType, context) + } + source.kind is FirFakeSourceElementKind.DesugaredIncrementOrDecrement -> { + if (!lValueType.isNullable && rValueType.isNullable) { + val tempType = rValueType + rValueType = lValueType + lValueType = tempType + } + reporter.reportOn(source, FirErrors.RESULT_TYPE_MISMATCH, lValueType, rValueType, context) + } + else -> { + reporter.reportOn(source, FirErrors.ASSIGNMENT_TYPE_MISMATCH, lValueType, rValueType, context) + } } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInitializerTypeMismatchChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInitializerTypeMismatchChecker.kt index d92b38413e6..8d55e748297 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInitializerTypeMismatchChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInitializerTypeMismatchChecker.kt @@ -23,6 +23,6 @@ object FirInitializerTypeMismatchChecker : FirPropertyChecker() { if (declaration.returnTypeRef.source?.kind != FirRealSourceElementKind) return val propertyType = declaration.returnTypeRef.coneType - checkTypeMismatch(propertyType, initializer, context, source, reporter, true) + checkTypeMismatch(propertyType, null, initializer, context, source, reporter, true) } } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAssignmentTypeMismatchChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAssignmentTypeMismatchChecker.kt index bddcb3e6f51..68de198a7c0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAssignmentTypeMismatchChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAssignmentTypeMismatchChecker.kt @@ -9,12 +9,22 @@ import org.jetbrains.kotlin.fir.analysis.checkers.checkTypeMismatch import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment +import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.types.coneType object FirAssignmentTypeMismatchChecker : FirVariableAssignmentChecker() { override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { val source = expression.rValue.source ?: return val coneType = expression.lValueTypeRef.coneType - checkTypeMismatch(coneType, expression.rValue, context, source, reporter, false) + checkTypeMismatch( + coneType, + expression, + expression.rValue, + context, + source, + reporter, + false, + ) } } 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 2e0c302afd6..e2e3bb0c283 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 @@ -301,6 +301,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_CLASS_CONS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE_IN_LOCAL_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SETTER_PROJECTED_OUT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SINGLETON_IN_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE @@ -1096,6 +1097,11 @@ class FirDefaultErrorMessages { "Captured member values initialization is forbidden due to possible reassignment", VARIABLE_NAME ) + map.put( + SETTER_PROJECTED_OUT, + "Setter for ''{0}'' is removed by type projection", + VARIABLE_NAME + ) map.put( WRONG_INVOCATION_KIND, "{2} wrong invocation kind: given {3} case, but {4} case is possible", diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.fir.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.fir.kt deleted file mode 100644 index 2a9d439e679..00000000000 --- a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.fir.kt +++ /dev/null @@ -1,13 +0,0 @@ -// !DIAGNOSTICS: -UNREACHABLE_CODE -UNUSED_PARAMETER -// !CHECK_TYPE -// t is unused due to KT-4233 -interface Tr { - var v: T -} - -fun test(t: Tr<*>) { - t.v = null!! - t.v = "" - t.v = null - t.v checkType { _() } -} diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt index be7dcf38e55..ca08a5c40ee 100644 --- a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNREACHABLE_CODE -UNUSED_PARAMETER // !CHECK_TYPE // t is unused due to KT-4233 diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.fir.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.fir.kt deleted file mode 100644 index 108e8147295..00000000000 --- a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.fir.kt +++ /dev/null @@ -1,10 +0,0 @@ -// !DIAGNOSTICS: -UNREACHABLE_CODE -interface Tr { - var v: T -} - -fun test(t: Tr) { - // resolved as t.v = t.v + null!!, where type of right operand is String, - // so TYPE_MISMATCH: String is not <: of Captured(out String) - t.v += null!! -} diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt index f1169423b91..fd237dcebb1 100644 --- a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNREACHABLE_CODE interface Tr { var v: T diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 0a64c401e23..8a66d4f2592 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -2250,6 +2250,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.SETTER_PROJECTED_OUT) { firDiagnostic -> + SetterProjectedOutImpl( + firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir), + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.WRONG_INVOCATION_KIND) { firDiagnostic -> WrongInvocationKindImpl( firSymbolBuilder.buildSymbol(firDiagnostic.a.fir as FirDeclaration), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 954073776e9..593a9f8c825 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1585,6 +1585,11 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val property: KtVariableSymbol } + abstract class SetterProjectedOut : KtFirDiagnostic() { + override val diagnosticClass get() = SetterProjectedOut::class + abstract val property: KtVariableSymbol + } + abstract class WrongInvocationKind : KtFirDiagnostic() { override val diagnosticClass get() = WrongInvocationKind::class abstract val declaration: KtSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 0608db6655c..15ce5dd5136 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -2564,6 +2564,14 @@ internal class CapturedMemberValInitializationImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class SetterProjectedOutImpl( + override val property: KtVariableSymbol, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.SetterProjectedOut(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class WrongInvocationKindImpl( override val declaration: KtSymbol, override val requiredRange: EventOccurrencesRange,