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) + } } }