[NI] Fix completion of error delegates with receiver with type parameters

This commit is contained in:
Dmitriy Novozhilov
2019-12-25 15:00:26 +03:00
parent e83941a98e
commit c8a1b876e8
3 changed files with 49 additions and 2 deletions
@@ -12,9 +12,12 @@ import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.typeForTypeVariable
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
import org.jetbrains.kotlin.types.TypeConstructor
abstract class ManyCandidatesResolver<D : CallableDescriptor>( abstract class ManyCandidatesResolver<D : CallableDescriptor>(
private val psiCallResolver: PSICallResolver, private val psiCallResolver: PSICallResolver,
@@ -94,11 +97,39 @@ abstract class ManyCandidatesResolver<D : CallableDescriptor>(
val allCandidates = arrayListOf<ResolutionResultCallInfo<D>>() val allCandidates = arrayListOf<ResolutionResultCallInfo<D>>()
if (hasOneSuccessfulAndOneErrorCandidate) { if (hasOneSuccessfulAndOneErrorCandidate) {
for (callInfo in resolvedCallsInfo) { val goodCandidate = resolvedCallsInfo.first { it.callResolutionResult.constraintSystem.errors.isEmpty() && it.callResolutionResult.diagnostics.isEmpty() }
val badCandidate = resolvedCallsInfo.first { it.callResolutionResult.constraintSystem.errors.isNotEmpty() || it.callResolutionResult.diagnostics.isNotEmpty()}
for (callInfo in listOf(goodCandidate, badCandidate)) {
val atomsToAnalyze = mutableListOf<ResolvedAtom>(callInfo.callResolutionResult)
val system = NewConstraintSystemImpl(callComponents.constraintInjector, builtIns).apply { val system = NewConstraintSystemImpl(callComponents.constraintInjector, builtIns).apply {
addOtherSystem(callInfo.callResolutionResult.constraintSystem) addOtherSystem(callInfo.callResolutionResult.constraintSystem)
/*
* This is needed for very stupid case, when we have some delegate with good `getValue` and bad `setValue` that
* was provided by some function call with generic (e.g. var x by lazy { "" })
* The problem is that we want to complete candidates for `getValue` and `setValue` separately, so diagnostics
* from `setValue` don't leak into resolved call of `getValue`, but both calls can have same
* atom for receiver (and type variables from it). After we complete first call, completion of
* second call fails because it's atoms don't contains type variable from receiver, because
* they was completed in first call
* To fix that we add equality constraints from first call to system of second call and create
* stub atoms in order to call completer doesn't fail
*/
if (callInfo === badCandidate) {
for ((typeVariable, fixedType) in allCandidates[0].resolutionResult.constraintSystem.fixedTypeVariables) {
if (typeVariable in this.notFixedTypeVariables) {
val type = (typeVariable as TypeConstructor).typeForTypeVariable()
addEqualityConstraint(
type,
fixedType,
SimpleConstraintSystemConstraintPosition
)
atomsToAnalyze += StubResolvedAtom(typeVariable)
}
}
}
} }
runCompletion(system, listOf(callInfo.callResolutionResult)) runCompletion(system, atomsToAnalyze)
val resolutionResult = callInfo.asCallResolutionResult(diagnosticHolder, system) val resolutionResult = callInfo.asCallResolutionResult(diagnosticHolder, system)
allCandidates += ResolutionResultCallInfo( allCandidates += ResolutionResultCallInfo(
resolutionResult, resolutionResult,
@@ -217,6 +217,13 @@ class KotlinConstraintSystemCompleter(
typeConstructor.takeIf { c.notFixedTypeVariables.containsKey(typeConstructor) } typeConstructor.takeIf { c.notFixedTypeVariables.containsKey(typeConstructor) }
} }
/*
* Hack for completing error candidates in delegate resolve
*/
if (this is StubResolvedAtom && typeVariable in c.notFixedTypeVariables) {
to += typeVariable
}
if (analyzed) { if (analyzed) {
subResolvedAtoms?.forEach { it.process(to) } subResolvedAtoms?.forEach { it.process(to) }
} }
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
@@ -247,3 +248,11 @@ class PartialCallContainer(val result: PartialCallResolutionResult?) {
val empty = PartialCallContainer(null) val empty = PartialCallContainer(null)
} }
} }
/*
* Used only for delegated properties with one good candidate and one for bad
* e.g. in case `var x by lazy { "" }
*/
class StubResolvedAtom(val typeVariable: TypeConstructor) : ResolvedAtom() {
override val atom: ResolutionAtom? get() = null
}