FIR: Implement more precise reporting diagnostics from new inference

This commit is contained in:
Denis.Zharkov
2021-04-27 15:19:18 +03:00
committed by teamcityserver
parent c67ae8a0a3
commit c46d049394
11 changed files with 150 additions and 16 deletions
@@ -38,6 +38,9 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val UNSUPPORTED_FEATURE by error<PsiElement> {
parameter<Pair<LanguageFeature, LanguageVersionSettings>>("unsupportedFeature")
}
val NEW_INFERENCE_ERROR by error<PsiElement> {
parameter<String>("error")
}
}
val Miscellaneous by object : DiagnosticGroup("Miscellaneous") {
@@ -74,6 +74,7 @@ object FirErrors {
// Meta-errors
val UNSUPPORTED by error1<PsiElement, String>()
val UNSUPPORTED_FEATURE by error1<PsiElement, Pair<LanguageFeature, LanguageVersionSettings>>()
val NEW_INFERENCE_ERROR by error1<PsiElement, String>()
// Miscellaneous
val SYNTAX by error0<PsiElement>()
@@ -15,10 +15,17 @@ 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.model.ConeArgumentConstraintPosition
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExplicitTypeParameterConstraintPosition
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -70,10 +77,11 @@ fun ConeDiagnostic.toFirDiagnostics(
source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?
): List<FirDiagnostic<FirSourceElement>> {
if (this is ConeInapplicableCandidateError) {
return mapInapplicableCandidateError(this, source, qualifiedAccessSource)
return when (this) {
is ConeInapplicableCandidateError -> mapInapplicableCandidateError(this, source, qualifiedAccessSource)
is ConeConstraintSystemHasContradiction -> mapSystemHasContradictionError(this, source, qualifiedAccessSource)
else -> listOfNotNull(toFirDiagnostic(source, qualifiedAccessSource))
}
return listOfNotNull(toFirDiagnostic(source, qualifiedAccessSource))
}
private fun mapUnsafeCallError(
@@ -150,6 +158,76 @@ private fun mapInapplicableCandidateError(
}.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) }
}
@OptIn(ExperimentalStdlibApi::class)
private fun mapSystemHasContradictionError(
diagnostic: ConeConstraintSystemHasContradiction,
source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?,
): List<FirDiagnostic<FirSourceElement>> = buildList<FirDiagnostic<FirSourceElement>> {
for (error in diagnostic.candidate.system.errors) {
addIfNotNull(error.toDiagnostic(source, qualifiedAccessSource, diagnostic.candidate.callInfo.session.typeContext))
}
}.ifEmpty {
listOfNotNull(
diagnostic.candidate.system.errors.firstNotNullOfOrNull {
val message = when (it) {
is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} <!: ${it.upperType}"
// Error should be reported on the error type itself
is ConstrainingTypeIsError -> return@firstNotNullOfOrNull null
else -> "Inference error: ${it::class.simpleName}"
}
FirErrors.NEW_INFERENCE_ERROR.on(qualifiedAccessSource ?: source, message)
}
)
}
private fun ConstraintSystemError.toDiagnostic(
source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?,
typeContext: ConeTypeContext,
): FirDiagnostic<FirSourceElement>? = when (this) {
is NewConstraintError -> {
val position = position.from
val argument =
when (position) {
// TODO: Support other ReceiverConstraintPositionImpl, LHSArgumentConstraintPositionImpl
is ConeArgumentConstraintPosition -> position.argument
is ConeLambdaArgumentConstraintPosition -> position.lambda
else -> null
}
argument?.let {
return FirErrors.TYPE_MISMATCH.on(it.source ?: source, lowerConeType, upperConeType)
}
when (position) {
is ExpectedTypeConstraintPosition<*> -> {
val inferredType =
if (!lowerConeType.isNullableNothing)
lowerConeType
else
upperConeType.withNullability(ConeNullability.NULLABLE, typeContext)
FirErrors.TYPE_MISMATCH.on(qualifiedAccessSource ?: source, upperConeType, inferredType)
}
is ExplicitTypeParameterConstraintPosition<*> -> {
val conePosition = position as ConeExplicitTypeParameterConstraintPosition
val typeArgument = conePosition.typeArgument
FirErrors.UPPER_BOUND_VIOLATED.on(
typeArgument.source ?: qualifiedAccessSource ?: source,
upperConeType,
)
}
else -> null
}
}
else -> null
}
private val NewConstraintError.lowerConeType: ConeKotlinType get() = lowerType as ConeKotlinType
private val NewConstraintError.upperConeType: ConeKotlinType get() = upperType as ConeKotlinType
private fun ConeSimpleDiagnostic.getFactory(source: FirSourceElement): FirDiagnosticFactory0<*> {
@Suppress("UNCHECKED_CAST")
return when (kind) {
@@ -13,13 +13,14 @@ import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeDeclaredUpperBoundConstraintPosition
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExplicitTypeParameterConstraintPosition
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirTypePlaceholderProjection
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
@@ -71,7 +72,7 @@ internal object CreateFreshTypeVariableSubstitutorStage : ResolutionStage() {
typeParameter,
context.session
).fullyExpandedType(context.session),
SimpleConstraintSystemConstraintPosition // TODO
ConeExplicitTypeParameterConstraintPosition(typeArgument)
)
is FirStarProjection -> csBuilder.addEqualityConstraint(
freshVariable.defaultType,
@@ -50,6 +50,12 @@ class ConeInapplicableCandidateError(
override val reason: String get() = "Inapplicable($applicability): ${describeSymbol(candidate.symbol)}"
}
class ConeConstraintSystemHasContradiction(
val candidate: Candidate,
) : ConeDiagnostic() {
override val reason: String get() = "CS errors: ${describeSymbol(candidate.symbol)}"
}
class ConeArgumentTypeMismatchCandidateError(
val expectedType: ConeKotlinType, val actualType: ConeKotlinType
) : ConeDiagnostic() {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
@@ -12,10 +13,10 @@ import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.fir.types.ConeTypeVariableType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.resolve.calls.model.LambdaWithTypeVariableAsExpectedTypeMarker
import org.jetbrains.kotlin.resolve.calls.model.PostponedCallableReferenceMarker
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
@@ -29,6 +30,7 @@ class ConeTypeVariableForPostponedAtom(name: String) : ConeTypeVariable(name)
// -------------------------- Atoms --------------------------
sealed class PostponedResolvedAtom : PostponedResolvedAtomMarker {
abstract val atom: FirElement
abstract override val inputTypes: Collection<ConeKotlinType>
abstract override val outputType: ConeKotlinType?
override var analyzed: Boolean = false
@@ -38,7 +40,7 @@ sealed class PostponedResolvedAtom : PostponedResolvedAtomMarker {
// ------------- Lambdas -------------
class ResolvedLambdaAtom(
val atom: FirAnonymousFunction,
override val atom: FirAnonymousFunction,
expectedType: ConeKotlinType?,
val isSuspend: Boolean,
val receiver: ConeKotlinType?,
@@ -75,7 +77,7 @@ class ResolvedLambdaAtom(
}
class LambdaWithTypeVariableAsExpectedTypeAtom(
val atom: FirAnonymousFunction,
override val atom: FirAnonymousFunction,
private val initialExpectedTypeType: ConeKotlinType,
val expectedTypeRef: FirTypeRef,
val candidateOfOuterCall: Candidate,
@@ -116,6 +118,9 @@ class ResolvedCallableReferenceAtom(
private val session: FirSession
) : PostponedResolvedAtom(), PostponedCallableReferenceMarker {
override val atom: FirCallableReferenceAccess
get() = reference
var hasBeenResolvedOnce: Boolean = false
var hasBeenPostponed: Boolean = false
@@ -5,13 +5,24 @@
package org.jetbrains.kotlin.fir.resolve.inference.model
import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.DeclaredUpperBoundConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.FixVariableConstraintPosition
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.types.model.TypeVariableMarker
class ConeDeclaredUpperBoundConstraintPosition : DeclaredUpperBoundConstraintPosition<Nothing?>(null)
class ConeFixVariableConstraintPosition(variable: TypeVariableMarker) : FixVariableConstraintPosition<Nothing?>(variable, null)
class ConeArgumentConstraintPosition : ArgumentConstraintPosition<Nothing?>(null)
class ConeArgumentConstraintPosition(argument: FirElement) : ArgumentConstraintPosition<FirElement>(argument)
class ConeExpectedTypeConstraintPosition : ExpectedTypeConstraintPosition<Nothing?>(null)
class ConeExplicitTypeParameterConstraintPosition(
typeArgument: FirTypeProjection,
) : ExplicitTypeParameterConstraintPosition<FirTypeProjection>(typeArgument)
class ConeLambdaArgumentConstraintPosition(
anonymousFunction: FirAnonymousFunction
) : LambdaArgumentConstraintPosition<FirAnonymousFunction>(anonymousFunction)
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeConstraintSystemHasContradiction
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeParameterInQualifiedAccess
import org.jetbrains.kotlin.fir.resolve.inference.*
@@ -37,6 +38,7 @@ import org.jetbrains.kotlin.fir.types.builder.buildStarProjection
import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.visitors.*
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.runIf
@@ -739,7 +741,14 @@ class FirCallCompletionResultsWriterTransformer(
private fun FirNamedReferenceWithCandidate.toResolvedReference(): FirNamedReference {
val errorDiagnostic = when {
this is FirErrorReferenceWithCandidate -> this.diagnostic
!candidate.isSuccessful -> ConeInapplicableCandidateError(candidate.currentApplicability, candidate)
!candidate.currentApplicability.isSuccess -> ConeInapplicableCandidateError(candidate.currentApplicability, candidate)
!candidate.isSuccessful -> {
require(candidate.system.hasContradiction) {
"Candidate is not successful, but system has no contradiction"
}
ConeConstraintSystemHasContradiction(candidate)
}
else -> null
}
@@ -71,6 +71,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.NEW_INFERENCE_ERROR) { firDiagnostic ->
NewInferenceErrorImpl(
firDiagnostic.a,
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.SYNTAX) { firDiagnostic ->
SyntaxImpl(
firDiagnostic as FirPsiDiagnostic<*>,
@@ -74,6 +74,11 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val unsupportedFeature: Pair<LanguageFeature, LanguageVersionSettings>
}
abstract class NewInferenceError : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = NewInferenceError::class
abstract val error: String
}
abstract class Syntax : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = Syntax::class
}
@@ -81,6 +81,14 @@ internal class UnsupportedFeatureImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class NewInferenceErrorImpl(
override val error: String,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.NewInferenceError(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class SyntaxImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,