[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:
Mikhail Zarechenskiy
2017-11-14 18:21:34 +03:00
parent 6e51c4d5db
commit e526a87d54
10 changed files with 156 additions and 99 deletions
@@ -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? {
@@ -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
@@ -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
@@ -31,9 +31,10 @@ class AdditionalDiagnosticReporter(private val languageVersionSettings: Language
fun reportAdditionalDiagnostics(
candidate: ResolvedCallAtom,
resultingDescriptor: CallableDescriptor,
kotlinDiagnosticsHolder: KotlinDiagnosticsHolder
kotlinDiagnosticsHolder: KotlinDiagnosticsHolder,
diagnostics: Collection<KotlinCallDiagnostic>
) {
reportSmartCasts(candidate, resultingDescriptor, kotlinDiagnosticsHolder)
reportSmartCasts(candidate, resultingDescriptor, kotlinDiagnosticsHolder, diagnostics)
}
private fun createSmartCastDiagnostic(
@@ -51,7 +52,8 @@ class AdditionalDiagnosticReporter(private val languageVersionSettings: Language
private fun reportSmartCastOnReceiver(
candidate: ResolvedCallAtom,
receiver: SimpleKotlinCallArgument?,
parameter: ReceiverParameterDescriptor?
parameter: ReceiverParameterDescriptor?,
diagnostics: Collection<KotlinCallDiagnostic>
): SmartCastDiagnostic? {
if (receiver == null || parameter == null) return null
val expectedType = parameter.type.unwrap().let { if (receiver.isSafeCall) it.makeNullableAsSpecified(true) else it }
@@ -60,11 +62,11 @@ class AdditionalDiagnosticReporter(private val languageVersionSettings: Language
// todo may be we have smart cast to Int?
return smartCastDiagnostic.takeIf {
candidate.diagnostics.filterIsInstance<UnsafeCallError>().none {
diagnostics.filterIsInstance<UnsafeCallError>().none {
it.receiver == receiver
}
&&
candidate.diagnostics.filterIsInstance<UnstableSmartCast>().none {
diagnostics.filterIsInstance<UnstableSmartCast>().none {
it.argument == receiver
}
}
@@ -73,17 +75,22 @@ class AdditionalDiagnosticReporter(private val languageVersionSettings: Language
private fun reportSmartCasts(
candidate: ResolvedCallAtom,
resultingDescriptor: CallableDescriptor,
kotlinDiagnosticsHolder: KotlinDiagnosticsHolder
kotlinDiagnosticsHolder: KotlinDiagnosticsHolder,
diagnostics: Collection<KotlinCallDiagnostic>
) {
kotlinDiagnosticsHolder.addDiagnosticIfNotNull(reportSmartCastOnReceiver(candidate, candidate.extensionReceiverArgument, resultingDescriptor.extensionReceiverParameter))
kotlinDiagnosticsHolder.addDiagnosticIfNotNull(reportSmartCastOnReceiver(candidate, candidate.dispatchReceiverArgument, resultingDescriptor.dispatchReceiverParameter))
kotlinDiagnosticsHolder.addDiagnosticIfNotNull(
reportSmartCastOnReceiver(candidate, candidate.extensionReceiverArgument, resultingDescriptor.extensionReceiverParameter, diagnostics)
)
kotlinDiagnosticsHolder.addDiagnosticIfNotNull(
reportSmartCastOnReceiver(candidate, candidate.dispatchReceiverArgument, resultingDescriptor.dispatchReceiverParameter, diagnostics)
)
for (parameter in resultingDescriptor.valueParameters) {
for (argument in candidate.argumentMappingByOriginal[parameter.original]?.arguments ?: continue) {
val effectiveExpectedType = argument.getExpectedType(parameter, languageVersionSettings)
val smartCastDiagnostic = createSmartCastDiagnostic(candidate, argument, effectiveExpectedType) ?: continue
val thereIsUnstableSmartCastError = candidate.diagnostics.filterIsInstance<UnstableSmartCast>().any {
val thereIsUnstableSmartCastError = diagnostics.filterIsInstance<UnstableSmartCast>().any {
it.argument == argument
}
@@ -29,8 +29,6 @@ import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
import org.jetbrains.kotlin.resolve.calls.tower.ImplicitScopeTower
import org.jetbrains.kotlin.resolve.calls.tower.TowerResolver
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
class CallableReferenceOverloadConflictResolver(
@@ -62,7 +60,8 @@ class CallableReferenceResolver(
fun processCallableReferenceArgument(
csBuilder: ConstraintSystemBuilder,
resolvedAtom: ResolvedCallableReferenceAtom
resolvedAtom: ResolvedCallableReferenceAtom,
diagnosticsHolder: KotlinDiagnosticsHolder
) {
val argument = resolvedAtom.atom
val expectedType = resolvedAtom.expectedType?.let { csBuilder.buildCurrentSubstitutor().safeSubstitute(it) }
@@ -71,30 +70,28 @@ class CallableReferenceResolver(
val candidates = runRHSResolution(scopeTower, argument, expectedType) { checkCallableReference ->
csBuilder.runTransaction { checkCallableReference(this); false }
}
val diagnostics = SmartList<KotlinCallDiagnostic>()
val chosenCandidate = candidates.singleOrNull()
if (chosenCandidate != null) {
val (toFreshSubstitutor, diagnostic) = with(chosenCandidate) {
csBuilder.checkCallableReference(argument, dispatchReceiver, extensionReceiver, candidate,
reflectionCandidateType, expectedType, scopeTower.lexicalScope.ownerDescriptor)
}
diagnostics.addIfNotNull(diagnostic)
diagnosticsHolder.addDiagnosticIfNotNull(diagnostic)
chosenCandidate.freshSubstitutor = toFreshSubstitutor
}
else {
if (candidates.isEmpty()) {
diagnostics.add(NoneCallableReferenceCandidates(argument))
diagnosticsHolder.addDiagnostic(NoneCallableReferenceCandidates(argument))
}
else {
diagnostics.add(CallableReferenceCandidatesAmbiguity(argument, candidates))
diagnosticsHolder.addDiagnostic(CallableReferenceCandidatesAmbiguity(argument, candidates))
}
}
// todo -- create this inside CallableReferencesCandidateFactory
val subKtArguments = listOfNotNull(buildResolvedKtArgument(argument.lhsResult))
resolvedAtom.setAnalyzedResults(chosenCandidate, subKtArguments, diagnostics)
resolvedAtom.setAnalyzedResults(chosenCandidate, subKtArguments)
}
private fun buildResolvedKtArgument(lhsResult: LHSResult): ResolvedAtom? {
@@ -49,13 +49,20 @@ class KotlinCallCompleter(
// this is needed at least for non-local return checker, because when we analyze lambda we should already bind descriptor for outer call
candidate?.resolvedCall?.let { resolutionCallbacks.bindStubResolvedCallForCandidate(it) }
val diagnosticsFromResolutionParts = candidate?.diagnosticsFromResolutionParts ?: emptyList<KotlinCallDiagnostic>()
if (candidate == null || candidate.csBuilder.hasContradiction) {
val candidateForCompletion = candidate ?: factory.createErrorCandidate().forceResolution()
candidateForCompletion.prepareForCompletion(expectedType, resolutionCallbacks)
runCompletion(candidateForCompletion.resolvedCall, ConstraintSystemCompletionMode.FULL, diagnosticHolder, candidateForCompletion.getSystem(), resolutionCallbacks)
val systemStorage = candidate?.getSystem()?.asReadOnlyStorage() ?: ConstraintStorage.Empty
return CallResolutionResult(CallResolutionResult.Type.ERROR, candidate?.resolvedCall, diagnosticHolder.getDiagnostics(), systemStorage)
return CallResolutionResult(
CallResolutionResult.Type.ERROR,
candidate?.resolvedCall,
diagnosticHolder.getDiagnostics() + diagnosticsFromResolutionParts,
systemStorage
)
}
val completionType = candidate.prepareForCompletion(expectedType, resolutionCallbacks)
@@ -63,10 +70,20 @@ class KotlinCallCompleter(
runCompletion(candidate.resolvedCall, completionType, diagnosticHolder, constraintSystem, resolutionCallbacks)
return if (completionType == ConstraintSystemCompletionMode.FULL) {
CallResolutionResult(CallResolutionResult.Type.COMPLETED, candidate.resolvedCall, diagnosticHolder.getDiagnostics(), constraintSystem.asReadOnlyStorage())
CallResolutionResult(
CallResolutionResult.Type.COMPLETED,
candidate.resolvedCall,
diagnosticHolder.getDiagnostics() + diagnosticsFromResolutionParts,
constraintSystem.asReadOnlyStorage()
)
}
else {
CallResolutionResult(CallResolutionResult.Type.PARTIAL, candidate.resolvedCall, diagnosticHolder.getDiagnostics(), constraintSystem.asReadOnlyStorage())
CallResolutionResult(
CallResolutionResult.Type.PARTIAL,
candidate.resolvedCall,
diagnosticHolder.getDiagnostics() + diagnosticsFromResolutionParts,
constraintSystem.asReadOnlyStorage()
)
}
}
@@ -100,7 +117,12 @@ class KotlinCallCompleter(
val returnType = resolvedCallAtom.freshReturnType ?: constraintSystem.builtIns.unitType
kotlinConstraintSystemCompleter.runCompletion(constraintSystem.asConstraintSystemCompleterContext(), completionMode, resolvedCallAtom, returnType) {
if (!skipPostponedArguments) {
postponedArgumentsAnalyzer.analyze(constraintSystem.asPostponedArgumentsAnalyzerContext(), resolutionCallbacks, it)
postponedArgumentsAnalyzer.analyze(
constraintSystem.asPostponedArgumentsAnalyzerContext(),
resolutionCallbacks,
it,
diagnosticsHolder
)
}
}
@@ -39,17 +39,24 @@ class PostponedArgumentsAnalyzer(
fun getBuilder(): ConstraintSystemBuilder
}
fun analyze(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, argument: ResolvedAtom) {
fun analyze(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, argument: ResolvedAtom, diagnosticsHolder: KotlinDiagnosticsHolder) {
when (argument) {
is ResolvedLambdaAtom -> analyzeLambda(c, resolutionCallbacks, argument)
is LambdaWithTypeVariableAsExpectedTypeAtom -> analyzeLambda(c, resolutionCallbacks, argument.transformToResolvedLambda(c.getBuilder()))
is ResolvedCallableReferenceAtom -> callableReferenceResolver.processCallableReferenceArgument(c.getBuilder(), argument)
is ResolvedLambdaAtom ->
analyzeLambda(c, resolutionCallbacks, argument, diagnosticsHolder)
is LambdaWithTypeVariableAsExpectedTypeAtom ->
analyzeLambda(c, resolutionCallbacks, argument.transformToResolvedLambda(c.getBuilder()), diagnosticsHolder)
is ResolvedCallableReferenceAtom ->
callableReferenceResolver.processCallableReferenceArgument(c.getBuilder(), argument, diagnosticsHolder)
is ResolvedCollectionLiteralAtom -> TODO("Not supported")
else -> error("Unexpected resolved primitive: ${argument.javaClass.canonicalName}")
}
}
private fun analyzeLambda(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, lambda: ResolvedLambdaAtom) {
private fun analyzeLambda(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, lambda: ResolvedLambdaAtom, diagnosticHolder: KotlinDiagnosticsHolder) {
val currentSubstitutor = c.buildCurrentSubstitutor()
fun substitute(type: UnwrappedType) = currentSubstitutor.safeSubstitute(type)
@@ -61,8 +68,6 @@ class PostponedArgumentsAnalyzer(
resultArguments.forEach { c.addSubsystemForArgument(it) }
val diagnosticHolder = KotlinDiagnosticsHolder.SimpleHolder()
val subResolvedKtPrimitives = resultArguments.map {
checkSimpleArgument(c.getBuilder(), it, lambda.returnType.let(::substitute), diagnosticHolder, isReceiver = false)
}
@@ -72,6 +77,6 @@ class PostponedArgumentsAnalyzer(
c.getBuilder().addSubtypeConstraint(lambda.returnType.let(::substitute), unitType, LambdaArgumentConstraintPosition(lambda))
}
lambda.setAnalyzedResults(resultArguments, subResolvedKtPrimitives, diagnosticHolder.getDiagnostics())
lambda.setAnalyzedResults(resultArguments, subResolvedKtPrimitives)
}
}
@@ -46,10 +46,8 @@ sealed class ResolvedAtom {
lateinit var subResolvedAtoms: List<ResolvedAtom>
private set
lateinit var diagnostics: Collection<KotlinCallDiagnostic>
private set
protected open fun setAnalyzedResults(subResolvedAtoms: List<ResolvedAtom>, diagnostics: Collection<KotlinCallDiagnostic>) {
protected open fun setAnalyzedResults(subResolvedAtoms: List<ResolvedAtom>) {
assert(!analyzed) {
"Already analyzed: $this"
}
@@ -57,7 +55,6 @@ sealed class ResolvedAtom {
analyzed = true
this.subResolvedAtoms = subResolvedAtoms
this.diagnostics = diagnostics
}
}
@@ -74,7 +71,7 @@ abstract class ResolvedCallAtom : ResolvedAtom() {
class ResolvedExpressionAtom(override val atom: ExpressionKotlinCallArgument) : ResolvedAtom() {
init {
setAnalyzedResults(listOf(), listOf())
setAnalyzedResults(listOf())
}
}
sealed class PostponedResolvedAtom : ResolvedAtom() {
@@ -90,7 +87,7 @@ class LambdaWithTypeVariableAsExpectedTypeAtom(
override val outputType: UnwrappedType? get() = null
fun setAnalyzed(resolvedLambdaAtom: ResolvedLambdaAtom) {
setAnalyzedResults(listOf(resolvedLambdaAtom), listOf())
setAnalyzedResults(listOf(resolvedLambdaAtom))
}
}
@@ -107,11 +104,10 @@ class ResolvedLambdaAtom(
fun setAnalyzedResults(
resultArguments: List<KotlinCallArgument>,
subResolvedAtoms: List<ResolvedAtom>,
diagnostics: Collection<KotlinCallDiagnostic>
subResolvedAtoms: List<ResolvedAtom>
) {
this.resultArguments = resultArguments
setAnalyzedResults(subResolvedAtoms, diagnostics)
setAnalyzedResults(subResolvedAtoms)
}
override val inputTypes: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
@@ -127,11 +123,10 @@ class ResolvedCallableReferenceAtom(
fun setAnalyzedResults(
candidate: CallableReferenceCandidate?,
subResolvedAtoms: List<ResolvedAtom>,
diagnostics: Collection<KotlinCallDiagnostic>
subResolvedAtoms: List<ResolvedAtom>
) {
this.candidate = candidate
setAnalyzedResults(subResolvedAtoms, diagnostics)
setAnalyzedResults(subResolvedAtoms)
}
override val inputTypes: Collection<UnwrappedType>
@@ -154,14 +149,14 @@ class ResolvedCollectionLiteralAtom(
val expectedType: UnwrappedType?
) : ResolvedAtom() {
init {
setAnalyzedResults(listOf(), listOf())
setAnalyzedResults(listOf())
}
}
class CallResolutionResult(
val type: Type,
val resultCallAtom: ResolvedCallAtom?,
diagnostics: List<KotlinCallDiagnostic>,
val diagnostics: List<KotlinCallDiagnostic>,
val constraintSystem: ConstraintStorage,
val allCandidates: Collection<KotlinResolutionCandidate>? = null
) : ResolvedAtom() {
@@ -175,7 +170,7 @@ class CallResolutionResult(
}
init {
setAnalyzedResults(listOfNotNull(resultCallAtom), diagnostics)
setAnalyzedResults(listOfNotNull(resultCallAtom))
}
override fun toString() = "$type, resultCallAtom = $resultCallAtom, (${diagnostics.joinToString()})"
@@ -68,8 +68,8 @@ class KotlinResolutionCandidate(
val knownTypeParametersResultingSubstitutor: TypeSubstitutor? = null,
private val resolutionSequence: List<ResolutionPart> = resolvedCall.atom.callKind.resolutionSequence
) : Candidate, KotlinDiagnosticsHolder {
val diagnosticsFromResolutionParts = arrayListOf<KotlinCallDiagnostic>() // TODO: this is mutable list, take diagnostics only once!
private var newSystem: NewConstraintSystemImpl? = null
private val diagnostics = arrayListOf<KotlinCallDiagnostic>()
private var currentApplicability = ResolutionCandidateApplicability.RESOLVED
private var subResolvedAtoms: MutableList<ResolvedAtom> = arrayListOf()
@@ -87,7 +87,7 @@ class KotlinResolutionCandidate(
internal val csBuilder get() = getSystem().getBuilder()
override fun addDiagnostic(diagnostic: KotlinCallDiagnostic) {
diagnostics.add(diagnostic)
diagnosticsFromResolutionParts.add(diagnostic)
currentApplicability = maxOf(diagnostic.candidateApplicability, currentApplicability)
}
@@ -121,7 +121,7 @@ class KotlinResolutionCandidate(
partIndex++
}
if (step == stepCount) {
resolvedCall.setAnalyzedResults(subResolvedAtoms, diagnostics + getSystem().diagnostics)
resolvedCall.setAnalyzedResults(subResolvedAtoms)
}
}
@@ -176,8 +176,8 @@ class MutableResolvedCallAtom(
override lateinit var substitutor: FreshVariableNewTypeSubstitutor
lateinit var argumentToCandidateParameter: Map<KotlinCallArgument, ValueParameterDescriptor>
override public fun setAnalyzedResults(subResolvedAtoms: List<ResolvedAtom>, diagnostics: Collection<KotlinCallDiagnostic>) {
super.setAnalyzedResults(subResolvedAtoms, diagnostics)
override public fun setAnalyzedResults(subResolvedAtoms: List<ResolvedAtom>) {
super.setAnalyzedResults(subResolvedAtoms)
}
override fun toString(): String = "$atom, candidate = $candidateDescriptor"