From e526a87d54ceeb267506ba31eb4fbbcf33c71777 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 14 Nov 2017 18:21:34 +0300 Subject: [PATCH] [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` --- .../tower/KotlinResolutionCallbacksImpl.kt | 3 +- .../tower/KotlinToResolvedCallTransformer.kt | 88 +++++++++++++------ .../resolve/calls/tower/PSICallResolver.kt | 26 +++--- .../calls/tower/ResolvedAtomCompleter.kt | 12 +-- .../AdditionalDiagnosticReporter.kt | 25 ++++-- .../components/CallableReferenceResolver.kt | 15 ++-- .../calls/components/KotlinCallCompleter.kt | 30 ++++++- .../components/PostponedArgumentsAnalyzer.kt | 21 +++-- .../resolve/calls/model/ResolutionAtoms.kt | 25 +++--- .../calls/model/ResolutionCandidate.kt | 10 +-- 10 files changed, 156 insertions(+), 99 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt index 54b52c1718e..f14664501f8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt @@ -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(candidate, trace) + kotlinToResolvedCallTransformer.createStubResolvedCallAndWriteItToTrace(candidate, trace, emptyList()) } override fun createReceiverWithSmartCastInfo(resolvedAtom: ResolvedCallAtom): ReceiverValueWithSmartCastInfo? { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt index bdef9894c2e..b76ace907c2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt @@ -77,8 +77,9 @@ class KotlinToResolvedCallTransformer( } fun onlyTransform( - resolvedCallAtom: ResolvedCallAtom - ): ResolvedCall = transformToResolvedCall(resolvedCallAtom, null) + resolvedCallAtom: ResolvedCallAtom, + diagnostics: Collection + ): ResolvedCall = transformToResolvedCall(resolvedCallAtom, null, null, diagnostics) fun 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 + return ktPrimitiveCompleter.completeResolvedCall(candidate, baseResolvedCall.diagnostics) as ResolvedCall } CallResolutionResult.Type.ALL_CANDIDATES -> error("Cannot transform result for ALL_CANDIDATES mode") } } - fun createStubResolvedCallAndWriteItToTrace(candidate: ResolvedCallAtom, trace: BindingTrace): ResolvedCall { - val result = transformToResolvedCall(candidate, trace) + fun createStubResolvedCallAndWriteItToTrace( + candidate: ResolvedCallAtom, + trace: BindingTrace, + diagnostics: Collection + ): ResolvedCall { + val result = transformToResolvedCall(candidate, trace, null, diagnostics) val psiKotlinCall = candidate.atom.psiKotlinCall val tracing = psiKotlinCall.safeAs()?.baseCall?.tracingStrategy ?: psiKotlinCall.tracingStrategy @@ -126,35 +131,38 @@ class KotlinToResolvedCallTransformer( fun 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 ): ResolvedCall { 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 } else { - createOrGet(completedCallAtom, trace, resultSubstitutor) + createOrGet(completedCallAtom, trace, resultSubstitutor, diagnostics) } } private fun createOrGet( completedSimpleAtom: ResolvedCallAtom, trace: BindingTrace?, - resultSubstitutor: NewTypeSubstitutor? + resultSubstitutor: NewTypeSubstitutor?, + diagnostics: Collection ): NewResolvedCallImpl { if (trace != null) { val storedResolvedCall = completedSimpleAtom.atom.psiKotlinCall.psiCall.getResolvedCall(trace.bindingContext)?. safeAs>() 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>()?.let { bindAndReport(context, trace, it) } - resolvedCall.safeAs()?.let { bindAndReport(context, trace, it) } + internal fun bindAndReport( + context: BasicCallResolutionContext, + trace: BindingTrace, + resolvedCall: ResolvedCall<*>, + diagnostics: Collection + ) { + resolvedCall.safeAs>()?.let { bindAndReport(context, trace, it, diagnostics) } + resolvedCall.safeAs()?.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 + ) { 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 + ) { 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 ) { 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(): ResolvedCall class NewResolvedCallImpl( val resolvedCallAtom: ResolvedCallAtom, - substitutor: NewTypeSubstitutor? + substitutor: NewTypeSubstitutor?, + private var diagnostics: Collection ): NewAbstractResolvedCall() { var isCompleted = false private set @@ -458,7 +486,7 @@ class NewResolvedCallImpl( 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 get() = resolvedCallAtom.argumentMappingByOriginal @@ -489,6 +517,10 @@ class NewResolvedCallImpl( this.smartCastDispatchReceiverType = smartCastDispatchReceiverType } + fun updateDiagnostics(completedDiagnostics: Collection) { + diagnostics = completedDiagnostics + } + fun setResultingSubstitutor(substitutor: NewTypeSubstitutor?) { //clear cached values argumentToParameterMap = null diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt index 4b79c28a735..b4e086d9bd1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt @@ -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(it.resolvedCall, null, resultingSubstitutor) + kotlinToResolvedCallTransformer.transformToResolvedCall(it.resolvedCall, null, resultingSubstitutor, result.diagnostics) } return AllCandidates(resolvedCalls ?: emptyList()) @@ -193,7 +193,7 @@ class PSICallResolver( } result.diagnostics.firstIsInstanceOrNull()?.let { - val resolvedCalls = it.candidates.map { kotlinToResolvedCallTransformer.onlyTransform(it.resolvedCall) } + val resolvedCalls = it.candidates.map { kotlinToResolvedCallTransformer.onlyTransform(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(singleCandidate).also { + val singleCandidate = result.resultCallAtom ?: error("Should be not null for result: $result") + kotlinToResolvedCallTransformer.onlyTransform(singleCandidate, result.diagnostics).also { tracingStrategy.unresolvedReferenceWrongReceiver(trace, listOf(it)) } } @@ -233,15 +233,15 @@ class PSICallResolver( } private fun CallResolutionResult.areAllInapplicable(): Boolean { - val candidates = diagnostics.firstIsInstanceOrNull()?.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()?.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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt index ca9214f81f5..4754c603e4a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt @@ -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): ResolvedCall<*>? { if (resolvedCallAtom.atom.psiKotlinCall is PSIKotlinCallForVariable) return null - val resolvedCall = kotlinToResolvedCallTransformer.transformToResolvedCall(resolvedCallAtom, trace, resultSubstitutor) - kotlinToResolvedCallTransformer.bindAndReport(topLevelCallContext, trace, resolvedCall) + val resolvedCall = kotlinToResolvedCallTransformer.transformToResolvedCall(resolvedCallAtom, trace, resultSubstitutor, diagnostics) + kotlinToResolvedCallTransformer.bindAndReport(topLevelCallContext, trace, resolvedCall, diagnostics) kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, callCheckerContext) val lastCall = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.functionCall else resolvedCall diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/AdditionalDiagnosticReporter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/AdditionalDiagnosticReporter.kt index a152c9e2942..b07ceaf93f5 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/AdditionalDiagnosticReporter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/AdditionalDiagnosticReporter.kt @@ -31,9 +31,10 @@ class AdditionalDiagnosticReporter(private val languageVersionSettings: Language fun reportAdditionalDiagnostics( candidate: ResolvedCallAtom, resultingDescriptor: CallableDescriptor, - kotlinDiagnosticsHolder: KotlinDiagnosticsHolder + kotlinDiagnosticsHolder: KotlinDiagnosticsHolder, + diagnostics: Collection ) { - 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 ): 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().none { + diagnostics.filterIsInstance().none { it.receiver == receiver } && - candidate.diagnostics.filterIsInstance().none { + diagnostics.filterIsInstance().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 ) { - 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().any { + val thereIsUnstableSmartCastError = diagnostics.filterIsInstance().any { it.argument == argument } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt index 35e5496f9d3..1cbabcbd2ca 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt @@ -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() - 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? { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index 41aebf98e19..7cb1e4c03ed 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -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() + 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 + ) } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt index 19c2fca3a3b..e71265233b3 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt @@ -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) } } \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt index c32be7a0d60..7107d98777a 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt @@ -46,10 +46,8 @@ sealed class ResolvedAtom { lateinit var subResolvedAtoms: List private set - lateinit var diagnostics: Collection - private set - protected open fun setAnalyzedResults(subResolvedAtoms: List, diagnostics: Collection) { + protected open fun setAnalyzedResults(subResolvedAtoms: List) { 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, - subResolvedAtoms: List, - diagnostics: Collection + subResolvedAtoms: List ) { this.resultArguments = resultArguments - setAnalyzedResults(subResolvedAtoms, diagnostics) + setAnalyzedResults(subResolvedAtoms) } override val inputTypes: Collection get() = receiver?.let { parameters + it } ?: parameters @@ -127,11 +123,10 @@ class ResolvedCallableReferenceAtom( fun setAnalyzedResults( candidate: CallableReferenceCandidate?, - subResolvedAtoms: List, - diagnostics: Collection + subResolvedAtoms: List ) { this.candidate = candidate - setAnalyzedResults(subResolvedAtoms, diagnostics) + setAnalyzedResults(subResolvedAtoms) } override val inputTypes: Collection @@ -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, + val diagnostics: List, val constraintSystem: ConstraintStorage, val allCandidates: Collection? = null ) : ResolvedAtom() { @@ -175,7 +170,7 @@ class CallResolutionResult( } init { - setAnalyzedResults(listOfNotNull(resultCallAtom), diagnostics) + setAnalyzedResults(listOfNotNull(resultCallAtom)) } override fun toString() = "$type, resultCallAtom = $resultCallAtom, (${diagnostics.joinToString()})" diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt index f8e8579626e..5499e110ef9 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt @@ -68,8 +68,8 @@ class KotlinResolutionCandidate( val knownTypeParametersResultingSubstitutor: TypeSubstitutor? = null, private val resolutionSequence: List = resolvedCall.atom.callKind.resolutionSequence ) : Candidate, KotlinDiagnosticsHolder { + val diagnosticsFromResolutionParts = arrayListOf() // TODO: this is mutable list, take diagnostics only once! private var newSystem: NewConstraintSystemImpl? = null - private val diagnostics = arrayListOf() private var currentApplicability = ResolutionCandidateApplicability.RESOLVED private var subResolvedAtoms: MutableList = 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 - override public fun setAnalyzedResults(subResolvedAtoms: List, diagnostics: Collection) { - super.setAnalyzedResults(subResolvedAtoms, diagnostics) + override public fun setAnalyzedResults(subResolvedAtoms: List) { + super.setAnalyzedResults(subResolvedAtoms) } override fun toString(): String = "$atom, candidate = $candidateDescriptor"