[FE 1.0] Introduce builder inference stub types checker which may report more precise and clear errors due to resolution ambiguity
^KT-49828 Fixed
This commit is contained in:
committed by
teamcity
parent
dcc42d66c3
commit
455b3143e7
@@ -27,8 +27,10 @@ import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData;
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier;
|
||||
import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo;
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DescriptorBasedDeprecationInfo;
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData;
|
||||
@@ -839,6 +841,9 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtExpression> NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> OVERLOAD_RESOLUTION_AMBIGUITY = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, String, String, String> OVERLOAD_RESOLUTION_AMBIGUITY_BECAUSE_OF_STUB_TYPES = DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, KotlinType, String, String> STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY = DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory4<PsiElement, KotlinType, String, String, BuilderLambdaLabelingInfo> STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY = DiagnosticFactory4.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> NONE_APPLICABLE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> CANNOT_COMPLETE_RESOLVE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> UNRESOLVED_REFERENCE_WRONG_RECEIVER =
|
||||
|
||||
+5
@@ -917,6 +917,11 @@ public class DefaultErrorMessages {
|
||||
ELEMENT_TEXT, STRING, ELEMENT_TEXT);
|
||||
|
||||
MAP.put(OVERLOAD_RESOLUTION_AMBIGUITY, "Overload resolution ambiguity: {0}", AMBIGUOUS_CALLS);
|
||||
MAP.put(OVERLOAD_RESOLUTION_AMBIGUITY_BECAUSE_OF_STUB_TYPES, "The builder `{0}` you are using has a type argument for type parameter(s) `{1}` that was not explicitly specified. " +
|
||||
"Without knowing the type of `{1}` compiler cannot choose which overloaded function `{2}` to call here. " +
|
||||
"Please, either specify the type `{1}` explicitly in `{0}` builder or use explicit cast to a specific type for parameter or receiver (see specific errors on them).", STRING, STRING, STRING);
|
||||
MAP.put(STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY, "Type of an argument hasn't inferred yet. To disambiguate this call, please use explicit cast for the parameter to {1} if you rely a type argument for type parameter(s) {2} to be inferred to {3}", RENDER_TYPE, STRING, STRING);
|
||||
MAP.put(STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY, "Type of a receiver hasn't inferred yet. To disambiguate this call, please use explicit cast for the receiver to {1} if you rely a type argument for type parameter(s) {2} to be inferred to {3}", RENDER_TYPE, STRING, STRING, null);
|
||||
MAP.put(NONE_APPLICABLE, "None of the following functions can be called with the arguments supplied: {0}", AMBIGUOUS_CALLS);
|
||||
MAP.put(CANNOT_COMPLETE_RESOLVE, "Cannot choose among the following candidates without completing type inference: {0}", AMBIGUOUS_CALLS);
|
||||
MAP.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, "Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: {0}", AMBIGUOUS_CALLS);
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.checkers
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.ImplicitScopeTower
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
|
||||
interface CallCheckerWithAdditionalResolve {
|
||||
fun check(
|
||||
overloadResolutionResults: OverloadResolutionResults<*>,
|
||||
scopeTower: ImplicitScopeTower,
|
||||
resolutionCallbacks: KotlinResolutionCallbacks,
|
||||
expectedType: UnwrappedType?,
|
||||
context: BasicCallResolutionContext,
|
||||
)
|
||||
}
|
||||
+7
-5
@@ -217,11 +217,13 @@ class BuilderInferenceSession(
|
||||
|
||||
override fun currentConstraintSystem() = ConstraintStorage.Empty
|
||||
|
||||
fun getNotFixedToInferredTypesSubstitutor(): NewTypeSubstitutor {
|
||||
val currentSubstitutor =
|
||||
commonSystem.buildCurrentSubstitutor().cast<NewTypeSubstitutor>().takeIf { !it.isEmpty } ?: return EmptySubstitutor
|
||||
return ComposedSubstitutor(currentSubstitutor, createNonFixedTypeToVariableSubstitutor())
|
||||
}
|
||||
fun getNotFixedToInferredTypesSubstitutor(): NewTypeSubstitutor =
|
||||
ComposedSubstitutor(getCurrentSubstitutor(), createNonFixedTypeToVariableSubstitutor())
|
||||
|
||||
fun getUsedStubTypes(): Set<StubTypeForBuilderInference> = stubsForPostponedVariables.values.toSet()
|
||||
|
||||
fun getCurrentSubstitutor(): NewTypeSubstitutor =
|
||||
commonSystem.buildCurrentSubstitutor().cast<NewTypeSubstitutor>().takeIf { !it.isEmpty } ?: EmptySubstitutor
|
||||
|
||||
override fun initializeLambda(lambda: ResolvedLambdaAtom) {
|
||||
this.lambda = lambda
|
||||
|
||||
+1
-3
@@ -208,13 +208,11 @@ class KotlinResolutionCallbacksImpl(
|
||||
kotlinToResolvedCallTransformer, expressionTypingServices, argumentTypeResolver,
|
||||
doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, typeApproximator,
|
||||
missingSupertypesResolver, lambdaArgument
|
||||
)
|
||||
).apply { lambdaArgument.builderInferenceSession = this }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
lambdaArgument.builderInferenceSession = builderInferenceSession
|
||||
|
||||
val temporaryTrace = if (builderInferenceSession != null)
|
||||
TemporaryBindingTrace.create(trace, "Trace to resolve builder inference lambda: $lambdaArgument")
|
||||
else
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.OldResolutionCandidate
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
|
||||
import org.jetbrains.kotlin.resolve.calls.util.*
|
||||
import org.jetbrains.kotlin.resolve.checkers.PassingProgressionAsCollectionCallChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.ResolutionWithStubTypesChecker
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
@@ -78,7 +79,10 @@ class PSICallResolver(
|
||||
private val missingSupertypesResolver: MissingSupertypesResolver,
|
||||
private val resultTypeResolver: ResultTypeResolver,
|
||||
) {
|
||||
private val callCheckersWithAdditionalResolve = listOf(PassingProgressionAsCollectionCallChecker(kotlinCallResolver))
|
||||
private val callCheckersWithAdditionalResolve = listOf(
|
||||
PassingProgressionAsCollectionCallChecker(kotlinCallResolver),
|
||||
ResolutionWithStubTypesChecker(kotlinCallResolver)
|
||||
)
|
||||
|
||||
private val givenCandidatesName = Name.special("<given candidates>")
|
||||
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ abstract class StubTypesBasedInferenceSession<D : CallableDescriptor>(
|
||||
val builtIns: KotlinBuiltIns
|
||||
) : InferenceSession {
|
||||
protected val partiallyResolvedCallsInfo = arrayListOf<PSIPartialCallInfo>()
|
||||
private val errorCallsInfo = arrayListOf<PSIErrorCallInfo<D>>()
|
||||
val errorCallsInfo = arrayListOf<PSIErrorCallInfo<D>>()
|
||||
private val completedCalls = hashSetOf<ResolvedAtom>()
|
||||
protected val nestedInferenceSessions = hashSetOf<StubTypesBasedInferenceSession<*>>()
|
||||
|
||||
|
||||
@@ -19,26 +19,34 @@ import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.CallTransformer
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ComposedSubstitutor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.EmptySubstitutor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.getNestedTypeVariables
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.OldResolutionCandidate
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isParameterOfAnnotation
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
|
||||
import org.jetbrains.kotlin.resolve.scopes.collectSyntheticConstructors
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.types.typeUtil.contains
|
||||
import org.jetbrains.kotlin.util.buildNotFixedVariablesToPossibleResultType
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
enum class ResolveArgumentsMode {
|
||||
@@ -328,3 +336,43 @@ internal fun PsiElement.reportOnElement() =
|
||||
?.takeIf { isImplicit }
|
||||
?.let { getStrictParentOfType<KtSecondaryConstructor>()!! }
|
||||
?: this
|
||||
|
||||
internal fun List<KotlinCallArgument>.replaceTypes(
|
||||
context: BasicCallResolutionContext,
|
||||
resolutionCallbacks: KotlinResolutionCallbacks,
|
||||
replace: (Int, UnwrappedType) -> UnwrappedType?,
|
||||
): List<KotlinCallArgument> = mapIndexed { i, argument ->
|
||||
if (argument !is SimpleKotlinCallArgument) return@mapIndexed argument
|
||||
|
||||
val psiExpression = argument.psiExpression ?: return@mapIndexed argument
|
||||
val argumentSubstitutor = if (argument is SubKotlinCallArgument) {
|
||||
val notFixedVariablesSubstitutor =
|
||||
argument.callResult.constraintSystem.buildNotFixedVariablesToPossibleResultType(resolutionCallbacks) as NewTypeSubstitutor
|
||||
val fixedVariablesSubstitutor =
|
||||
argument.callResult.constraintSystem.getBuilder().buildCurrentSubstitutor() as NewTypeSubstitutor
|
||||
|
||||
ComposedSubstitutor(notFixedVariablesSubstitutor, fixedVariablesSubstitutor)
|
||||
} else EmptySubstitutor
|
||||
|
||||
val newType = replace(i, argumentSubstitutor.safeSubstitute(argument.receiver.receiverValue.type.unwrap()))
|
||||
?: return@mapIndexed argument
|
||||
|
||||
ExpressionKotlinCallArgumentImpl(
|
||||
argument.psiCallArgument.valueArgument,
|
||||
argument.psiCallArgument.dataFlowInfoBeforeThisArgument,
|
||||
argument.psiCallArgument.dataFlowInfoAfterThisArgument,
|
||||
ReceiverValueWithSmartCastInfo(
|
||||
ExpressionReceiver.create(psiExpression, newType, context.trace.bindingContext),
|
||||
typesFromSmartCasts = emptySet(),
|
||||
isStable = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
internal fun PSIKotlinCall.replaceArguments(
|
||||
newArguments: List<KotlinCallArgument>,
|
||||
newReceiverArgument: ReceiverExpressionKotlinCallArgument? = null,
|
||||
): PSIKotlinCall = PSIKotlinCallImpl(
|
||||
callKind, psiCall, tracingStrategy, newReceiverArgument, dispatchReceiverForInvokeExtension, name, typeArguments, newArguments,
|
||||
externalArgument, startingDataFlowInfo, resultDataFlowInfo, dataFlowInfoForArguments, isForImplicitInvoke
|
||||
)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.util
|
||||
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
|
||||
class BuilderLambdaLabelingInfo(val builderLambda: KtLambdaExpression?) {
|
||||
companion object {
|
||||
val EMPTY = BuilderLambdaLabelingInfo(null)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return builderLambda.toString()
|
||||
}
|
||||
}
|
||||
+19
-47
@@ -7,21 +7,18 @@ package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.PROGRESSIONS_CHANGING_RESOLVE
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.KotlinCallResolver
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerWithAdditionalResolve
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.*
|
||||
import org.jetbrains.kotlin.resolve.calls.util.replaceArguments
|
||||
import org.jetbrains.kotlin.resolve.calls.util.replaceTypes
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerState
|
||||
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerStateInternals
|
||||
@@ -33,7 +30,7 @@ import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
Please don't use similar logic for other checkers
|
||||
*/
|
||||
@OptIn(ClassicTypeCheckerStateInternals::class)
|
||||
class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver: KotlinCallResolver) {
|
||||
class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver: KotlinCallResolver) : CallCheckerWithAdditionalResolve {
|
||||
private val typeCheckerState = ClassicTypeCheckerState(isErrorTypeEqualsToAnything = false)
|
||||
|
||||
private val iterableProgressions = listOf(
|
||||
@@ -67,14 +64,21 @@ class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver:
|
||||
if (progressionOrRangeArgumentTypes.all { it == null }) return
|
||||
|
||||
val builtIns = resolvedCall.candidateDescriptor.builtIns
|
||||
val newArguments = replaceArgumentsWithCollectionIfNeeded(valueArguments, progressionOrRangeArgumentTypes, context.trace, builtIns)
|
||||
|
||||
val newCall = PSIKotlinCallImpl(
|
||||
kotlinCall.callKind, kotlinCall.psiCall, kotlinCall.tracingStrategy, kotlinCall.explicitReceiver,
|
||||
kotlinCall.dispatchReceiverForInvokeExtension, kotlinCall.name, kotlinCall.typeArguments, newArguments,
|
||||
kotlinCall.externalArgument, kotlinCall.startingDataFlowInfo, kotlinCall.resultDataFlowInfo,
|
||||
kotlinCall.dataFlowInfoForArguments, kotlinCall.isForImplicitInvoke
|
||||
)
|
||||
val newArguments = valueArguments.replaceTypes(context, resolutionCallbacks) { i, type ->
|
||||
val progressionOrRangeElementType = progressionOrRangeArgumentTypes[i] ?: return@replaceTypes null
|
||||
intersectTypes(
|
||||
listOf(
|
||||
KotlinTypeFactory.simpleNotNullType(
|
||||
TypeAttributes.Empty,
|
||||
builtIns.collection,
|
||||
listOf(TypeProjectionImpl(progressionOrRangeElementType))
|
||||
),
|
||||
type
|
||||
)
|
||||
)
|
||||
}
|
||||
val newCall = kotlinCall.replaceArguments(newArguments)
|
||||
|
||||
val candidateForCollectionReplacedArgument = kotlinCallResolver.resolveCall(
|
||||
scopeTower, resolutionCallbacks, newCall, expectedType, context.collectAllCandidates
|
||||
@@ -129,7 +133,7 @@ class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver:
|
||||
}
|
||||
}
|
||||
|
||||
fun check(
|
||||
override fun check(
|
||||
overloadResolutionResults: OverloadResolutionResults<*>,
|
||||
scopeTower: ImplicitScopeTower,
|
||||
resolutionCallbacks: KotlinResolutionCallbacks,
|
||||
@@ -149,36 +153,4 @@ class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver:
|
||||
builtIns.collection,
|
||||
listOf(TypeProjectionImpl(builtIns.nullableAnyType))
|
||||
)
|
||||
|
||||
private fun replaceArgumentsWithCollectionIfNeeded(
|
||||
valueArguments: List<KotlinCallArgument>,
|
||||
progressionOrRangeArgumentTypes: List<KotlinType?>,
|
||||
trace: BindingTrace,
|
||||
builtIns: KotlinBuiltIns
|
||||
): List<KotlinCallArgument> = valueArguments.mapIndexed { i, argument ->
|
||||
if (argument !is ExpressionKotlinCallArgumentImpl) return@mapIndexed argument
|
||||
val progressionOrRangeElementType = progressionOrRangeArgumentTypes[i] ?: return@mapIndexed argument
|
||||
val psiExpression = argument.psiExpression ?: return@mapIndexed argument
|
||||
val newType = intersectTypes(
|
||||
listOf(
|
||||
KotlinTypeFactory.simpleNotNullType(
|
||||
TypeAttributes.Empty,
|
||||
builtIns.collection,
|
||||
listOf(TypeProjectionImpl(progressionOrRangeElementType))
|
||||
),
|
||||
argument.receiver.receiverValue.type.unwrap()
|
||||
)
|
||||
)
|
||||
|
||||
ExpressionKotlinCallArgumentImpl(
|
||||
argument.psiCallArgument.valueArgument,
|
||||
DataFlowInfo.EMPTY,
|
||||
DataFlowInfo.EMPTY,
|
||||
ReceiverValueWithSmartCastInfo(
|
||||
ExpressionReceiver.create(psiExpression, newType, trace.bindingContext),
|
||||
emptySet(),
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.KotlinCallResolver
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerWithAdditionalResolve
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.components.stableType
|
||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceSession
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.*
|
||||
import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.util.replaceArguments
|
||||
import org.jetbrains.kotlin.resolve.calls.util.replaceTypes
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor
|
||||
|
||||
class ResolutionWithStubTypesChecker(private val kotlinCallResolver: KotlinCallResolver) : CallCheckerWithAdditionalResolve {
|
||||
override fun check(
|
||||
overloadResolutionResults: OverloadResolutionResults<*>,
|
||||
scopeTower: ImplicitScopeTower,
|
||||
resolutionCallbacks: KotlinResolutionCallbacks,
|
||||
expectedType: UnwrappedType?,
|
||||
context: BasicCallResolutionContext,
|
||||
) {
|
||||
// Don't check builder inference lambdas if the entire builder call itself has resolution ambiguity
|
||||
if (!overloadResolutionResults.isSingleResult) return
|
||||
|
||||
val builderResolvedCall = overloadResolutionResults.resultingCall as? NewAbstractResolvedCall<*> ?: return
|
||||
|
||||
val builderLambdas = (builderResolvedCall.psiKotlinCall.argumentsInParenthesis + builderResolvedCall.psiKotlinCall.externalArgument)
|
||||
.filterIsInstance<LambdaKotlinCallArgument>()
|
||||
.filter { it.hasBuilderInferenceAnnotation }
|
||||
|
||||
for (lambda in builderLambdas) {
|
||||
val builderInferenceSession = lambda.builderInferenceSession as? BuilderInferenceSession ?: continue
|
||||
val errorCalls = builderInferenceSession.errorCallsInfo
|
||||
for (errorCall in errorCalls) {
|
||||
val resolutionResult = errorCall.result
|
||||
if (resolutionResult.isAmbiguity) {
|
||||
val firstResolvedCall = resolutionResult.resultingCalls.first() as? NewAbstractResolvedCall<*> ?: continue
|
||||
processResolutionAmbiguityError(context, firstResolvedCall, lambda, resolutionCallbacks, expectedType, scopeTower)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processResolutionAmbiguityError(
|
||||
context: BasicCallResolutionContext,
|
||||
firstResolvedCall: NewAbstractResolvedCall<*>,
|
||||
lambda: LambdaKotlinCallArgument,
|
||||
resolutionCallbacks: KotlinResolutionCallbacks,
|
||||
expectedType: UnwrappedType?,
|
||||
scopeTower: ImplicitScopeTower,
|
||||
) {
|
||||
val kotlinCall = firstResolvedCall.psiKotlinCall
|
||||
val calleeExpression = kotlinCall.psiCall.calleeExpression
|
||||
val builderCalleeExpression = context.call.calleeExpression
|
||||
|
||||
if (calleeExpression == null || builderCalleeExpression == null) return
|
||||
|
||||
val receiverValue = firstResolvedCall.extensionReceiver
|
||||
val valueArguments = kotlinCall.argumentsInParenthesis
|
||||
|
||||
val builderInferenceSession = lambda.builderInferenceSession as BuilderInferenceSession
|
||||
val stubVariablesSubstitutor = builderInferenceSession.getNotFixedToInferredTypesSubstitutor()
|
||||
val variablesForUsedStubTypes = builderInferenceSession.getUsedStubTypes().map { it.originalTypeVariable }
|
||||
val typeVariablesSubstitutionMap = (builderInferenceSession.getCurrentSubstitutor() as NewTypeSubstitutorByConstructorMap).map
|
||||
.filterKeys { it in variablesForUsedStubTypes }
|
||||
|
||||
val newReceiverArgument = receiverValue?.buildSubstitutedReceiverArgument(stubVariablesSubstitutor, context)
|
||||
val newArguments = valueArguments.replaceTypes(context, resolutionCallbacks) { _, type ->
|
||||
stubVariablesSubstitutor.safeSubstitute(type)
|
||||
}
|
||||
|
||||
if (newReceiverArgument == null && valueArguments == newArguments) return
|
||||
|
||||
val newCall = kotlinCall.replaceArguments(newArguments, newReceiverArgument)
|
||||
val candidatesForSubstitutedCall = kotlinCallResolver.resolveCall(
|
||||
scopeTower, resolutionCallbacks, newCall, expectedType, context.collectAllCandidates
|
||||
)
|
||||
|
||||
// It means we can't disambiguate the call with substituted receiver and arguments
|
||||
if (candidatesForSubstitutedCall.size != 1) return
|
||||
|
||||
val typeVariablesCausedAmbiguity = reportStubTypeCausesAmbiguityOnArgumentsIfNeeded(
|
||||
valueArguments, newArguments, context, typeVariablesSubstitutionMap
|
||||
).toMutableSet()
|
||||
|
||||
val newReceiverValue = newReceiverArgument?.receiverValue
|
||||
|
||||
if (receiverValue != null && newReceiverValue != null) {
|
||||
typeVariablesCausedAmbiguity.addAll(
|
||||
reportStubTypeCausesAmbiguityOnReceiverIfNeeded(
|
||||
receiverValue, newReceiverValue, kotlinCall, lambda, context, typeVariablesSubstitutionMap
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (typeVariablesCausedAmbiguity.isNotEmpty()) {
|
||||
context.trace.report(
|
||||
OVERLOAD_RESOLUTION_AMBIGUITY_BECAUSE_OF_STUB_TYPES.on(
|
||||
calleeExpression,
|
||||
builderCalleeExpression.toString(),
|
||||
typeVariablesCausedAmbiguity.toString(),
|
||||
calleeExpression.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportStubTypeCausesAmbiguityOnReceiverIfNeeded(
|
||||
receiver: ReceiverValue,
|
||||
newReceiver: ReceiverValue,
|
||||
kotlinCall: PSIKotlinCall,
|
||||
lambda: LambdaKotlinCallArgument,
|
||||
context: BasicCallResolutionContext,
|
||||
substitutionMap: Map<TypeConstructor, UnwrappedType>
|
||||
): Set<NewTypeVariableConstructor> = buildSet {
|
||||
val receiverType = receiver.type
|
||||
val newReceiverType = newReceiver.type
|
||||
val relatedLambdaToLabel = (lambda.psiExpression as? KtLambdaExpression)?.takeIf {
|
||||
val lexicalScope = context.trace.bindingContext[BindingContext.LEXICAL_SCOPE, kotlinCall.psiCall.callElement]
|
||||
val nearestScopeDescriptor = lexicalScope?.ownerDescriptor
|
||||
// Don't need to store lambda psi element if it can be accessed though unmarked `this`
|
||||
nearestScopeDescriptor != null && nearestScopeDescriptor != (receiver as? ExtensionReceiver)?.declarationDescriptor
|
||||
}
|
||||
|
||||
if (receiverType != newReceiverType) {
|
||||
val typeVariables = substitutionMap.map { it.key as NewTypeVariableConstructor }
|
||||
val typeParameters = typeVariables.joinToString { (it.originalTypeParameter?.name ?: it).toString() }
|
||||
val inferredTypes = substitutionMap.values.joinToString()
|
||||
|
||||
addAll(typeVariables)
|
||||
|
||||
context.trace.report(
|
||||
STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY.on(
|
||||
kotlinCall.explicitReceiver?.psiExpression ?: kotlinCall.psiCall.callElement,
|
||||
newReceiverType, typeParameters, inferredTypes,
|
||||
if (relatedLambdaToLabel != null) BuilderLambdaLabelingInfo(relatedLambdaToLabel) else BuilderLambdaLabelingInfo.EMPTY
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportStubTypeCausesAmbiguityOnArgumentsIfNeeded(
|
||||
valueArguments: List<KotlinCallArgument>,
|
||||
newArguments: List<KotlinCallArgument>,
|
||||
context: BasicCallResolutionContext,
|
||||
substitutionMap: Map<TypeConstructor, UnwrappedType>
|
||||
): Set<NewTypeVariableConstructor> = buildSet {
|
||||
for ((i, valueArgument) in valueArguments.withIndex()) {
|
||||
if (valueArgument !is SimpleKotlinCallArgument) continue
|
||||
|
||||
val substitutedValueArgument = newArguments[i] as? SimpleKotlinCallArgument ?: continue
|
||||
val originalType = valueArgument.receiver.stableType
|
||||
val substitutedType = substitutedValueArgument.receiver.stableType
|
||||
|
||||
if (originalType != substitutedType) {
|
||||
val psiExpression = valueArgument.psiExpression ?: continue
|
||||
val typeVariables = substitutionMap.map { it.key as NewTypeVariableConstructor }
|
||||
val typeParameters = typeVariables.joinToString { (it.originalTypeParameter?.name ?: it).toString() }
|
||||
val inferredTypes = substitutionMap.values.joinToString()
|
||||
|
||||
addAll(typeVariables)
|
||||
|
||||
context.trace.report(
|
||||
STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY.on(psiExpression, substitutedType, typeParameters, inferredTypes)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ReceiverValue.buildSubstitutedReceiverArgument(
|
||||
substitutor: NewTypeSubstitutor,
|
||||
context: BasicCallResolutionContext,
|
||||
): ReceiverExpressionKotlinCallArgument? {
|
||||
val newType = substitutor.safeSubstitute(type.unwrap())
|
||||
val receiverValue = when (this) {
|
||||
is ExpressionReceiver -> ExpressionReceiver.create(expression, newType, context.trace.bindingContext)
|
||||
is ExtensionReceiver -> ExtensionReceiver(declarationDescriptor, newType, original)
|
||||
else -> return null
|
||||
}
|
||||
|
||||
return ReceiverExpressionKotlinCallArgument(
|
||||
ReceiverValueWithSmartCastInfo(receiverValue, typesFromSmartCasts = emptySet(), true)
|
||||
)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -373,7 +373,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CastDiagnosticsUtil.isCastPossible(actualType, targetType, components.platformToKotlinClassMapper) && !TypeUtilsKt.isStubType(actualType)) {
|
||||
if (!TypeUtilsKt.isStubType(actualType) && !CastDiagnosticsUtil.isCastPossible(actualType, targetType, components.platformToKotlinClassMapper)) {
|
||||
context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user