Revert "[FE 1.0] Get rid of the old inference call completer"
This reverts commit 2c7a442cbd.
This commit is contained in:
@@ -0,0 +1,480 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
||||||
|
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionOrSuspendFunctionType
|
||||||
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
|
import org.jetbrains.kotlin.contracts.EffectSystem
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.*
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext.CONSTRAINT_SYSTEM_COMPLETER
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.getEffectiveExpectedType
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.isInvokeCallOnVariable
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.isCallableReference
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.isFakeElement
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.CallPosition
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.inference.filterConstraintsOut
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.inference.toHandle
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.makeNullableTypeIfSafeReceiver
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.MissingDependencySupertypeChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
|
||||||
|
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
import org.jetbrains.kotlin.types.*
|
||||||
|
import org.jetbrains.kotlin.types.error.ErrorScopeKind
|
||||||
|
import org.jetbrains.kotlin.types.error.ErrorUtils
|
||||||
|
import org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class CallCompleter(
|
||||||
|
private val argumentTypeResolver: ArgumentTypeResolver,
|
||||||
|
private val candidateResolver: CandidateResolver,
|
||||||
|
private val dataFlowAnalyzer: DataFlowAnalyzer,
|
||||||
|
private val callCheckers: Iterable<CallChecker>,
|
||||||
|
private val moduleDescriptor: ModuleDescriptor,
|
||||||
|
private val deprecationResolver: DeprecationResolver,
|
||||||
|
private val effectSystem: EffectSystem,
|
||||||
|
private val dataFlowValueFactory: DataFlowValueFactory,
|
||||||
|
private val missingSupertypesResolver: MissingSupertypesResolver
|
||||||
|
) {
|
||||||
|
fun <D : CallableDescriptor> completeCall(
|
||||||
|
context: BasicCallResolutionContext,
|
||||||
|
results: OverloadResolutionResultsImpl<D>,
|
||||||
|
tracing: TracingStrategy
|
||||||
|
): OverloadResolutionResultsImpl<D> {
|
||||||
|
|
||||||
|
val resolvedCall = if (results.isSingleResult) results.resultingCall else null
|
||||||
|
|
||||||
|
// for the case 'foo(a)' where 'foo' is a variable, the call 'foo.invoke(a)' shouldn't be completed separately,
|
||||||
|
// it's completed when the outer (variable as function call) is completed
|
||||||
|
if (!isInvokeCallOnVariable(context.call)) {
|
||||||
|
completeResolvedCallAndArguments(resolvedCall, results, context, tracing)
|
||||||
|
completeAllCandidates(context, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.trace.wantsDiagnostics()) {
|
||||||
|
if (resolvedCall == null) {
|
||||||
|
checkMissingSupertypes(context, missingSupertypesResolver)
|
||||||
|
} else {
|
||||||
|
val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall)
|
||||||
|
resolvedCall.variableCall.call.calleeExpression
|
||||||
|
else
|
||||||
|
resolvedCall.call.calleeExpression
|
||||||
|
val reportOn =
|
||||||
|
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
|
||||||
|
else resolvedCall.call.callElement
|
||||||
|
|
||||||
|
val callCheckerContext = CallCheckerContext(context, deprecationResolver, moduleDescriptor, missingSupertypesResolver)
|
||||||
|
for (callChecker in callCheckers) {
|
||||||
|
callChecker.check(resolvedCall, reportOn, callCheckerContext)
|
||||||
|
|
||||||
|
if (resolvedCall is VariableAsFunctionResolvedCall) {
|
||||||
|
callChecker.check(resolvedCall.variableCall, reportOn, callCheckerContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.isSingleResult && results.resultingCall.status.isSuccess) {
|
||||||
|
return results.changeStatusToSuccess()
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkMissingSupertypes(
|
||||||
|
context: BasicCallResolutionContext,
|
||||||
|
missingSupertypesResolver: MissingSupertypesResolver
|
||||||
|
) {
|
||||||
|
val call = context.call
|
||||||
|
val explicitReceiver = call.explicitReceiver.safeAs<ReceiverValue>() ?: return
|
||||||
|
|
||||||
|
MissingDependencySupertypeChecker.checkSupertypes(
|
||||||
|
explicitReceiver.type, call.callElement, context.trace, missingSupertypesResolver
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <D : CallableDescriptor> completeAllCandidates(
|
||||||
|
context: BasicCallResolutionContext,
|
||||||
|
results: OverloadResolutionResultsImpl<D>
|
||||||
|
) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val candidates = (if (context.collectAllCandidates) {
|
||||||
|
results.allCandidates!!
|
||||||
|
} else {
|
||||||
|
results.resultingCalls
|
||||||
|
}) as Collection<MutableResolvedCall<D>>
|
||||||
|
|
||||||
|
val temporaryBindingTrace =
|
||||||
|
TemporaryBindingTrace.create(context.trace, "Trace to complete a candidate that is not a resulting call")
|
||||||
|
candidates.filterNot { resolvedCall -> resolvedCall.isCompleted }.forEach { resolvedCall ->
|
||||||
|
|
||||||
|
completeResolvedCallAndArguments(
|
||||||
|
resolvedCall,
|
||||||
|
results,
|
||||||
|
context.replaceBindingTrace(temporaryBindingTrace),
|
||||||
|
TracingStrategy.EMPTY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <D : CallableDescriptor> completeResolvedCallAndArguments(
|
||||||
|
resolvedCall: MutableResolvedCall<D>?,
|
||||||
|
results: OverloadResolutionResultsImpl<D>,
|
||||||
|
context: BasicCallResolutionContext,
|
||||||
|
tracing: TracingStrategy
|
||||||
|
) {
|
||||||
|
disableContractsInsideContractsBlock(context.call, resolvedCall?.resultingDescriptor, context.scope, context.trace)
|
||||||
|
|
||||||
|
if (resolvedCall == null || resolvedCall.isCompleted || resolvedCall.constraintSystem == null) {
|
||||||
|
completeArguments(context, results)
|
||||||
|
resolvedCall?.updateResultDataFlowInfoUsingEffects(context.trace)
|
||||||
|
resolvedCall?.markCallAsCompleted()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedCall.completeConstraintSystem(context.expectedType, context.trace)
|
||||||
|
|
||||||
|
completeArguments(context, results)
|
||||||
|
|
||||||
|
resolvedCall.updateResolutionStatusFromConstraintSystem(context, tracing)
|
||||||
|
resolvedCall.updateResultDataFlowInfoUsingEffects(context.trace)
|
||||||
|
resolvedCall.markCallAsCompleted()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <D : CallableDescriptor> MutableResolvedCall<D>.completeConstraintSystem(
|
||||||
|
expectedType: KotlinType,
|
||||||
|
trace: BindingTrace
|
||||||
|
) {
|
||||||
|
val returnType = candidateDescriptor.returnType
|
||||||
|
|
||||||
|
val expectedReturnType =
|
||||||
|
if (call.isCallableReference()) {
|
||||||
|
// TODO: compute generic type argument for R in the kotlin.Function<R> supertype (KT-12963)
|
||||||
|
if (!TypeUtils.noExpectedType(expectedType) && expectedType.isFunctionOrSuspendFunctionType)
|
||||||
|
expectedType.getReturnTypeFromFunctionType()
|
||||||
|
else TypeUtils.NO_EXPECTED_TYPE
|
||||||
|
} else expectedType
|
||||||
|
|
||||||
|
fun ConstraintSystem.Builder.typeInSystem(type: KotlinType?): KotlinType? =
|
||||||
|
type?.let {
|
||||||
|
val substitutor = typeVariableSubstitutors[call.toHandle()] ?: error("No substitutor for call: $call")
|
||||||
|
substitutor.substitute(it, Variance.INVARIANT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSystemIfNeeded(buildSystemWithAdditionalConstraints: (ConstraintSystem.Builder) -> ConstraintSystem?) {
|
||||||
|
val system = buildSystemWithAdditionalConstraints(constraintSystem!!.toBuilder())
|
||||||
|
if (system != null) {
|
||||||
|
setConstraintSystem(system)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnType != null && !TypeUtils.noExpectedType(expectedReturnType)) {
|
||||||
|
updateSystemIfNeeded { builder ->
|
||||||
|
val returnTypeInSystem = builder.typeInSystem(returnType)
|
||||||
|
if (returnTypeInSystem != null) {
|
||||||
|
builder.addSubtypeConstraint(returnTypeInSystem, expectedReturnType, EXPECTED_TYPE_POSITION.position())
|
||||||
|
builder.build()
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val constraintSystemCompleter = trace[CONSTRAINT_SYSTEM_COMPLETER, call.calleeExpression]
|
||||||
|
if (constraintSystemCompleter != null) {
|
||||||
|
// todo improve error reporting with errors in constraints from completer
|
||||||
|
// todo add constraints from completer unconditionally; improve constraints from completer for generic methods
|
||||||
|
// add the constraints only if they don't lead to errors (except errors from upper bounds to improve diagnostics)
|
||||||
|
updateSystemIfNeeded { builder ->
|
||||||
|
constraintSystemCompleter.completeConstraintSystem(builder, this)
|
||||||
|
val system = builder.build()
|
||||||
|
val status = system.filterConstraintsOut(TYPE_BOUND_POSITION).status
|
||||||
|
if (status.hasOnlyErrorsDerivedFrom(FROM_COMPLETER)) null else system
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnType != null && expectedReturnType === TypeUtils.UNIT_EXPECTED_TYPE) {
|
||||||
|
updateSystemIfNeeded { builder ->
|
||||||
|
val returnTypeInSystem = builder.typeInSystem(returnType)
|
||||||
|
if (returnTypeInSystem != null) {
|
||||||
|
builder.addSubtypeConstraint(returnTypeInSystem, moduleDescriptor.builtIns.unitType, EXPECTED_TYPE_POSITION.position())
|
||||||
|
val system = builder.build()
|
||||||
|
if (system.status.isSuccessful()) system else null
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (call.isCallableReference() && !TypeUtils.noExpectedType(expectedType) && expectedType.isFunctionOrSuspendFunctionType) {
|
||||||
|
updateSystemIfNeeded { builder ->
|
||||||
|
candidateDescriptor.valueParameters.zip(expectedType.getValueParameterTypesFromFunctionType())
|
||||||
|
.forEach { (parameter, argument) ->
|
||||||
|
val valueParameterInSystem = builder.typeInSystem(parameter.type)
|
||||||
|
builder.addSubtypeConstraint(
|
||||||
|
valueParameterInSystem,
|
||||||
|
argument.type,
|
||||||
|
VALUE_PARAMETER_POSITION.position(parameter.index)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder = constraintSystem!!.toBuilder()
|
||||||
|
builder.fixVariables()
|
||||||
|
val system = builder.build()
|
||||||
|
setConstraintSystem(system)
|
||||||
|
|
||||||
|
val isNewInferenceEnabled = effectSystem.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)
|
||||||
|
val resultingSubstitutor = if (isNewInferenceEnabled) {
|
||||||
|
system.resultingSubstitutor.replaceWithContravariantApproximatingSubstitution()
|
||||||
|
} else system.resultingSubstitutor
|
||||||
|
|
||||||
|
setSubstitutor(resultingSubstitutor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <D : CallableDescriptor> MutableResolvedCall<D>.updateResolutionStatusFromConstraintSystem(
|
||||||
|
context: BasicCallResolutionContext,
|
||||||
|
tracing: TracingStrategy
|
||||||
|
) {
|
||||||
|
val contextWithResolvedCall = CallCandidateResolutionContext.createForCallBeingAnalyzed(this, context, tracing)
|
||||||
|
val valueArgumentsCheckingResult = candidateResolver.checkAllValueArguments(contextWithResolvedCall, RESOLVE_FUNCTION_ARGUMENTS)
|
||||||
|
|
||||||
|
val status = status
|
||||||
|
if (constraintSystem!!.status.isSuccessful()) {
|
||||||
|
if (status == ResolutionStatus.UNKNOWN_STATUS || status == ResolutionStatus.INCOMPLETE_TYPE_INFERENCE) {
|
||||||
|
setStatusToSuccess()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val receiverType = extensionReceiver?.type
|
||||||
|
|
||||||
|
val errorData = InferenceErrorData.create(
|
||||||
|
candidateDescriptor, constraintSystem!!, valueArgumentsCheckingResult.argumentTypes,
|
||||||
|
receiverType, context.expectedType, context.call
|
||||||
|
)
|
||||||
|
tracing.typeInferenceFailed(context, errorData)
|
||||||
|
|
||||||
|
addStatus(ResolutionStatus.OTHER_ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <D : CallableDescriptor> completeArguments(
|
||||||
|
context: BasicCallResolutionContext,
|
||||||
|
results: OverloadResolutionResultsImpl<D>
|
||||||
|
) {
|
||||||
|
if (context.checkArguments != CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS) return
|
||||||
|
|
||||||
|
val getArgumentMapping: (ValueArgument) -> ArgumentMapping
|
||||||
|
val getDataFlowInfoForArgument: (ValueArgument) -> DataFlowInfo
|
||||||
|
if (results.isSingleResult) {
|
||||||
|
val resolvedCall = results.resultingCall
|
||||||
|
getArgumentMapping = { argument -> resolvedCall.getArgumentMapping(argument) }
|
||||||
|
getDataFlowInfoForArgument = { argument -> resolvedCall.dataFlowInfoForArguments.getInfo(argument) }
|
||||||
|
} else {
|
||||||
|
getArgumentMapping = { ArgumentUnmapped }
|
||||||
|
getDataFlowInfoForArgument = { context.dataFlowInfo }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (valueArgument in context.call.valueArguments) {
|
||||||
|
val argumentMapping = getArgumentMapping(valueArgument!!)
|
||||||
|
|
||||||
|
val expectedType: KotlinType
|
||||||
|
val callPosition: CallPosition
|
||||||
|
val parameter: ValueParameterDescriptor?
|
||||||
|
when (argumentMapping) {
|
||||||
|
is ArgumentMatch -> {
|
||||||
|
expectedType = getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument, context)
|
||||||
|
callPosition = CallPosition.ValueArgumentPosition(results.resultingCall, argumentMapping.valueParameter, valueArgument)
|
||||||
|
parameter = argumentMapping.valueParameter
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
expectedType = TypeUtils.NO_EXPECTED_TYPE
|
||||||
|
callPosition = CallPosition.Unknown
|
||||||
|
parameter = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newContext =
|
||||||
|
context.replaceDataFlowInfo(getDataFlowInfoForArgument(valueArgument))
|
||||||
|
.replaceExpectedType(expectedType)
|
||||||
|
.replaceCallPosition(callPosition)
|
||||||
|
completeOneArgument(valueArgument, parameter, newContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTypeForConvertableConstant(constant: CompileTimeConstant<*>): SimpleType? {
|
||||||
|
val value = constant.getValue(TypeUtils.NO_EXPECTED_TYPE).safeAs<Number>()?.toLong() ?: return null
|
||||||
|
val typeConstructor = IntegerValueTypeConstructor(value, moduleDescriptor, constant.parameters)
|
||||||
|
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(
|
||||||
|
TypeAttributes.Empty, typeConstructor, emptyList(), false,
|
||||||
|
ErrorUtils.createErrorScope(ErrorScopeKind.INTEGER_LITERAL_TYPE_SCOPE, throwExceptions = true, typeConstructor.toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun completeOneArgument(
|
||||||
|
argument: ValueArgument,
|
||||||
|
parameter: ValueParameterDescriptor?,
|
||||||
|
context: BasicCallResolutionContext
|
||||||
|
) {
|
||||||
|
if (argument.isExternal()) return
|
||||||
|
|
||||||
|
val expression = argument.getArgumentExpression() ?: return
|
||||||
|
val deparenthesized = KtPsiUtil.getLastElementDeparenthesized(expression, context.statementFilter) ?: return
|
||||||
|
|
||||||
|
val recordedType = context.trace.getType(expression)
|
||||||
|
var updatedType: KotlinType? = recordedType
|
||||||
|
|
||||||
|
val results = completeCallForArgument(deparenthesized, context)
|
||||||
|
|
||||||
|
val constant = context.trace[BindingContext.COMPILE_TIME_VALUE, deparenthesized]
|
||||||
|
val convertedConst = constant is IntegerValueTypeConstant && constant.convertedFromSigned
|
||||||
|
|
||||||
|
if (results != null && results.isSingleResult) {
|
||||||
|
val resolvedCall = results.resultingCall
|
||||||
|
if (!convertedConst) {
|
||||||
|
updatedType =
|
||||||
|
if (resolvedCall.hasInferredReturnType())
|
||||||
|
resolvedCall.makeNullableTypeIfSafeReceiver(resolvedCall.resultingDescriptor?.returnType, context)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the cases like 'foo(1)' the type of '1' depends on expected type (it can be Int, Byte, etc.),
|
||||||
|
// so while the expected type is not known, it's IntegerValueType(1), and should be updated when the expected type is known.
|
||||||
|
if (recordedType != null && !recordedType.constructor.isDenotable) {
|
||||||
|
updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, expression) ?: updatedType
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameter != null && ImplicitIntegerCoercion.isEnabledForParameter(parameter)) {
|
||||||
|
val argumentCompileTimeValue = context.trace[BindingContext.COMPILE_TIME_VALUE, deparenthesized]
|
||||||
|
if (argumentCompileTimeValue != null && argumentCompileTimeValue.parameters.isConvertableConstVal) {
|
||||||
|
val generalNumberType = createTypeForConvertableConstant(argumentCompileTimeValue)
|
||||||
|
if (generalNumberType != null) {
|
||||||
|
updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(
|
||||||
|
context.trace, context.statementFilter, context.expectedType, generalNumberType, expression
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (convertedConst) {
|
||||||
|
context.trace.report(Errors.SIGNED_CONSTANT_CONVERTED_TO_UNSIGNED.on(deparenthesized))
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedType = updateRecordedTypeForArgument(updatedType, recordedType, expression, context.statementFilter, context.trace)
|
||||||
|
|
||||||
|
// While the expected type is not known, the function literal arguments are not analyzed (to analyze function literal bodies once),
|
||||||
|
// but they should be analyzed when the expected type is known (during the call completion).
|
||||||
|
ArgumentTypeResolver.getFunctionLiteralArgumentIfAny(expression, context)?.let { functionLiteralArgument ->
|
||||||
|
argumentTypeResolver.getFunctionLiteralTypeInfo(expression, functionLiteralArgument, context, RESOLVE_FUNCTION_ARGUMENTS, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// While the expected type is not known, (possibly overloaded) callable references can have placeholder types
|
||||||
|
// (to avoid exponential search for overloaded higher-order functions).
|
||||||
|
// They should be analyzed now.
|
||||||
|
ArgumentTypeResolver.getCallableReferenceExpressionIfAny(expression, context)?.let { callableReferenceArgument ->
|
||||||
|
argumentTypeResolver.getCallableReferenceTypeInfo(expression, callableReferenceArgument, context, RESOLVE_FUNCTION_ARGUMENTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
dataFlowAnalyzer.checkType(updatedType, deparenthesized, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun completeCallForArgument(
|
||||||
|
expression: KtExpression,
|
||||||
|
context: BasicCallResolutionContext
|
||||||
|
): OverloadResolutionResultsImpl<*>? {
|
||||||
|
val cachedData = getResolutionResultsCachedData(expression, context) ?: return null
|
||||||
|
val (cachedResolutionResults, cachedContext, tracing) = cachedData
|
||||||
|
|
||||||
|
val contextForArgument = cachedContext.replaceBindingTrace(context.trace)
|
||||||
|
.replaceExpectedType(context.expectedType).replaceCollectAllCandidates(false).replaceCallPosition(context.callPosition)
|
||||||
|
|
||||||
|
return completeCall(contextForArgument, cachedResolutionResults, tracing)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateRecordedTypeForArgument(
|
||||||
|
updatedType: KotlinType?,
|
||||||
|
recordedType: KotlinType?,
|
||||||
|
argumentExpression: KtExpression,
|
||||||
|
statementFilter: StatementFilter,
|
||||||
|
trace: BindingTrace
|
||||||
|
): KotlinType? {
|
||||||
|
//workaround for KT-8218
|
||||||
|
if ((!ErrorUtils.containsErrorType(recordedType) && recordedType == updatedType) || updatedType == null) return updatedType
|
||||||
|
|
||||||
|
fun deparenthesizeOrGetSelector(expression: KtExpression?): KtExpression? {
|
||||||
|
val deparenthesized = KtPsiUtil.deparenthesizeOnce(expression)
|
||||||
|
if (deparenthesized != expression) return deparenthesized
|
||||||
|
|
||||||
|
// see KtPsiUtil.getLastElementDeparenthesized
|
||||||
|
if (expression is KtBlockExpression) {
|
||||||
|
return statementFilter.getLastStatementInABlock(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (expression as? KtQualifiedExpression)?.selectorExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
val expressions = ArrayList<KtExpression>()
|
||||||
|
var expression: KtExpression? = argumentExpression
|
||||||
|
while (expression != null) {
|
||||||
|
expressions.add(expression)
|
||||||
|
expression = deparenthesizeOrGetSelector(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
var shouldBeMadeNullable = false
|
||||||
|
expressions.asReversed().forEach { ktExpression ->
|
||||||
|
if (!(ktExpression is KtParenthesizedExpression || ktExpression is KtLabeledExpression || ktExpression is KtAnnotatedExpression)) {
|
||||||
|
shouldBeMadeNullable = hasNecessarySafeCall(ktExpression, trace)
|
||||||
|
}
|
||||||
|
BindingContextUtils.updateRecordedType(updatedType, ktExpression, trace, shouldBeMadeNullable)
|
||||||
|
}
|
||||||
|
return trace.getType(argumentExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasNecessarySafeCall(expression: KtExpression, trace: BindingTrace): Boolean {
|
||||||
|
// We are interested in type of the last call:
|
||||||
|
// 'a.b?.foo()' is safe call, but 'a?.b.foo()' is not.
|
||||||
|
// Since receiver is 'a.b' and selector is 'foo()',
|
||||||
|
// we can only check if an expression is safe call.
|
||||||
|
if (expression !is KtSafeQualifiedExpression) return false
|
||||||
|
|
||||||
|
//If a receiver type is not null, then this safe expression is useless, and we don't need to make the result type nullable.
|
||||||
|
val expressionType = trace.getType(expression.receiverExpression)
|
||||||
|
return expressionType != null && TypeUtils.isNullableType(expressionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableResolvedCall<*>.updateResultDataFlowInfoUsingEffects(bindingTrace: BindingTrace) {
|
||||||
|
if (dataFlowInfoForArguments is MutableDataFlowInfoForArguments.WithoutArgumentsCheck) return
|
||||||
|
|
||||||
|
val moduleDescriptor = DescriptorUtils.getContainingModule(this.resultingDescriptor?.containingDeclaration ?: return)
|
||||||
|
val resultDFIfromES = effectSystem.getDataFlowInfoForFinishedCall(this, bindingTrace, moduleDescriptor)
|
||||||
|
dataFlowInfoForArguments.updateResultInfo(resultDFIfromES)
|
||||||
|
|
||||||
|
effectSystem.recordDefiniteInvocations(this, bindingTrace, moduleDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user