[NI] Support smartcast info in ResolvedCall on receivers
This commit is contained in:
+29
-16
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.INVOKE_ON
|
|||||||
import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.NON_KOTLIN_FUNCTION
|
import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.NON_KOTLIN_FUNCTION
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||||
@@ -126,28 +127,40 @@ class DiagnosticReporterByTrackingStrategy(
|
|||||||
|
|
||||||
private fun reportSmartCast(smartCastDiagnostic: SmartCastDiagnostic) {
|
private fun reportSmartCast(smartCastDiagnostic: SmartCastDiagnostic) {
|
||||||
val expressionArgument = smartCastDiagnostic.argument
|
val expressionArgument = smartCastDiagnostic.argument
|
||||||
if (expressionArgument is ExpressionKotlinCallArgumentImpl) {
|
val smartCastResult = when (expressionArgument) {
|
||||||
val context = context.replaceDataFlowInfo(expressionArgument.dataFlowInfoBeforeThisArgument)
|
is ExpressionKotlinCallArgumentImpl -> {
|
||||||
val argumentExpression = KtPsiUtil.getLastElementDeparenthesized(expressionArgument.valueArgument.getArgumentExpression (), context.statementFilter)
|
trace.markAsReported()
|
||||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expressionArgument.receiver.receiverValue, context)
|
val context = context.replaceDataFlowInfo(expressionArgument.dataFlowInfoBeforeThisArgument)
|
||||||
SmartCastManager.checkAndRecordPossibleCast(
|
val argumentExpression = KtPsiUtil.getLastElementDeparenthesized(expressionArgument.valueArgument.getArgumentExpression (), context.statementFilter)
|
||||||
dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call,
|
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expressionArgument.receiver.receiverValue, context)
|
||||||
recordExpressionType = true)
|
SmartCastManager.checkAndRecordPossibleCast(
|
||||||
trace.markAsReported()
|
dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call,
|
||||||
|
recordExpressionType = true)
|
||||||
|
}
|
||||||
|
is ReceiverExpressionKotlinCallArgument -> {
|
||||||
|
trace.markAsReported()
|
||||||
|
val receiverValue = expressionArgument.receiver.receiverValue
|
||||||
|
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, context)
|
||||||
|
SmartCastManager.checkAndRecordPossibleCast(
|
||||||
|
dataFlowValue, smartCastDiagnostic.smartCastType, (receiverValue as? ExpressionReceiver)?.expression, context, call,
|
||||||
|
recordExpressionType = true)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
else if(expressionArgument is ReceiverExpressionKotlinCallArgument) {
|
val resolvedCall = smartCastDiagnostic.kotlinCall?.psiKotlinCall?.psiCall?.getResolvedCall(trace.bindingContext) as? NewResolvedCallImpl<*>
|
||||||
val receiverValue = expressionArgument.receiver.receiverValue
|
if (resolvedCall != null && smartCastResult != null) {
|
||||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, context)
|
if (resolvedCall.extensionReceiver == expressionArgument.receiver.receiverValue) {
|
||||||
SmartCastManager.checkAndRecordPossibleCast(
|
resolvedCall.updateExtensionReceiverWithSmartCastIfNeeded(smartCastResult.resultType)
|
||||||
dataFlowValue, smartCastDiagnostic.smartCastType, (receiverValue as? ExpressionReceiver)?.expression, context, call,
|
}
|
||||||
recordExpressionType = true)
|
if (resolvedCall.dispatchReceiver == expressionArgument.receiver.receiverValue) {
|
||||||
trace.markAsReported()
|
resolvedCall.setSmartCastDispatchReceiverType(smartCastResult.resultType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportUnstableSmartCast(unstableSmartCast: UnstableSmartCast) {
|
private fun reportUnstableSmartCast(unstableSmartCast: UnstableSmartCast) {
|
||||||
// todo hack -- remove it after removing SmartCastManager
|
// todo hack -- remove it after removing SmartCastManager
|
||||||
reportSmartCast(SmartCastDiagnostic(unstableSmartCast.argument, unstableSmartCast.targetType))
|
reportSmartCast(SmartCastDiagnostic(unstableSmartCast.argument, unstableSmartCast.targetType, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun constraintError(diagnostic: KotlinCallDiagnostic) {
|
override fun constraintError(diagnostic: KotlinCallDiagnostic) {
|
||||||
|
|||||||
+25
-6
@@ -45,6 +45,8 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
|
|||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.CastImplicitClassReceiver
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.NewCapturedType
|
import org.jetbrains.kotlin.types.checker.NewCapturedType
|
||||||
@@ -264,21 +266,22 @@ class KotlinToResolvedCallTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindAndReport(context: BasicCallResolutionContext, trace: BindingTrace, simpleResolvedCall: NewResolvedCallImpl<*>) {
|
private fun bindAndReport(context: BasicCallResolutionContext, trace: BindingTrace, simpleResolvedCall: NewResolvedCallImpl<*>) {
|
||||||
reportCallDiagnostic(context, trace, simpleResolvedCall.resolvedCallAtom, simpleResolvedCall.resultingDescriptor)
|
|
||||||
val tracing = simpleResolvedCall.resolvedCallAtom.atom.psiKotlinCall.tracingStrategy
|
val tracing = simpleResolvedCall.resolvedCallAtom.atom.psiKotlinCall.tracingStrategy
|
||||||
|
|
||||||
tracing.bindReference(trace, simpleResolvedCall)
|
tracing.bindReference(trace, simpleResolvedCall)
|
||||||
tracing.bindResolvedCall(trace, simpleResolvedCall)
|
tracing.bindResolvedCall(trace, simpleResolvedCall)
|
||||||
|
|
||||||
|
reportCallDiagnostic(context, trace, simpleResolvedCall.resolvedCallAtom, simpleResolvedCall.resultingDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindAndReport(context: BasicCallResolutionContext, trace: BindingTrace, variableAsFunction: NewVariableAsFunctionResolvedCallImpl) {
|
private fun bindAndReport(context: BasicCallResolutionContext, trace: BindingTrace, variableAsFunction: NewVariableAsFunctionResolvedCallImpl) {
|
||||||
reportCallDiagnostic(context, trace, variableAsFunction.variableCall.resolvedCallAtom, variableAsFunction.variableCall.resultingDescriptor)
|
|
||||||
reportCallDiagnostic(context, trace, variableAsFunction.functionCall.resolvedCallAtom, variableAsFunction.functionCall.resultingDescriptor)
|
|
||||||
|
|
||||||
val outerTracingStrategy = variableAsFunction.baseCall.tracingStrategy
|
val outerTracingStrategy = variableAsFunction.baseCall.tracingStrategy
|
||||||
outerTracingStrategy.bindReference(trace, variableAsFunction.variableCall)
|
outerTracingStrategy.bindReference(trace, variableAsFunction.variableCall)
|
||||||
outerTracingStrategy.bindResolvedCall(trace, variableAsFunction)
|
outerTracingStrategy.bindResolvedCall(trace, variableAsFunction)
|
||||||
variableAsFunction.functionCall.kotlinCall.psiKotlinCall.tracingStrategy.bindReference(trace, variableAsFunction.functionCall)
|
variableAsFunction.functionCall.kotlinCall.psiKotlinCall.tracingStrategy.bindReference(trace, variableAsFunction.functionCall)
|
||||||
|
|
||||||
|
reportCallDiagnostic(context, trace, variableAsFunction.variableCall.resolvedCallAtom, variableAsFunction.variableCall.resultingDescriptor)
|
||||||
|
reportCallDiagnostic(context, trace, variableAsFunction.functionCall.resolvedCallAtom, variableAsFunction.functionCall.resultingDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportCallDiagnostic(
|
private fun reportCallDiagnostic(
|
||||||
@@ -431,6 +434,9 @@ class NewResolvedCallImpl<D : CallableDescriptor>(
|
|||||||
TypeApproximator().approximateToSuperType(substituted, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: substituted
|
TypeApproximator().approximateToSuperType(substituted, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: substituted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var extensionReceiver = resolvedCallAtom.extensionReceiverArgument?.receiver?.receiverValue
|
||||||
|
private var smartCastDispatchReceiverType: KotlinType? = null
|
||||||
|
|
||||||
override val kotlinCall: KotlinCall get() = resolvedCallAtom.atom
|
override val kotlinCall: KotlinCall get() = resolvedCallAtom.atom
|
||||||
|
|
||||||
override fun getStatus(): ResolutionStatus = getResultApplicability(resolvedCallAtom.diagnostics).toResolutionStatus()
|
override fun getStatus(): ResolutionStatus = getResultApplicability(resolvedCallAtom.diagnostics).toResolutionStatus()
|
||||||
@@ -440,7 +446,7 @@ class NewResolvedCallImpl<D : CallableDescriptor>(
|
|||||||
|
|
||||||
override fun getCandidateDescriptor(): D = resolvedCallAtom.candidateDescriptor as D
|
override fun getCandidateDescriptor(): D = resolvedCallAtom.candidateDescriptor as D
|
||||||
override fun getResultingDescriptor(): D = resultingDescriptor as D
|
override fun getResultingDescriptor(): D = resultingDescriptor as D
|
||||||
override fun getExtensionReceiver(): ReceiverValue? = resolvedCallAtom.extensionReceiverArgument?.receiver?.receiverValue
|
override fun getExtensionReceiver(): ReceiverValue? = extensionReceiver
|
||||||
override fun getDispatchReceiver(): ReceiverValue? = resolvedCallAtom.dispatchReceiverArgument?.receiver?.receiverValue
|
override fun getDispatchReceiver(): ReceiverValue? = resolvedCallAtom.dispatchReceiverArgument?.receiver?.receiverValue
|
||||||
override fun getExplicitReceiverKind(): ExplicitReceiverKind = resolvedCallAtom.explicitReceiverKind
|
override fun getExplicitReceiverKind(): ExplicitReceiverKind = resolvedCallAtom.explicitReceiverKind
|
||||||
|
|
||||||
@@ -449,7 +455,20 @@ class NewResolvedCallImpl<D : CallableDescriptor>(
|
|||||||
return typeParameters.zip(typeArguments).toMap()
|
return typeParameters.zip(typeArguments).toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSmartCastDispatchReceiverType(): KotlinType? = null // todo
|
override fun getSmartCastDispatchReceiverType(): KotlinType? = smartCastDispatchReceiverType
|
||||||
|
|
||||||
|
fun updateExtensionReceiverWithSmartCastIfNeeded(smartCastExtensionReceiverType: KotlinType) {
|
||||||
|
if (extensionReceiver is ImplicitClassReceiver) {
|
||||||
|
extensionReceiver = CastImplicitClassReceiver(
|
||||||
|
(extensionReceiver as ImplicitClassReceiver).classDescriptor,
|
||||||
|
smartCastExtensionReceiverType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSmartCastDispatchReceiverType(smartCastDispatchReceiverType: KotlinType) {
|
||||||
|
this.smartCastDispatchReceiverType = smartCastDispatchReceiverType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ResolutionCandidateApplicability.toResolutionStatus(): ResolutionStatus = when (this) {
|
fun ResolutionCandidateApplicability.toResolutionStatus(): ResolutionStatus = when (this) {
|
||||||
|
|||||||
+8
-4
@@ -35,10 +35,14 @@ class AdditionalDiagnosticReporter {
|
|||||||
reportSmartCasts(candidate, resultingDescriptor, kotlinDiagnosticsHolder)
|
reportSmartCasts(candidate, resultingDescriptor, kotlinDiagnosticsHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSmartCastDiagnostic(argument: KotlinCallArgument, expectedResultType: UnwrappedType): SmartCastDiagnostic? {
|
private fun createSmartCastDiagnostic(
|
||||||
|
candidate: ResolvedCallAtom,
|
||||||
|
argument: KotlinCallArgument,
|
||||||
|
expectedResultType: UnwrappedType
|
||||||
|
): SmartCastDiagnostic? {
|
||||||
if (argument !is ExpressionKotlinCallArgument) return null
|
if (argument !is ExpressionKotlinCallArgument) return null
|
||||||
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(argument.receiver.receiverValue.type, expectedResultType)) {
|
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(argument.receiver.receiverValue.type, expectedResultType)) {
|
||||||
return SmartCastDiagnostic(argument, expectedResultType.unwrap())
|
return SmartCastDiagnostic(argument, expectedResultType.unwrap(), candidate.atom)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -51,7 +55,7 @@ class AdditionalDiagnosticReporter {
|
|||||||
if (receiver == null || parameter == null) return null
|
if (receiver == null || parameter == null) return null
|
||||||
val expectedType = parameter.type.unwrap().let { if (receiver.isSafeCall) it.makeNullableAsSpecified(true) else it }
|
val expectedType = parameter.type.unwrap().let { if (receiver.isSafeCall) it.makeNullableAsSpecified(true) else it }
|
||||||
|
|
||||||
val smartCastDiagnostic = createSmartCastDiagnostic(receiver, expectedType) ?: return null
|
val smartCastDiagnostic = createSmartCastDiagnostic(candidate, receiver, expectedType) ?: return null
|
||||||
|
|
||||||
// todo may be we have smart cast to Int?
|
// todo may be we have smart cast to Int?
|
||||||
return smartCastDiagnostic.takeIf {
|
return smartCastDiagnostic.takeIf {
|
||||||
@@ -75,7 +79,7 @@ class AdditionalDiagnosticReporter {
|
|||||||
|
|
||||||
for (parameter in resultingDescriptor.valueParameters) {
|
for (parameter in resultingDescriptor.valueParameters) {
|
||||||
for (argument in candidate.argumentMappingByOriginal[parameter.original]?.arguments ?: continue) {
|
for (argument in candidate.argumentMappingByOriginal[parameter.original]?.arguments ?: continue) {
|
||||||
val smartCastDiagnostic = createSmartCastDiagnostic(argument, argument.getExpectedType(parameter)) ?: continue
|
val smartCastDiagnostic = createSmartCastDiagnostic(candidate, argument, argument.getExpectedType(parameter)) ?: continue
|
||||||
|
|
||||||
val thereIsUnstableSmartCastError = candidate.diagnostics.filterIsInstance<UnstableSmartCast>().any {
|
val thereIsUnstableSmartCastError = candidate.diagnostics.filterIsInstance<UnstableSmartCast>().any {
|
||||||
it.argument == argument
|
it.argument == argument
|
||||||
|
|||||||
+1
-1
@@ -58,7 +58,7 @@ internal object CheckVisibility : ResolutionPart() {
|
|||||||
if (dispatchReceiverArgument is ExpressionKotlinCallArgument) {
|
if (dispatchReceiverArgument is ExpressionKotlinCallArgument) {
|
||||||
val smartCastReceiver = getReceiverValueWithSmartCast(receiverValue, dispatchReceiverArgument.receiver.stableType)
|
val smartCastReceiver = getReceiverValueWithSmartCast(receiverValue, dispatchReceiverArgument.receiver.stableType)
|
||||||
if (Visibilities.findInvisibleMember(smartCastReceiver, candidateDescriptor, containingDescriptor) == null) {
|
if (Visibilities.findInvisibleMember(smartCastReceiver, candidateDescriptor, containingDescriptor) == null) {
|
||||||
addDiagnostic(SmartCastDiagnostic(dispatchReceiverArgument, dispatchReceiverArgument.receiver.stableType))
|
addDiagnostic(SmartCastDiagnostic(dispatchReceiverArgument, dispatchReceiverArgument.receiver.stableType, resolvedCall.atom))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -128,7 +128,8 @@ class NotCallableExpectedType(
|
|||||||
// SmartCasts
|
// SmartCasts
|
||||||
class SmartCastDiagnostic(
|
class SmartCastDiagnostic(
|
||||||
val argument: ExpressionKotlinCallArgument,
|
val argument: ExpressionKotlinCallArgument,
|
||||||
val smartCastType: UnwrappedType
|
val smartCastType: UnwrappedType,
|
||||||
|
val kotlinCall: KotlinCall?
|
||||||
): KotlinCallDiagnostic(RESOLVED) {
|
): KotlinCallDiagnostic(RESOLVED) {
|
||||||
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
|
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user