From 7de0937031ff6669d3175a3727030f23bf34953e Mon Sep 17 00:00:00 2001 From: Tianyu Geng Date: Wed, 3 Nov 2021 10:39:16 -0700 Subject: [PATCH] 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 --- .../components/KtFe10SmartCastProvider.kt | 6 +++- ...artcastAsReceiver_unstable.descriptors.txt | 4 +-- .../smartcastAsReceiver_unstable.txt | 4 +-- .../kotlin/resolve/BindingContext.java | 1 + .../DiagnosticReporterByTrackingStrategy.kt | 11 ++++-- .../calls/smartcasts/SmartCastManager.kt | 35 ++++++++++++------- 6 files changed, 41 insertions(+), 20 deletions(-) diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SmartCastProvider.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SmartCastProvider.kt index 54cbcf009e4..75928249308 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SmartCastProvider.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SmartCastProvider.kt @@ -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 } } diff --git a/analysis/analysis-api/testData/components/smartCastInfo/multiSmartcastAsReceiver_unstable.descriptors.txt b/analysis/analysis-api/testData/components/smartCastInfo/multiSmartcastAsReceiver_unstable.descriptors.txt index 50cbb6e17c6..7a9d527ae14 100644 --- a/analysis/analysis-api/testData/components/smartCastInfo/multiSmartcastAsReceiver_unstable.descriptors.txt +++ b/analysis/analysis-api/testData/components/smartCastInfo/multiSmartcastAsReceiver_unstable.descriptors.txt @@ -1,3 +1,3 @@ expression: a -isStable: null -smartCastType: null +isStable: false +smartCastType: (A & B) diff --git a/analysis/analysis-api/testData/components/smartCastInfo/smartcastAsReceiver_unstable.txt b/analysis/analysis-api/testData/components/smartCastInfo/smartcastAsReceiver_unstable.txt index 50cbb6e17c6..7b477c75631 100644 --- a/analysis/analysis-api/testData/components/smartCastInfo/smartcastAsReceiver_unstable.txt +++ b/analysis/analysis-api/testData/components/smartCastInfo/smartcastAsReceiver_unstable.txt @@ -1,3 +1,3 @@ expression: a -isStable: null -smartCastType: null +isStable: false +smartCastType: kotlin.String diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index b41d8358a78..5d88ede8299 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -157,6 +157,7 @@ public interface BindingContext { WritableSlice> COLLECTION_LITERAL_CALL = Slices.createSimpleSlice(); WritableSlice SMARTCAST = new BasicWritableSlice<>(DO_NOTHING); + WritableSlice UNSTABLE_SMARTCAST = new BasicWritableSlice<>(DO_NOTHING); WritableSlice SMARTCAST_NULL = Slices.createSimpleSlice(); WritableSlice IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice<>(DO_NOTHING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index cdeb86f368c..a00b4793e10 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -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 ) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt index f6c39dbbc8e..d33ac250c31 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt @@ -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? + ) { + 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) + } } }