FIR: Report INFERENCE_NO_INFORMATION_FOR_PARAMETER diagnostic

This commit is contained in:
Denis.Zharkov
2021-05-25 10:33:46 +03:00
committed by TeamCityServer
parent 6e901e3785
commit c420957eac
34 changed files with 143 additions and 236 deletions
@@ -341,6 +341,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
}
val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error<KtValueArgument>()
val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error<KtElement> {
parameter<String>("name")
}
}
val AMBIGUITY by object : DiagnosticGroup("Ambiguity") {
@@ -256,6 +256,7 @@ object FirErrors {
val ASSIGNMENT_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>()
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>()
// Ambiguity
val OVERLOAD_RESOLUTION_AMBIGUITY by error1<PsiElement, Collection<AbstractFirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -15,6 +15,8 @@ import org.jetbrains.kotlin.fir.declarations.isOperator
import org.jetbrains.kotlin.fir.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeVariableForLambdaReturnType
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
@@ -191,6 +193,7 @@ private fun mapSystemHasContradictionError(
qualifiedAccessSource,
diagnostic.candidate.callInfo.session.typeContext,
errorsToIgnore,
diagnostic.candidate,
)
)
}
@@ -202,6 +205,7 @@ private fun mapSystemHasContradictionError(
is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} <!: ${it.upperType}"
// Error should be reported on the error type itself
is ConstrainingTypeIsError -> return@firstNotNullOfOrNull null
is NotEnoughInformationForTypeParameter<*> -> return@firstNotNullOfOrNull null
else -> "Inference error: ${it::class.simpleName}"
}
@@ -223,6 +227,7 @@ private fun ConstraintSystemError.toDiagnostic(
qualifiedAccessSource: FirSourceElement?,
typeContext: ConeTypeContext,
errorsToIgnore: MutableSet<ConstraintSystemError>,
candidate: Candidate,
): FirDiagnostic<FirSourceElement>? {
return when (this) {
is NewConstraintError -> {
@@ -270,6 +275,25 @@ private fun ConstraintSystemError.toDiagnostic(
else -> null
}
}
is NotEnoughInformationForTypeParameter<*> -> {
val isDiagnosticRedundant = candidate.system.errors.any { otherError ->
(otherError is ConstrainingTypeIsError && otherError.typeVariable == this.typeVariable)
|| otherError is NewConstraintError
}
if (isDiagnosticRedundant) return null
val typeVariableName = when (val typeVariable = this.typeVariable) {
is ConeTypeParameterBasedTypeVariable -> typeVariable.typeParameterSymbol.name.asString()
is ConeTypeVariableForLambdaReturnType -> "return type of lambda"
else -> error("Unsupported type variable: $typeVariable")
}
FirErrors.NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER.on(
source,
typeVariableName,
)
}
else -> null
}
}
@@ -87,7 +87,7 @@ typealias ConeKotlinErrorType = ConeClassErrorType
class ConeClassLikeErrorLookupTag(override val classId: ClassId) : ConeClassLikeLookupTag()
class ConeClassErrorType(val diagnostic: ConeDiagnostic) : ConeClassLikeType() {
class ConeClassErrorType(val diagnostic: ConeDiagnostic, val isUninferredParameter: Boolean = false) : ConeClassLikeType() {
override val lookupTag: ConeClassLikeLookupTag
get() = ConeClassLikeErrorLookupTag(ClassId.fromString("<error>"))
@@ -99,8 +99,9 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
argument: PostponedAtomWithRevisableExpectedType,
index: Int
): TypeVariableMarker {
return ConeTypeVariableForPostponedAtom(
PostponedArgumentInputTypesResolver.TYPE_VARIABLE_NAME_PREFIX_FOR_LAMBDA_PARAMETER_TYPE + index
return ConeTypeVariableForLambdaParameterType(
PostponedArgumentInputTypesResolver.TYPE_VARIABLE_NAME_PREFIX_FOR_LAMBDA_PARAMETER_TYPE + index,
index
)
}
@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
@@ -15,10 +18,12 @@ import org.jetbrains.kotlin.fir.returnExpressions
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.inference.components.*
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -219,19 +224,83 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents) {
fixVariable(asConstraintSystemCompletionContext(), topLevelType, variableWithConstraints, postponedArguments)
return true
} else {
// TODO("Not enough information for parameter")
fixVariable(
asConstraintSystemCompletionContext(),
topLevelType,
variableWithConstraints,
postponedArguments
) // means Nothing/Any instead of Error type
processVariableWhenNotEnoughInformation(this, variableWithConstraints, topLevelAtoms)
}
}
return false
}
private fun processVariableWhenNotEnoughInformation(
c: ConstraintSystemCompletionContext,
variableWithConstraints: VariableWithConstraints,
topLevelAtoms: List<FirStatement>,
) {
val typeVariable = variableWithConstraints.typeVariable
val resolvedAtom =
findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull()
if (resolvedAtom != null) {
c.addError(NotEnoughInformationForTypeParameter(typeVariable, resolvedAtom))
}
val resultErrorType = when (typeVariable) {
is ConeTypeParameterBasedTypeVariable ->
createCannotInferErrorType(
"Cannot infer argument for type parameter ${typeVariable.typeParameterSymbol.name}",
isUninferredParameter = true,
)
is ConeTypeVariableForLambdaParameterType -> createCannotInferErrorType("Cannot infer lambda parameter type")
else -> createCannotInferErrorType("Cannot infer type variable $typeVariable")
}
c.fixVariable(typeVariable, resultErrorType, ConeFixVariableConstraintPosition(typeVariable))
}
private fun createCannotInferErrorType(message: String, isUninferredParameter: Boolean = false) =
ConeClassErrorType(
ConeSimpleDiagnostic(
message,
DiagnosticKind.CannotInferParameterType,
),
isUninferredParameter,
)
private fun findResolvedAtomBy(
typeVariable: TypeVariableMarker,
topLevelAtoms: List<FirStatement>
): FirStatement? {
fun FirStatement.findFirstAtomContainingVariable(): FirStatement? {
var result: FirStatement? = null
fun suggestElement(element: FirElement) {
if (result == null && element is FirStatement) {
result = element
}
}
this@findFirstAtomContainingVariable.processAllContainingCallCandidates(processBlocks = true) { candidate ->
if (typeVariable in candidate.freshVariables) {
suggestElement(candidate.callInfo.callSite)
}
for (postponedAtom in candidate.postponedAtoms) {
if (postponedAtom is ResolvedLambdaAtom) {
if (postponedAtom.typeVariableForLambdaReturnType == typeVariable) {
suggestElement(postponedAtom.atom)
}
}
}
}
return result
}
return topLevelAtoms.firstNotNullOfOrNull(FirStatement::findFirstAtomContainingVariable)
}
private fun analyzeRemainingNotAnalyzedPostponedArgument(
postponedArguments: List<PostponedResolvedAtom>,
analyze: (PostponedResolvedAtom) -> Unit
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.types.model.KotlinTypeMarker
class ConeTypeVariableForLambdaReturnType(val argument: FirAnonymousFunction, name: String) : ConeTypeVariable(name)
class ConeTypeVariableForPostponedAtom(name: String) : ConeTypeVariable(name)
class ConeTypeVariableForLambdaParameterType(name: String, val index: Int) : ConeTypeVariable(name)
// -------------------------- Atoms --------------------------
@@ -91,7 +91,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
override fun KotlinTypeMarker.isUninferredParameter(): Boolean {
assert(this is ConeKotlinType)
return false // TODO
return this is ConeClassErrorType && this.isUninferredParameter
}
override fun FlexibleTypeMarker.asDynamicType(): DynamicTypeMarker? {