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 {
val bindingContext = analysisSession.analyze(expression)
val stableSmartCasts = bindingContext[BindingContext.SMARTCAST, expression]
val unstableSmartCasts = bindingContext[BindingContext.UNSTABLE_SMARTCAST, expression]
return when {
stableSmartCasts != null -> {
val type = stableSmartCasts.getKtType() ?: return null
SmartCastInfo(type, true)
}
// TODO: collect unstable smartcast here.
unstableSmartCasts != null -> {
val type = unstableSmartCasts.getKtType() ?: return null
SmartCastInfo(type, false)
}
else -> null
}
}
@@ -1,3 +1,3 @@
expression: a
isStable: null
smartCastType: null
isStable: false
smartCastType: (A & B)
@@ -1,3 +1,3 @@
expression: a
isStable: null
smartCastType: null
isStable: false
smartCastType: kotlin.String
@@ -157,6 +157,7 @@ public interface BindingContext {
WritableSlice<KtCollectionLiteralExpression, ResolvedCall<FunctionDescriptor>> COLLECTION_LITERAL_CALL = Slices.createSimpleSlice();
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, 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.lastBlockStatementOrThis
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.inference.BuilderInferenceExpectedTypeConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
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.tasks.TracingStrategy
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.TypedCompileTimeConstant
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
@@ -358,10 +361,12 @@ class DiagnosticReporterByTrackingStrategy(
val argumentExpression = unstableSmartCast.argument.psiExpression ?: return
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(
SMARTCAST_IMPOSSIBLE.on(
argumentExpression,
intersectWrappedTypes(possibleTypes),
intersectWrappedTypes,
argumentExpression.text,
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.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST
import org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST
import org.jetbrains.kotlin.resolve.BindingContext.*
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
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.checker.intersectWrappedTypes
import org.jetbrains.kotlin.types.typeUtil.expandIntersectionTypeIfNecessary
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
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))
}
val oldSmartCasts = trace[SMARTCAST, 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}")
}
}
trace.record(SMARTCAST, expression, oldSmartCasts?.let { it + newSmartCast } ?: newSmartCast)
updateSmartCast(trace, expression, call, type, SMARTCAST)
if (recordExpressionType) {
//TODO
//Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
trace.recordType(expression, type)
}
} else {
updateSmartCast(trace, expression, call, type, UNSTABLE_SMARTCAST)
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)
}
}
}