[NI] Make model more robust: remove diagnostics from resolution atoms
Currently there are two major phases in NI that report diagnostics: resolution parts and completion. They connected in method `KotlinCallCompleter.runCompletion` and previously diagnostics were collected in several places, from resolution atoms, partly from constraint system and partly from `CallResolutionResult`, some of them were lost. To mitigate this problem, now diagnostics are not bind to the intermediate candidate, only to the result resolution candidate (overloaded or usual one). And all diagnostics are now collected in method `runCompletion`
This commit is contained in:
+1
-2
@@ -30,7 +30,6 @@ import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
|
||||
import org.jetbrains.kotlin.resolve.calls.model.KotlinCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallAtom
|
||||
import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument
|
||||
@@ -145,7 +144,7 @@ class KotlinResolutionCallbacksImpl(
|
||||
}
|
||||
|
||||
override fun bindStubResolvedCallForCandidate(candidate: ResolvedCallAtom) {
|
||||
kotlinToResolvedCallTransformer.createStubResolvedCallAndWriteItToTrace<CallableDescriptor>(candidate, trace)
|
||||
kotlinToResolvedCallTransformer.createStubResolvedCallAndWriteItToTrace<CallableDescriptor>(candidate, trace, emptyList())
|
||||
}
|
||||
|
||||
override fun createReceiverWithSmartCastInfo(resolvedAtom: ResolvedCallAtom): ReceiverValueWithSmartCastInfo? {
|
||||
|
||||
+60
-28
@@ -77,8 +77,9 @@ class KotlinToResolvedCallTransformer(
|
||||
}
|
||||
|
||||
fun <D : CallableDescriptor> onlyTransform(
|
||||
resolvedCallAtom: ResolvedCallAtom
|
||||
): ResolvedCall<D> = transformToResolvedCall(resolvedCallAtom, null)
|
||||
resolvedCallAtom: ResolvedCallAtom,
|
||||
diagnostics: Collection<KotlinCallDiagnostic>
|
||||
): ResolvedCall<D> = transformToResolvedCall(resolvedCallAtom, null, null, diagnostics)
|
||||
|
||||
fun <D : CallableDescriptor> transformAndReport(
|
||||
baseResolvedCall: CallResolutionResult,
|
||||
@@ -95,7 +96,7 @@ class KotlinToResolvedCallTransformer(
|
||||
|
||||
context.trace.record(BindingContext.ONLY_RESOLVED_CALL, psiCall, baseResolvedCall)
|
||||
|
||||
return createStubResolvedCallAndWriteItToTrace(candidate, context.trace)
|
||||
return createStubResolvedCallAndWriteItToTrace(candidate, context.trace, baseResolvedCall.diagnostics)
|
||||
}
|
||||
CallResolutionResult.Type.ERROR, CallResolutionResult.Type.COMPLETED -> {
|
||||
val resultSubstitutor = baseResolvedCall.constraintSystem.buildResultingSubstitutor()
|
||||
@@ -107,14 +108,18 @@ class KotlinToResolvedCallTransformer(
|
||||
ktPrimitiveCompleter.completeAll(subKtPrimitive)
|
||||
}
|
||||
|
||||
return ktPrimitiveCompleter.completeResolvedCall(candidate) as ResolvedCall<D>
|
||||
return ktPrimitiveCompleter.completeResolvedCall(candidate, baseResolvedCall.diagnostics) as ResolvedCall<D>
|
||||
}
|
||||
CallResolutionResult.Type.ALL_CANDIDATES -> error("Cannot transform result for ALL_CANDIDATES mode")
|
||||
}
|
||||
}
|
||||
|
||||
fun <D : CallableDescriptor> createStubResolvedCallAndWriteItToTrace(candidate: ResolvedCallAtom, trace: BindingTrace): ResolvedCall<D> {
|
||||
val result = transformToResolvedCall<D>(candidate, trace)
|
||||
fun <D : CallableDescriptor> createStubResolvedCallAndWriteItToTrace(
|
||||
candidate: ResolvedCallAtom,
|
||||
trace: BindingTrace,
|
||||
diagnostics: Collection<KotlinCallDiagnostic>
|
||||
): ResolvedCall<D> {
|
||||
val result = transformToResolvedCall<D>(candidate, trace, null, diagnostics)
|
||||
val psiKotlinCall = candidate.atom.psiKotlinCall
|
||||
val tracing = psiKotlinCall.safeAs<PSIKotlinCallForInvoke>()?.baseCall?.tracingStrategy ?: psiKotlinCall.tracingStrategy
|
||||
|
||||
@@ -126,35 +131,38 @@ class KotlinToResolvedCallTransformer(
|
||||
fun <D : CallableDescriptor> transformToResolvedCall(
|
||||
completedCallAtom: ResolvedCallAtom,
|
||||
trace: BindingTrace?,
|
||||
resultSubstitutor: NewTypeSubstitutor? = null // if substitutor is not null, it means that this call is completed
|
||||
resultSubstitutor: NewTypeSubstitutor? = null, // if substitutor is not null, it means that this call is completed
|
||||
diagnostics: Collection<KotlinCallDiagnostic>
|
||||
): ResolvedCall<D> {
|
||||
val psiKotlinCall = completedCallAtom.atom.psiKotlinCall
|
||||
return if (psiKotlinCall is PSIKotlinCallForInvoke) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
NewVariableAsFunctionResolvedCallImpl(
|
||||
createOrGet(psiKotlinCall.variableCall.resolvedCall, trace, resultSubstitutor),
|
||||
createOrGet(completedCallAtom, trace, resultSubstitutor)
|
||||
createOrGet(psiKotlinCall.variableCall.resolvedCall, trace, resultSubstitutor, diagnostics),
|
||||
createOrGet(completedCallAtom, trace, resultSubstitutor, diagnostics)
|
||||
) as ResolvedCall<D>
|
||||
}
|
||||
else {
|
||||
createOrGet(completedCallAtom, trace, resultSubstitutor)
|
||||
createOrGet(completedCallAtom, trace, resultSubstitutor, diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <D : CallableDescriptor> createOrGet(
|
||||
completedSimpleAtom: ResolvedCallAtom,
|
||||
trace: BindingTrace?,
|
||||
resultSubstitutor: NewTypeSubstitutor?
|
||||
resultSubstitutor: NewTypeSubstitutor?,
|
||||
diagnostics: Collection<KotlinCallDiagnostic>
|
||||
): NewResolvedCallImpl<D> {
|
||||
if (trace != null) {
|
||||
val storedResolvedCall = completedSimpleAtom.atom.psiKotlinCall.psiCall.getResolvedCall(trace.bindingContext)?.
|
||||
safeAs<NewResolvedCallImpl<D>>()
|
||||
if (storedResolvedCall != null) {
|
||||
storedResolvedCall.setResultingSubstitutor(resultSubstitutor)
|
||||
storedResolvedCall.updateDiagnostics(diagnostics)
|
||||
return storedResolvedCall
|
||||
}
|
||||
}
|
||||
return NewResolvedCallImpl(completedSimpleAtom, resultSubstitutor)
|
||||
return NewResolvedCallImpl(completedSimpleAtom, resultSubstitutor, diagnostics)
|
||||
}
|
||||
|
||||
fun runCallCheckers(resolvedCall: ResolvedCall<*>, callCheckerContext: CallCheckerContext) {
|
||||
@@ -289,44 +297,63 @@ class KotlinToResolvedCallTransformer(
|
||||
return expressionType != null && TypeUtils.isNullableType(expressionType)
|
||||
}
|
||||
|
||||
internal fun bindAndReport(context: BasicCallResolutionContext, trace: BindingTrace, resolvedCall: ResolvedCall<*>) {
|
||||
resolvedCall.safeAs<NewResolvedCallImpl<*>>()?.let { bindAndReport(context, trace, it) }
|
||||
resolvedCall.safeAs<NewVariableAsFunctionResolvedCallImpl>()?.let { bindAndReport(context, trace, it) }
|
||||
internal fun bindAndReport(
|
||||
context: BasicCallResolutionContext,
|
||||
trace: BindingTrace,
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
diagnostics: Collection<KotlinCallDiagnostic>
|
||||
) {
|
||||
resolvedCall.safeAs<NewResolvedCallImpl<*>>()?.let { bindAndReport(context, trace, it, diagnostics) }
|
||||
resolvedCall.safeAs<NewVariableAsFunctionResolvedCallImpl>()?.let { bindAndReport(context, trace, it, diagnostics) }
|
||||
}
|
||||
|
||||
private fun bindAndReport(context: BasicCallResolutionContext, trace: BindingTrace, simpleResolvedCall: NewResolvedCallImpl<*>) {
|
||||
private fun bindAndReport(
|
||||
context: BasicCallResolutionContext,
|
||||
trace: BindingTrace,
|
||||
simpleResolvedCall: NewResolvedCallImpl<*>,
|
||||
diagnostics: Collection<KotlinCallDiagnostic>
|
||||
) {
|
||||
val tracing = simpleResolvedCall.resolvedCallAtom.atom.psiKotlinCall.tracingStrategy
|
||||
|
||||
tracing.bindReference(trace, simpleResolvedCall)
|
||||
tracing.bindResolvedCall(trace, simpleResolvedCall)
|
||||
|
||||
reportCallDiagnostic(context, trace, simpleResolvedCall.resolvedCallAtom, simpleResolvedCall.resultingDescriptor)
|
||||
reportCallDiagnostic(context, trace, simpleResolvedCall.resolvedCallAtom, simpleResolvedCall.resultingDescriptor, diagnostics)
|
||||
}
|
||||
|
||||
private fun bindAndReport(context: BasicCallResolutionContext, trace: BindingTrace, variableAsFunction: NewVariableAsFunctionResolvedCallImpl) {
|
||||
private fun bindAndReport(
|
||||
context: BasicCallResolutionContext,
|
||||
trace: BindingTrace,
|
||||
variableAsFunction: NewVariableAsFunctionResolvedCallImpl,
|
||||
diagnostics: Collection<KotlinCallDiagnostic>
|
||||
) {
|
||||
val outerTracingStrategy = variableAsFunction.baseCall.tracingStrategy
|
||||
outerTracingStrategy.bindReference(trace, variableAsFunction.variableCall)
|
||||
outerTracingStrategy.bindResolvedCall(trace, variableAsFunction)
|
||||
variableAsFunction.functionCall.kotlinCall.psiKotlinCall.tracingStrategy.bindReference(trace, variableAsFunction.functionCall)
|
||||
val variableCall = variableAsFunction.variableCall
|
||||
val functionCall = variableAsFunction.functionCall
|
||||
|
||||
reportCallDiagnostic(context, trace, variableAsFunction.variableCall.resolvedCallAtom, variableAsFunction.variableCall.resultingDescriptor)
|
||||
reportCallDiagnostic(context, trace, variableAsFunction.functionCall.resolvedCallAtom, variableAsFunction.functionCall.resultingDescriptor)
|
||||
outerTracingStrategy.bindReference(trace, variableCall)
|
||||
outerTracingStrategy.bindResolvedCall(trace, variableAsFunction)
|
||||
functionCall.kotlinCall.psiKotlinCall.tracingStrategy.bindReference(trace, functionCall)
|
||||
|
||||
reportCallDiagnostic(context, trace, variableCall.resolvedCallAtom, variableCall.resultingDescriptor, diagnostics)
|
||||
reportCallDiagnostic(context, trace, functionCall.resolvedCallAtom, functionCall.resultingDescriptor, emptyList())
|
||||
}
|
||||
|
||||
private fun reportCallDiagnostic(
|
||||
context: BasicCallResolutionContext,
|
||||
trace: BindingTrace,
|
||||
completedCallAtom: ResolvedCallAtom,
|
||||
resultingDescriptor: CallableDescriptor
|
||||
resultingDescriptor: CallableDescriptor,
|
||||
diagnostics: Collection<KotlinCallDiagnostic>
|
||||
) {
|
||||
val trackingTrace = TrackingBindingTrace(trace)
|
||||
val newContext = context.replaceBindingTrace(trackingTrace)
|
||||
val diagnosticReporter = DiagnosticReporterByTrackingStrategy(constantExpressionEvaluator, newContext, completedCallAtom.atom.psiKotlinCall)
|
||||
|
||||
val diagnosticHolder = KotlinDiagnosticsHolder.SimpleHolder()
|
||||
additionalDiagnosticReporter.reportAdditionalDiagnostics(completedCallAtom, resultingDescriptor, diagnosticHolder)
|
||||
additionalDiagnosticReporter.reportAdditionalDiagnostics(completedCallAtom, resultingDescriptor, diagnosticHolder, diagnostics)
|
||||
|
||||
for (diagnostic in completedCallAtom.diagnostics + diagnosticHolder.getDiagnostics()) {
|
||||
for (diagnostic in diagnostics + diagnosticHolder.getDiagnostics()) {
|
||||
trackingTrace.reported = false
|
||||
diagnostic.report(diagnosticReporter)
|
||||
|
||||
@@ -445,7 +472,8 @@ sealed class NewAbstractResolvedCall<D : CallableDescriptor>(): ResolvedCall<D>
|
||||
|
||||
class NewResolvedCallImpl<D : CallableDescriptor>(
|
||||
val resolvedCallAtom: ResolvedCallAtom,
|
||||
substitutor: NewTypeSubstitutor?
|
||||
substitutor: NewTypeSubstitutor?,
|
||||
private var diagnostics: Collection<KotlinCallDiagnostic>
|
||||
): NewAbstractResolvedCall<D>() {
|
||||
var isCompleted = false
|
||||
private set
|
||||
@@ -458,7 +486,7 @@ class NewResolvedCallImpl<D : CallableDescriptor>(
|
||||
|
||||
override val kotlinCall: KotlinCall get() = resolvedCallAtom.atom
|
||||
|
||||
override fun getStatus(): ResolutionStatus = getResultApplicability(resolvedCallAtom.diagnostics).toResolutionStatus()
|
||||
override fun getStatus(): ResolutionStatus = getResultApplicability(diagnostics).toResolutionStatus()
|
||||
|
||||
override val argumentMappingByOriginal: Map<ValueParameterDescriptor, ResolvedCallArgument>
|
||||
get() = resolvedCallAtom.argumentMappingByOriginal
|
||||
@@ -489,6 +517,10 @@ class NewResolvedCallImpl<D : CallableDescriptor>(
|
||||
this.smartCastDispatchReceiverType = smartCastDispatchReceiverType
|
||||
}
|
||||
|
||||
fun updateDiagnostics(completedDiagnostics: Collection<KotlinCallDiagnostic>) {
|
||||
diagnostics = completedDiagnostics
|
||||
}
|
||||
|
||||
fun setResultingSubstitutor(substitutor: NewTypeSubstitutor?) {
|
||||
//clear cached values
|
||||
argumentToParameterMap = null
|
||||
|
||||
@@ -179,7 +179,7 @@ class PSICallResolver(
|
||||
if (result.type == CallResolutionResult.Type.ALL_CANDIDATES) {
|
||||
val resolvedCalls = result.allCandidates?.map {
|
||||
val resultingSubstitutor = it.getSystem().asReadOnlyStorage().buildResultingSubstitutor()
|
||||
kotlinToResolvedCallTransformer.transformToResolvedCall<D>(it.resolvedCall, null, resultingSubstitutor)
|
||||
kotlinToResolvedCallTransformer.transformToResolvedCall<D>(it.resolvedCall, null, resultingSubstitutor, result.diagnostics)
|
||||
}
|
||||
|
||||
return AllCandidates(resolvedCalls ?: emptyList())
|
||||
@@ -193,7 +193,7 @@ class PSICallResolver(
|
||||
}
|
||||
|
||||
result.diagnostics.firstIsInstanceOrNull<ManyCandidatesCallDiagnostic>()?.let {
|
||||
val resolvedCalls = it.candidates.map { kotlinToResolvedCallTransformer.onlyTransform<D>(it.resolvedCall) }
|
||||
val resolvedCalls = it.candidates.map { kotlinToResolvedCallTransformer.onlyTransform<D>(it.resolvedCall, emptyList()) }
|
||||
if (it.candidates.areAllFailed()) {
|
||||
tracingStrategy.noneApplicable(trace, resolvedCalls)
|
||||
tracingStrategy.recordAmbiguity(trace, resolvedCalls)
|
||||
@@ -210,11 +210,11 @@ class PSICallResolver(
|
||||
return ManyCandidates(resolvedCalls)
|
||||
}
|
||||
|
||||
val singleCandidate = result.resultCallAtom ?: error("Should be not null for result: $result")
|
||||
val isInapplicableReceiver = getResultApplicability(singleCandidate.diagnostics) == ResolutionCandidateApplicability.INAPPLICABLE_WRONG_RECEIVER
|
||||
val isInapplicableReceiver = getResultApplicability(result.diagnostics) == ResolutionCandidateApplicability.INAPPLICABLE_WRONG_RECEIVER
|
||||
|
||||
val resolvedCall = if (isInapplicableReceiver) {
|
||||
kotlinToResolvedCallTransformer.onlyTransform<D>(singleCandidate).also {
|
||||
val singleCandidate = result.resultCallAtom ?: error("Should be not null for result: $result")
|
||||
kotlinToResolvedCallTransformer.onlyTransform<D>(singleCandidate, result.diagnostics).also {
|
||||
tracingStrategy.unresolvedReferenceWrongReceiver(trace, listOf(it))
|
||||
}
|
||||
}
|
||||
@@ -233,15 +233,15 @@ class PSICallResolver(
|
||||
}
|
||||
|
||||
private fun CallResolutionResult.areAllInapplicable(): Boolean {
|
||||
val candidates = diagnostics.firstIsInstanceOrNull<ManyCandidatesCallDiagnostic>()?.candidates?.map { it.resolvedCall }
|
||||
?: listOfNotNull(resultCallAtom)
|
||||
|
||||
return candidates.all {
|
||||
val applicability = getResultApplicability(it.diagnostics)
|
||||
applicability == ResolutionCandidateApplicability.INAPPLICABLE ||
|
||||
applicability == ResolutionCandidateApplicability.INAPPLICABLE_WRONG_RECEIVER ||
|
||||
applicability == ResolutionCandidateApplicability.HIDDEN
|
||||
val manyCandidates = diagnostics.firstIsInstanceOrNull<ManyCandidatesCallDiagnostic>()?.candidates
|
||||
if (manyCandidates != null) {
|
||||
return manyCandidates.areAllFailed()
|
||||
}
|
||||
|
||||
val applicability = getResultApplicability(diagnostics)
|
||||
return applicability == ResolutionCandidateApplicability.INAPPLICABLE ||
|
||||
applicability == ResolutionCandidateApplicability.INAPPLICABLE_WRONG_RECEIVER ||
|
||||
applicability == ResolutionCandidateApplicability.HIDDEN
|
||||
}
|
||||
|
||||
// true if we found something
|
||||
|
||||
+6
-6
@@ -60,12 +60,12 @@ class ResolvedAtomCompleter(
|
||||
) {
|
||||
private val callCheckerContext = CallCheckerContext(topLevelCallContext, languageVersionSettings, deprecationResolver)
|
||||
|
||||
fun completeAndReport(resolvedAtom: ResolvedAtom) {
|
||||
private fun complete(resolvedAtom: ResolvedAtom) {
|
||||
when (resolvedAtom) {
|
||||
is ResolvedCollectionLiteralAtom -> completeCollectionLiteralCalls(resolvedAtom)
|
||||
is ResolvedCallableReferenceAtom -> completeCallableReference(resolvedAtom)
|
||||
is ResolvedLambdaAtom -> completeLambda(resolvedAtom)
|
||||
is ResolvedCallAtom -> completeResolvedCall(resolvedAtom)
|
||||
is ResolvedCallAtom -> completeResolvedCall(resolvedAtom, emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,14 +73,14 @@ class ResolvedAtomCompleter(
|
||||
for (subKtPrimitive in resolvedAtom.subResolvedAtoms) {
|
||||
completeAll(subKtPrimitive)
|
||||
}
|
||||
completeAndReport(resolvedAtom)
|
||||
complete(resolvedAtom)
|
||||
}
|
||||
|
||||
fun completeResolvedCall(resolvedCallAtom: ResolvedCallAtom): ResolvedCall<*>? {
|
||||
fun completeResolvedCall(resolvedCallAtom: ResolvedCallAtom, diagnostics: Collection<KotlinCallDiagnostic>): ResolvedCall<*>? {
|
||||
if (resolvedCallAtom.atom.psiKotlinCall is PSIKotlinCallForVariable) return null
|
||||
|
||||
val resolvedCall = kotlinToResolvedCallTransformer.transformToResolvedCall<CallableDescriptor>(resolvedCallAtom, trace, resultSubstitutor)
|
||||
kotlinToResolvedCallTransformer.bindAndReport(topLevelCallContext, trace, resolvedCall)
|
||||
val resolvedCall = kotlinToResolvedCallTransformer.transformToResolvedCall<CallableDescriptor>(resolvedCallAtom, trace, resultSubstitutor, diagnostics)
|
||||
kotlinToResolvedCallTransformer.bindAndReport(topLevelCallContext, trace, resolvedCall, diagnostics)
|
||||
kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, callCheckerContext)
|
||||
|
||||
val lastCall = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.functionCall else resolvedCall
|
||||
|
||||
Reference in New Issue
Block a user