FIR checker: report SETTER_PROJECTED_OUT

This commit is contained in:
Tianyu Geng
2021-06-17 21:36:07 +03:00
committed by TeamCityServer
parent c3a91efea3
commit cdfb2fb3d9
13 changed files with 77 additions and 48 deletions
@@ -811,6 +811,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val CAPTURED_MEMBER_VAL_INITIALIZATION by error<KtExpression> { val CAPTURED_MEMBER_VAL_INITIALIZATION by error<KtExpression> {
parameter<FirPropertySymbol>("property") parameter<FirPropertySymbol>("property")
} }
val SETTER_PROJECTED_OUT by error<KtBinaryExpression>(PositioningStrategy.ASSIGNMENT_LHS) {
parameter<FirPropertySymbol>("property")
}
val WRONG_INVOCATION_KIND by warning<PsiElement> { val WRONG_INVOCATION_KIND by warning<PsiElement> {
parameter<Symbol>("declaration") parameter<Symbol>("declaration")
parameter<EventOccurrencesRange>("requiredRange") parameter<EventOccurrencesRange>("requiredRange")
@@ -451,6 +451,7 @@ object FirErrors {
val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error1<KtExpression, FirPropertySymbol>() val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error1<KtExpression, FirPropertySymbol>()
val CAPTURED_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>() val CAPTURED_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>()
val CAPTURED_MEMBER_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>() val CAPTURED_MEMBER_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>()
val SETTER_PROJECTED_OUT by error1<KtBinaryExpression, FirPropertySymbol>(SourceElementPositioningStrategies.ASSIGNMENT_LHS)
val WRONG_INVOCATION_KIND by warning3<PsiElement, FirBasedSymbol<*>, EventOccurrencesRange, EventOccurrencesRange>() val WRONG_INVOCATION_KIND by warning3<PsiElement, FirBasedSymbol<*>, EventOccurrencesRange, EventOccurrencesRange>()
val LEAKED_IN_PLACE_LAMBDA by error1<PsiElement, FirBasedSymbol<*>>() val LEAKED_IN_PLACE_LAMBDA by error1<PsiElement, FirBasedSymbol<*>>()
val WRONG_IMPLIES_CONDITION by warning0<PsiElement>() val WRONG_IMPLIES_CONDITION by warning0<PsiElement>()
@@ -15,9 +15,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.analysis.getChild import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.SessionHolder
@@ -535,6 +533,7 @@ val ConeTypeProjection.isConflictingOrNotInvariant: Boolean get() = kind != Proj
fun checkTypeMismatch( fun checkTypeMismatch(
lValueOriginalType: ConeKotlinType, lValueOriginalType: ConeKotlinType,
assignment: FirVariableAssignment?,
rValue: FirExpression, rValue: FirExpression,
context: CheckerContext, context: CheckerContext,
source: FirSourceElement, source: FirSourceElement,
@@ -543,25 +542,16 @@ fun checkTypeMismatch(
) { ) {
var lValueType = lValueOriginalType var lValueType = lValueOriginalType
var rValueType = rValue.typeRef.coneType var rValueType = rValue.typeRef.coneType
val typeContext = context.session.typeContext if (source.kind is FirFakeSourceElementKind.DesugaredIncrementOrDecrement) {
if (!lValueType.isNullable && rValueType.isNullable) {
val diagnosticFactory = when { val tempType = rValueType
isInitializer -> { rValueType = lValueType
FirErrors.INITIALIZER_TYPE_MISMATCH lValueType = tempType
}
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
} }
} }
val typeContext = context.session.typeContext
if (!isSubtypeForTypeMismatch(typeContext, subtype = rValueType, supertype = lValueType)) { if (!isSubtypeForTypeMismatch(typeContext, subtype = rValueType, supertype = lValueType)) {
if (rValueType is ConeClassLikeType && if (rValueType is ConeClassLikeType &&
rValueType.lookupTag.classId == StandardClassIds.Int && rValueType.lookupTag.classId == StandardClassIds.Int &&
@@ -576,10 +566,30 @@ fun checkTypeMismatch(
// TODO: remove after fix of KT-45989 // TODO: remove after fix of KT-45989
return return
} }
if (rValue.isNullLiteral && lValueType.nullability == ConeNullability.NOT_NULL) { val resolvedSymbol = assignment?.calleeReference?.toResolvedCallableSymbol() as? FirPropertySymbol
reporter.reportOn(rValue.source, FirErrors.NULL_FOR_NONNULL_TYPE, context) when {
} else { resolvedSymbol != null && lValueType is ConeCapturedType && lValueType.constructor.projection.kind.let {
reporter.reportOn(source, diagnosticFactory, lValueType, rValueType, context) 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)
}
} }
} }
} }
@@ -23,6 +23,6 @@ object FirInitializerTypeMismatchChecker : FirPropertyChecker() {
if (declaration.returnTypeRef.source?.kind != FirRealSourceElementKind) return if (declaration.returnTypeRef.source?.kind != FirRealSourceElementKind) return
val propertyType = declaration.returnTypeRef.coneType val propertyType = declaration.returnTypeRef.coneType
checkTypeMismatch(propertyType, initializer, context, source, reporter, true) checkTypeMismatch(propertyType, null, initializer, context, source, reporter, true)
} }
} }
@@ -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.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment 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 import org.jetbrains.kotlin.fir.types.coneType
object FirAssignmentTypeMismatchChecker : FirVariableAssignmentChecker() { object FirAssignmentTypeMismatchChecker : FirVariableAssignmentChecker() {
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val source = expression.rValue.source ?: return val source = expression.rValue.source ?: return
val coneType = expression.lValueTypeRef.coneType val coneType = expression.lValueTypeRef.coneType
checkTypeMismatch(coneType, expression.rValue, context, source, reporter, false) checkTypeMismatch(
coneType,
expression,
expression.rValue,
context,
source,
reporter,
false,
)
} }
} }
@@ -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
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE_IN_LOCAL_CLASS 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.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.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.SINGLETON_IN_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE 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", "Captured member values initialization is forbidden due to possible reassignment",
VARIABLE_NAME VARIABLE_NAME
) )
map.put(
SETTER_PROJECTED_OUT,
"Setter for ''{0}'' is removed by type projection",
VARIABLE_NAME
)
map.put( map.put(
WRONG_INVOCATION_KIND, WRONG_INVOCATION_KIND,
"{2} wrong invocation kind: given {3} case, but {4} case is possible", "{2} wrong invocation kind: given {3} case, but {4} case is possible",
@@ -1,13 +0,0 @@
// !DIAGNOSTICS: -UNREACHABLE_CODE -UNUSED_PARAMETER
// !CHECK_TYPE
// t is unused due to KT-4233
interface Tr<T> {
var v: T
}
fun test(t: Tr<*>) {
t.v = null!!
t.v = <!ASSIGNMENT_TYPE_MISMATCH!>""<!>
t.v = <!NULL_FOR_NONNULL_TYPE!>null<!>
t.v checkType { _<Any?>() }
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNREACHABLE_CODE -UNUSED_PARAMETER // !DIAGNOSTICS: -UNREACHABLE_CODE -UNUSED_PARAMETER
// !CHECK_TYPE // !CHECK_TYPE
// t is unused due to KT-4233 // t is unused due to KT-4233
@@ -1,10 +0,0 @@
// !DIAGNOSTICS: -UNREACHABLE_CODE
interface Tr<T> {
var v: T
}
fun test(t: Tr<out String>) {
// resolved as t.v = t.v + null!!, where type of right operand is String,
// so TYPE_MISMATCH: String is not <: of Captured(out String)
<!ASSIGNMENT_TYPE_MISMATCH!>t.v += null!!<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNREACHABLE_CODE // !DIAGNOSTICS: -UNREACHABLE_CODE
interface Tr<T> { interface Tr<T> {
var v: T var v: T
@@ -2250,6 +2250,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.SETTER_PROJECTED_OUT) { firDiagnostic ->
SetterProjectedOutImpl(
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir),
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.WRONG_INVOCATION_KIND) { firDiagnostic -> add(FirErrors.WRONG_INVOCATION_KIND) { firDiagnostic ->
WrongInvocationKindImpl( WrongInvocationKindImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir as FirDeclaration), firSymbolBuilder.buildSymbol(firDiagnostic.a.fir as FirDeclaration),
@@ -1585,6 +1585,11 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val property: KtVariableSymbol abstract val property: KtVariableSymbol
} }
abstract class SetterProjectedOut : KtFirDiagnostic<KtBinaryExpression>() {
override val diagnosticClass get() = SetterProjectedOut::class
abstract val property: KtVariableSymbol
}
abstract class WrongInvocationKind : KtFirDiagnostic<PsiElement>() { abstract class WrongInvocationKind : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = WrongInvocationKind::class override val diagnosticClass get() = WrongInvocationKind::class
abstract val declaration: KtSymbol abstract val declaration: KtSymbol
@@ -2564,6 +2564,14 @@ internal class CapturedMemberValInitializationImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
} }
internal class SetterProjectedOutImpl(
override val property: KtVariableSymbol,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.SetterProjectedOut(), KtAbstractFirDiagnostic<KtBinaryExpression> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class WrongInvocationKindImpl( internal class WrongInvocationKindImpl(
override val declaration: KtSymbol, override val declaration: KtSymbol,
override val requiredRange: EventOccurrencesRange, override val requiredRange: EventOccurrencesRange,