FIR IDE: get unstable smartcast from FE1.0-based analysis API

Note that the current FE1.0 implementation still has some limitations

1. only references used as receiver are being considered for smartcast.
2. Somehow the smartcast information is not retained if the for generic
typed receivers. See multiSmartcastAsReceiver_stable.descriptors.txt
This commit is contained in:
Tianyu Geng
2021-11-03 10:39:16 -07:00
committed by Ilya Kirillov
parent 197079c85c
commit 7de0937031
6 changed files with 41 additions and 20 deletions
@@ -28,13 +28,17 @@ internal class KtFe10SmartCastProvider(override val analysisSession: KtFe10Analy
withValidityAssertion { withValidityAssertion {
val bindingContext = analysisSession.analyze(expression) val bindingContext = analysisSession.analyze(expression)
val stableSmartCasts = bindingContext[BindingContext.SMARTCAST, expression] val stableSmartCasts = bindingContext[BindingContext.SMARTCAST, expression]
val unstableSmartCasts = bindingContext[BindingContext.UNSTABLE_SMARTCAST, expression]
return when { return when {
stableSmartCasts != null -> { stableSmartCasts != null -> {
val type = stableSmartCasts.getKtType() ?: return null val type = stableSmartCasts.getKtType() ?: return null
SmartCastInfo(type, true) SmartCastInfo(type, true)
} }
// TODO: collect unstable smartcast here. unstableSmartCasts != null -> {
val type = unstableSmartCasts.getKtType() ?: return null
SmartCastInfo(type, false)
}
else -> null else -> null
} }
} }
@@ -1,3 +1,3 @@
expression: a expression: a
isStable: null isStable: false
smartCastType: null smartCastType: (A & B)
@@ -1,3 +1,3 @@
expression: a expression: a
isStable: null isStable: false
smartCastType: null smartCastType: kotlin.String
@@ -157,6 +157,7 @@ public interface BindingContext {
WritableSlice<KtCollectionLiteralExpression, ResolvedCall<FunctionDescriptor>> COLLECTION_LITERAL_CALL = Slices.createSimpleSlice(); WritableSlice<KtCollectionLiteralExpression, ResolvedCall<FunctionDescriptor>> COLLECTION_LITERAL_CALL = Slices.createSimpleSlice();
WritableSlice<KtExpression, ExplicitSmartCasts> SMARTCAST = new BasicWritableSlice<>(DO_NOTHING); WritableSlice<KtExpression, ExplicitSmartCasts> SMARTCAST = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<KtExpression, ExplicitSmartCasts> UNSTABLE_SMARTCAST = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<KtExpression, Boolean> SMARTCAST_NULL = Slices.createSimpleSlice(); WritableSlice<KtExpression, Boolean> SMARTCAST_NULL = Slices.createSimpleSlice();
WritableSlice<KtExpression, ImplicitSmartCasts> IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice<>(DO_NOTHING); WritableSlice<KtExpression, ImplicitSmartCasts> IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice<>(DO_NOTHING);
@@ -16,16 +16,19 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isNull import org.jetbrains.kotlin.psi.psiUtil.isNull
import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.util.*
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceExpectedTypeConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceExpectedTypeConstraintPosition
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.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.smartcasts.SingleSmartCast
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
import org.jetbrains.kotlin.resolve.calls.tower.* import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.resolve.calls.util.* import org.jetbrains.kotlin.resolve.calls.util.extractCallableReferenceExpression
import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.reportTrailingLambdaErrorOr
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstantChecker import org.jetbrains.kotlin.resolve.constants.CompileTimeConstantChecker
import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
@@ -358,10 +361,12 @@ class DiagnosticReporterByTrackingStrategy(
val argumentExpression = unstableSmartCast.argument.psiExpression ?: return val argumentExpression = unstableSmartCast.argument.psiExpression ?: return
require(possibleTypes.isNotEmpty()) { "Receiver for unstable smart cast without possible types" } require(possibleTypes.isNotEmpty()) { "Receiver for unstable smart cast without possible types" }
val intersectWrappedTypes = intersectWrappedTypes(possibleTypes)
trace.record(BindingContext.UNSTABLE_SMARTCAST, argumentExpression, SingleSmartCast(null, intersectWrappedTypes))
trace.report( trace.report(
SMARTCAST_IMPOSSIBLE.on( SMARTCAST_IMPOSSIBLE.on(
argumentExpression, argumentExpression,
intersectWrappedTypes(possibleTypes), intersectWrappedTypes,
argumentExpression.text, argumentExpression.text,
dataFlowValue.kind.description dataFlowValue.kind.description
) )
@@ -25,8 +25,7 @@ import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE
import org.jetbrains.kotlin.psi.Call import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST import org.jetbrains.kotlin.resolve.BindingContext.*
import org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
@@ -37,8 +36,8 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.intersectWrappedTypes import org.jetbrains.kotlin.types.checker.intersectWrappedTypes
import org.jetbrains.kotlin.types.typeUtil.expandIntersectionTypeIfNecessary import org.jetbrains.kotlin.types.typeUtil.expandIntersectionTypeIfNecessary
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
class SmartCastManager(private val argumentTypeResolver: ArgumentTypeResolver) { class SmartCastManager(private val argumentTypeResolver: ArgumentTypeResolver) {
@@ -232,23 +231,35 @@ class SmartCastManager(private val argumentTypeResolver: ArgumentTypeResolver) {
trace.report(Errors.DEPRECATED_SMARTCAST.on(expression, type, expression.text, dataFlowValue.kind.description)) trace.report(Errors.DEPRECATED_SMARTCAST.on(expression, type, expression.text, dataFlowValue.kind.description))
} }
val oldSmartCasts = trace[SMARTCAST, expression] updateSmartCast(trace, expression, call, type, SMARTCAST)
val newSmartCast = SingleSmartCast(call, type)
if (oldSmartCasts != null) {
val oldType = oldSmartCasts.type(call)
if (oldType != null && oldType != type) {
throw AssertionError("Rewriting key $call for smart cast on ${expression.text}")
}
}
trace.record(SMARTCAST, expression, oldSmartCasts?.let { it + newSmartCast } ?: newSmartCast)
if (recordExpressionType) { if (recordExpressionType) {
//TODO //TODO
//Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary? //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
trace.recordType(expression, type) trace.recordType(expression, type)
} }
} else { } else {
updateSmartCast(trace, expression, call, type, UNSTABLE_SMARTCAST)
trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.text, dataFlowValue.kind.description)) trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.text, dataFlowValue.kind.description))
} }
} }
private fun updateSmartCast(
trace: BindingTrace,
expression: KtExpression,
call: Call?,
type: KotlinType,
key: WritableSlice<KtExpression, ExplicitSmartCasts>?
) {
val oldSmartCasts = trace[key, expression]
val newSmartCast = SingleSmartCast(call, type)
if (oldSmartCasts != null) {
val oldType = oldSmartCasts.type(call)
if (oldType != null && oldType != type) {
throw AssertionError("Rewriting key $call for smart cast on ${expression.text}")
}
}
val updatedSmartCasts = oldSmartCasts?.let { it + newSmartCast } ?: newSmartCast
trace.record(key, expression, updatedSmartCasts)
}
} }
} }