FIR: Avoid reporting redundant TYPE_MISMATCH (for assignments)
This commit is contained in:
committed by
teamcityserver
parent
36c9418d55
commit
db500fab94
+25
-2
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.diagnostics.*
|
|||||||
import org.jetbrains.kotlin.fir.resolve.calls.*
|
import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition
|
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.ConeExplicitTypeParameterConstraintPosition
|
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExplicitTypeParameterConstraintPosition
|
||||||
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition
|
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
|
||||||
@@ -164,19 +165,36 @@ private fun mapSystemHasContradictionError(
|
|||||||
source: FirSourceElement,
|
source: FirSourceElement,
|
||||||
qualifiedAccessSource: FirSourceElement?,
|
qualifiedAccessSource: FirSourceElement?,
|
||||||
): List<FirDiagnostic<FirSourceElement>> {
|
): List<FirDiagnostic<FirSourceElement>> {
|
||||||
|
val errorsToIgnore = mutableSetOf<ConstraintSystemError>()
|
||||||
return buildList<FirDiagnostic<FirSourceElement>> {
|
return buildList<FirDiagnostic<FirSourceElement>> {
|
||||||
for (error in diagnostic.candidate.system.errors) {
|
for (error in diagnostic.candidate.system.errors) {
|
||||||
addIfNotNull(error.toDiagnostic(source, qualifiedAccessSource, diagnostic.candidate.callInfo.session.typeContext))
|
addIfNotNull(
|
||||||
|
error.toDiagnostic(
|
||||||
|
source,
|
||||||
|
qualifiedAccessSource,
|
||||||
|
diagnostic.candidate.callInfo.session.typeContext,
|
||||||
|
errorsToIgnore,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}.ifEmpty {
|
}.ifEmpty {
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
diagnostic.candidate.system.errors.firstNotNullOfOrNull {
|
diagnostic.candidate.system.errors.firstNotNullOfOrNull {
|
||||||
|
if (it in errorsToIgnore) return@firstNotNullOfOrNull null
|
||||||
val message = when (it) {
|
val message = when (it) {
|
||||||
is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} <!: ${it.upperType}"
|
is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} <!: ${it.upperType}"
|
||||||
// Error should be reported on the error type itself
|
// Error should be reported on the error type itself
|
||||||
is ConstrainingTypeIsError -> return@firstNotNullOfOrNull null
|
is ConstrainingTypeIsError -> return@firstNotNullOfOrNull null
|
||||||
else -> "Inference error: ${it::class.simpleName}"
|
else -> "Inference error: ${it::class.simpleName}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (it is NewConstraintError && it.position.from is FixVariableConstraintPosition<*>) {
|
||||||
|
val morePreciseDiagnosticExists = diagnostic.candidate.system.errors.any { other ->
|
||||||
|
other is NewConstraintError && other.position.from !is FixVariableConstraintPosition<*>
|
||||||
|
}
|
||||||
|
if (morePreciseDiagnosticExists) return@firstNotNullOfOrNull null
|
||||||
|
}
|
||||||
|
|
||||||
FirErrors.NEW_INFERENCE_ERROR.on(qualifiedAccessSource ?: source, message)
|
FirErrors.NEW_INFERENCE_ERROR.on(qualifiedAccessSource ?: source, message)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -187,6 +205,7 @@ private fun ConstraintSystemError.toDiagnostic(
|
|||||||
source: FirSourceElement,
|
source: FirSourceElement,
|
||||||
qualifiedAccessSource: FirSourceElement?,
|
qualifiedAccessSource: FirSourceElement?,
|
||||||
typeContext: ConeTypeContext,
|
typeContext: ConeTypeContext,
|
||||||
|
errorsToIgnore: MutableSet<ConstraintSystemError>,
|
||||||
): FirDiagnostic<FirSourceElement>? {
|
): FirDiagnostic<FirSourceElement>? {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is NewConstraintError -> {
|
is NewConstraintError -> {
|
||||||
@@ -204,7 +223,11 @@ private fun ConstraintSystemError.toDiagnostic(
|
|||||||
}
|
}
|
||||||
|
|
||||||
when (position) {
|
when (position) {
|
||||||
is ExpectedTypeConstraintPosition<*> -> {
|
is ConeExpectedTypeConstraintPosition -> {
|
||||||
|
if (position.expectedTypeMismatchIsReportedInChecker) {
|
||||||
|
errorsToIgnore.add(this)
|
||||||
|
return null
|
||||||
|
}
|
||||||
val inferredType =
|
val inferredType =
|
||||||
if (!lowerConeType.isNullableNothing)
|
if (!lowerConeType.isNullableNothing)
|
||||||
lowerConeType
|
lowerConeType
|
||||||
|
|||||||
@@ -15,8 +15,13 @@ sealed class ResolutionMode {
|
|||||||
object ContextDependent : ResolutionMode()
|
object ContextDependent : ResolutionMode()
|
||||||
object ContextDependentDelegate : ResolutionMode()
|
object ContextDependentDelegate : ResolutionMode()
|
||||||
object ContextIndependent : ResolutionMode()
|
object ContextIndependent : ResolutionMode()
|
||||||
|
|
||||||
// TODO: it's better not to use WithExpectedType(FirImplicitTypeRef)
|
// TODO: it's better not to use WithExpectedType(FirImplicitTypeRef)
|
||||||
class WithExpectedType(val expectedTypeRef: FirTypeRef, val mayBeCoercionToUnitApplied: Boolean = false) : ResolutionMode()
|
class WithExpectedType(
|
||||||
|
val expectedTypeRef: FirTypeRef,
|
||||||
|
val mayBeCoercionToUnitApplied: Boolean = false,
|
||||||
|
val expectedTypeMismatchIsReportedInChecker: Boolean = false,
|
||||||
|
) : ResolutionMode()
|
||||||
|
|
||||||
class WithStatus(val status: FirDeclarationStatus) : ResolutionMode()
|
class WithStatus(val status: FirDeclarationStatus) : ResolutionMode()
|
||||||
|
|
||||||
@@ -29,8 +34,13 @@ fun ResolutionMode.expectedType(components: BodyResolveComponents): FirTypeRef?
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withExpectedType(expectedTypeRef: FirTypeRef?): ResolutionMode =
|
fun withExpectedType(expectedTypeRef: FirTypeRef?, expectedTypeMismatchIsReportedInChecker: Boolean = false): ResolutionMode =
|
||||||
expectedTypeRef?.let { ResolutionMode.WithExpectedType(it) } ?: ResolutionMode.ContextDependent
|
expectedTypeRef?.let {
|
||||||
|
ResolutionMode.WithExpectedType(
|
||||||
|
it,
|
||||||
|
expectedTypeMismatchIsReportedInChecker = expectedTypeMismatchIsReportedInChecker
|
||||||
|
)
|
||||||
|
} ?: ResolutionMode.ContextDependent
|
||||||
|
|
||||||
@JvmName("withExpectedTypeNullable")
|
@JvmName("withExpectedTypeNullable")
|
||||||
fun withExpectedType(coneType: ConeKotlinType?, mayBeCoercionToUnitApplied: Boolean = false): ResolutionMode {
|
fun withExpectedType(coneType: ConeKotlinType?, mayBeCoercionToUnitApplied: Boolean = false): ResolutionMode {
|
||||||
|
|||||||
+20
-8
@@ -31,8 +31,6 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
|||||||
import org.jetbrains.kotlin.fir.resolve.typeFromCallee
|
import org.jetbrains.kotlin.fir.resolve.typeFromCallee
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -56,13 +54,26 @@ class FirCallCompleter(
|
|||||||
|
|
||||||
data class CompletionResult<T>(val result: T, val callCompleted: Boolean)
|
data class CompletionResult<T>(val result: T, val callCompleted: Boolean)
|
||||||
|
|
||||||
fun <T> completeCall(call: T, expectedTypeRef: FirTypeRef?): CompletionResult<T> where T : FirResolvable, T : FirStatement =
|
fun <T> completeCall(
|
||||||
completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false)
|
call: T,
|
||||||
|
expectedTypeRef: FirTypeRef?,
|
||||||
|
expectedTypeMismatchIsReportedInChecker: Boolean = false,
|
||||||
|
): CompletionResult<T> where T : FirResolvable, T : FirStatement =
|
||||||
|
completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false, expectedTypeMismatchIsReportedInChecker)
|
||||||
|
|
||||||
fun <T> completeCall(call: T, data: ResolutionMode): CompletionResult<T> where T : FirResolvable, T : FirStatement =
|
fun <T> completeCall(call: T, data: ResolutionMode): CompletionResult<T> where T : FirResolvable, T : FirStatement =
|
||||||
completeCall(call, data.expectedType(components), (data as? ResolutionMode.WithExpectedType)?.mayBeCoercionToUnitApplied == true)
|
completeCall(
|
||||||
|
call,
|
||||||
|
data.expectedType(components),
|
||||||
|
(data as? ResolutionMode.WithExpectedType)?.mayBeCoercionToUnitApplied == true,
|
||||||
|
(data as? ResolutionMode.WithExpectedType)?.expectedTypeMismatchIsReportedInChecker == true,
|
||||||
|
)
|
||||||
|
|
||||||
fun <T> completeCall(call: T, expectedTypeRef: FirTypeRef?, mayBeCoercionToUnitApplied: Boolean): CompletionResult<T>
|
private fun <T> completeCall(
|
||||||
|
call: T, expectedTypeRef: FirTypeRef?,
|
||||||
|
mayBeCoercionToUnitApplied: Boolean,
|
||||||
|
expectedTypeMismatchIsReportedInChecker: Boolean,
|
||||||
|
): CompletionResult<T>
|
||||||
where T : FirResolvable, T : FirStatement {
|
where T : FirResolvable, T : FirStatement {
|
||||||
val typeRef = components.typeFromCallee(call)
|
val typeRef = components.typeFromCallee(call)
|
||||||
|
|
||||||
@@ -82,14 +93,15 @@ class FirCallCompleter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (expectedTypeRef is FirResolvedTypeRef) {
|
if (expectedTypeRef is FirResolvedTypeRef) {
|
||||||
|
val expectedTypeConstraintPosition = ConeExpectedTypeConstraintPosition(expectedTypeMismatchIsReportedInChecker)
|
||||||
if (expectedTypeRef.coneType.isUnitOrFlexibleUnit && mayBeCoercionToUnitApplied) {
|
if (expectedTypeRef.coneType.isUnitOrFlexibleUnit && mayBeCoercionToUnitApplied) {
|
||||||
if (candidate.system.notFixedTypeVariables.isNotEmpty()) {
|
if (candidate.system.notFixedTypeVariables.isNotEmpty()) {
|
||||||
candidate.system.addSubtypeConstraintIfCompatible(
|
candidate.system.addSubtypeConstraintIfCompatible(
|
||||||
initialType, expectedTypeRef.type, ConeExpectedTypeConstraintPosition()
|
initialType, expectedTypeRef.type, expectedTypeConstraintPosition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
candidate.system.addSubtypeConstraint(initialType, expectedTypeRef.type, ConeExpectedTypeConstraintPosition())
|
candidate.system.addSubtypeConstraint(initialType, expectedTypeRef.type, expectedTypeConstraintPosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -17,7 +17,9 @@ class ConeFixVariableConstraintPosition(variable: TypeVariableMarker) : FixVaria
|
|||||||
|
|
||||||
class ConeArgumentConstraintPosition(argument: FirElement) : ArgumentConstraintPosition<FirElement>(argument)
|
class ConeArgumentConstraintPosition(argument: FirElement) : ArgumentConstraintPosition<FirElement>(argument)
|
||||||
|
|
||||||
class ConeExpectedTypeConstraintPosition : ExpectedTypeConstraintPosition<Nothing?>(null)
|
class ConeExpectedTypeConstraintPosition(
|
||||||
|
val expectedTypeMismatchIsReportedInChecker: Boolean
|
||||||
|
) : ExpectedTypeConstraintPosition<Nothing?>(null)
|
||||||
|
|
||||||
class ConeExplicitTypeParameterConstraintPosition(
|
class ConeExplicitTypeParameterConstraintPosition(
|
||||||
typeArgument: FirTypeProjection,
|
typeArgument: FirTypeProjection,
|
||||||
|
|||||||
+9
-2
@@ -423,7 +423,11 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun chooseOperator(): FirStatement {
|
fun chooseOperator(): FirStatement {
|
||||||
callCompleter.completeCall(resolvedOperatorCall, lhsVariable?.returnTypeRef ?: noExpectedType)
|
callCompleter.completeCall(
|
||||||
|
resolvedOperatorCall,
|
||||||
|
lhsVariable?.returnTypeRef ?: noExpectedType,
|
||||||
|
expectedTypeMismatchIsReportedInChecker = true
|
||||||
|
)
|
||||||
dataFlowAnalyzer.exitFunctionCall(resolvedOperatorCall, callCompleted = true)
|
dataFlowAnalyzer.exitFunctionCall(resolvedOperatorCall, callCompleted = true)
|
||||||
val assignment =
|
val assignment =
|
||||||
buildVariableAssignment {
|
buildVariableAssignment {
|
||||||
@@ -684,7 +688,10 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
|||||||
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment)
|
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment)
|
||||||
val result = if (resolvedAssignment is FirVariableAssignment) {
|
val result = if (resolvedAssignment is FirVariableAssignment) {
|
||||||
val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check
|
val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check
|
||||||
completeAssignment.transformRValue(transformer, withExpectedType(variableAssignment.lValueTypeRef))
|
completeAssignment.transformRValue(
|
||||||
|
transformer,
|
||||||
|
withExpectedType(variableAssignment.lValueTypeRef, expectedTypeMismatchIsReportedInChecker = true),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// This can happen in erroneous code only
|
// This can happen in erroneous code only
|
||||||
resolvedAssignment
|
resolvedAssignment
|
||||||
|
|||||||
Reference in New Issue
Block a user