Store map (Call to KotlinType) for SMARTCAST slice #KT-13426 Fixed

Also EA-86848 Fixed
This commit is contained in:
Mikhail Glukhikh
2016-08-16 11:31:52 +03:00
parent dbff2be6fa
commit ae856e834a
15 changed files with 171 additions and 19 deletions
@@ -297,7 +297,7 @@ object WhenChecker {
} }
private fun whenSubjectType(expression: KtWhenExpression, context: BindingContext) = private fun whenSubjectType(expression: KtWhenExpression, context: BindingContext) =
expression.subjectExpression?.let { context.get(SMARTCAST, it) ?: context.getType(it) } ?: null expression.subjectExpression?.let { context.get(SMARTCAST, it)?.defaultType ?: context.getType(it) } ?: null
@JvmStatic @JvmStatic
fun getEnumMissingCases( fun getEnumMissingCases(
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.smartcasts.ExplicitSmartCasts;
import org.jetbrains.kotlin.resolve.calls.smartcasts.ImplicitSmartCasts; import org.jetbrains.kotlin.resolve.calls.smartcasts.ImplicitSmartCasts;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics; import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
@@ -143,7 +144,7 @@ public interface BindingContext {
WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> INDEXED_LVALUE_GET = Slices.createSimpleSlice(); WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> INDEXED_LVALUE_GET = Slices.createSimpleSlice();
WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> INDEXED_LVALUE_SET = Slices.createSimpleSlice(); WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> INDEXED_LVALUE_SET = Slices.createSimpleSlice();
WritableSlice<KtExpression, KotlinType> SMARTCAST = Slices.createSimpleSlice(); WritableSlice<KtExpression, ExplicitSmartCasts> SMARTCAST = new BasicWritableSlice<KtExpression, ExplicitSmartCasts>(DO_NOTHING);
WritableSlice<KtExpression, Boolean> SMARTCAST_NULL = Slices.createSimpleSlice(); WritableSlice<KtExpression, Boolean> SMARTCAST_NULL = Slices.createSimpleSlice();
WritableSlice<KtExpression, ImplicitSmartCasts> IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice<KtExpression, ImplicitSmartCasts>(DO_NOTHING); WritableSlice<KtExpression, ImplicitSmartCasts> IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice<KtExpression, ImplicitSmartCasts>(DO_NOTHING);
@@ -377,7 +377,8 @@ class CandidateResolver(
val spreadElement = argument.getSpreadElement() val spreadElement = argument.getSpreadElement()
if (spreadElement != null && !type.isFlexible() && type.isMarkedNullable) { if (spreadElement != null && !type.isFlexible() && type.isMarkedNullable) {
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context)
val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(dataFlowValue, expectedType, expression, context, calleeExpression = null, recordExpressionType = false) val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(dataFlowValue, expectedType, expression, context,
call = null, recordExpressionType = false)
if (smartCastResult == null || !smartCastResult.isCorrect) { if (smartCastResult == null || !smartCastResult.isCorrect) {
context.trace.report(Errors.SPREAD_OF_NULLABLE.on(spreadElement)) context.trace.report(Errors.SPREAD_OF_NULLABLE.on(spreadElement))
} }
@@ -531,7 +532,7 @@ class CandidateResolver(
val smartCastResult = SmartCastManager.checkAndRecordPossibleCast( val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(
dataFlowValue, expectedReceiverParameterType, dataFlowValue, expectedReceiverParameterType,
{ possibleSmartCast -> isCandidateVisibleOrExtensionReceiver(receiverArgument, possibleSmartCast, isDispatchReceiver) }, { possibleSmartCast -> isCandidateVisibleOrExtensionReceiver(receiverArgument, possibleSmartCast, isDispatchReceiver) },
expression, this, candidateCall.call.calleeExpression, recordExpressionType = true expression, this, candidateCall.call, recordExpressionType = true
) )
if (smartCastResult == null) { if (smartCastResult == null) {
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.calls.smartcasts
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.types.KotlinType
interface ExplicitSmartCasts {
fun type(call: Call?): KotlinType?
val defaultType: KotlinType?
operator fun plus(smartCast: SingleSmartCast): ExplicitSmartCasts
}
data class SingleSmartCast(val call: Call?, val type: KotlinType) : ExplicitSmartCasts {
override fun type(call: Call?) = if (call == this.call) type else null
override val defaultType: KotlinType get() = type
override fun plus(smartCast: SingleSmartCast) =
if (this == smartCast) this
else MultipleSmartCasts(mapOf(call to type, smartCast.call to smartCast.type))
}
data class MultipleSmartCasts internal constructor(val map: Map<Call?, KotlinType>) : ExplicitSmartCasts {
override fun type(call: Call?) = map[call]
override val defaultType: KotlinType? get() = null
override fun plus(smartCast: SingleSmartCast) = MultipleSmartCasts(map + mapOf(smartCast.call to smartCast.type))
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -114,11 +115,14 @@ class SmartCastManager {
type: KotlinType, type: KotlinType,
trace: BindingTrace, trace: BindingTrace,
dataFlowValue: DataFlowValue, dataFlowValue: DataFlowValue,
call: Call?,
recordExpressionType: Boolean recordExpressionType: Boolean
) { ) {
if (KotlinBuiltIns.isNullableNothing(type)) return if (KotlinBuiltIns.isNullableNothing(type)) return
if (dataFlowValue.isStable) { if (dataFlowValue.isStable) {
trace.record(SMARTCAST, expression, type) val oldSmartCasts = trace[SMARTCAST, expression]
val newSmartCast = SingleSmartCast(call, type)
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?
@@ -135,10 +139,10 @@ class SmartCastManager {
expectedType: KotlinType, expectedType: KotlinType,
expression: KtExpression?, expression: KtExpression?,
c: ResolutionContext<*>, c: ResolutionContext<*>,
calleeExpression: KtExpression?, call: Call?,
recordExpressionType: Boolean recordExpressionType: Boolean
): SmartCastResult? { ): SmartCastResult? {
return checkAndRecordPossibleCast(dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType) return checkAndRecordPossibleCast(dataFlowValue, expectedType, null, expression, c, call, recordExpressionType)
} }
fun checkAndRecordPossibleCast( fun checkAndRecordPossibleCast(
@@ -147,13 +151,14 @@ class SmartCastManager {
additionalPredicate: ((KotlinType) -> Boolean)?, additionalPredicate: ((KotlinType) -> Boolean)?,
expression: KtExpression?, expression: KtExpression?,
c: ResolutionContext<*>, c: ResolutionContext<*>,
calleeExpression: KtExpression?, call: Call?,
recordExpressionType: Boolean recordExpressionType: Boolean
): SmartCastResult? { ): SmartCastResult? {
val calleeExpression = call?.calleeExpression
for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue)) { for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue)) {
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) && (additionalPredicate == null || additionalPredicate(possibleType))) { if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) && (additionalPredicate == null || additionalPredicate(possibleType))) {
if (expression != null) { if (expression != null) {
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType) recordCastOrError(expression, possibleType, c.trace, dataFlowValue, call, recordExpressionType)
} }
else if (calleeExpression != null && dataFlowValue.isStable) { else if (calleeExpression != null && dataFlowValue.isStable) {
val receiver = (dataFlowValue.identifierInfo as? IdentifierInfo.Receiver)?.value val receiver = (dataFlowValue.identifierInfo as? IdentifierInfo.Receiver)?.value
@@ -188,12 +193,12 @@ class SmartCastManager {
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.type, nullableExpectedType) && (additionalPredicate == null || additionalPredicate(dataFlowValue.type))) { if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.type, nullableExpectedType) && (additionalPredicate == null || additionalPredicate(dataFlowValue.type))) {
if (!immanentlyNotNull && expression != null) { if (!immanentlyNotNull && expression != null) {
recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, recordExpressionType) recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, call, recordExpressionType)
} }
return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isStable) return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isStable)
} }
return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType) return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, call, recordExpressionType)
} }
return null return null
@@ -0,0 +1,19 @@
interface I1
interface I2
interface I3
interface I4
interface I5
operator fun I1.component1() = 1
operator fun I2.component2() = 2
operator fun I3.component3() = 3
operator fun I4.component4() = 4
operator fun I5.component5() = 5
fun test(x: Any): Int {
if (x is I1 && x is I2 && x is I3 && x is I4 && x is I5) {
val (t1, t2, t3, t4, t5) = <!DEBUG_INFO_SMARTCAST!>x<!>
return t1 + t2 + t3 + t4 + t5
}
else return 0
}
@@ -0,0 +1,38 @@
package
public fun test(/*0*/ x: kotlin.Any): kotlin.Int
public operator fun I1.component1(): kotlin.Int
public operator fun I2.component2(): kotlin.Int
public operator fun I3.component3(): kotlin.Int
public operator fun I4.component4(): kotlin.Int
public operator fun I5.component5(): kotlin.Int
public interface I1 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I2 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I3 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I4 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I5 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -17577,6 +17577,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("multipleResolvedCalls.kt")
public void testMultipleResolvedCalls() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/multipleResolvedCalls.kt");
doTest(fileName);
}
@TestMetadata("noErrorCheckForPackageLevelVal.kt") @TestMetadata("noErrorCheckForPackageLevelVal.kt")
public void testNoErrorCheckForPackageLevelVal() throws Exception { public void testNoErrorCheckForPackageLevelVal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/noErrorCheckForPackageLevelVal.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/noErrorCheckForPackageLevelVal.kt");
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.* import org.jetbrains.kotlin.resolve.BindingContext.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.MultipleSmartCasts
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
@@ -101,9 +102,19 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
val smartCast = bindingContext.get(SMARTCAST, expression) val smartCast = bindingContext.get(SMARTCAST, expression)
if (smartCast != null) { if (smartCast != null) {
holder.createInfoAnnotation(getSmartCastTarget(expression), val defaultType = smartCast.defaultType
"Smart cast to " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast)) if (defaultType != null) {
.textAttributes = SMART_CAST_VALUE holder.createInfoAnnotation(getSmartCastTarget(expression),
"Smart cast to " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(defaultType))
.textAttributes = SMART_CAST_VALUE
}
else if (smartCast is MultipleSmartCasts) {
for ((call, type) in smartCast.map) {
holder.createInfoAnnotation(getSmartCastTarget(expression),
"Smart cast to ${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type)} (for $call call)")
.textAttributes = SMART_CAST_VALUE
}
}
} }
super.visitExpression(expression) super.visitExpression(expression)
@@ -160,7 +160,7 @@ private fun KtCodeFragment.markSmartCasts() {
val factory = KtPsiFactory(project) val factory = KtPsiFactory(project)
getContentElement()?.forEachDescendantOfType<KtExpression> { expression -> getContentElement()?.forEachDescendantOfType<KtExpression> { expression ->
val smartCast = bindingContext.get(SMARTCAST, expression) val smartCast = bindingContext.get(SMARTCAST, expression)?.defaultType
if (smartCast != null) { if (smartCast != null) {
val smartCastedExpression = factory.createExpressionByPattern( val smartCastedExpression = factory.createExpressionByPattern(
"($0 as ${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast)})", "($0 as ${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast)})",
@@ -93,7 +93,7 @@ class AddFunctionParametersFix(
validator.addName(parameters[i].name.asString()) validator.addName(parameters[i].name.asString())
val argumentType = expression?.let { val argumentType = expression?.let {
val bindingContext = it.analyze() val bindingContext = it.analyze()
bindingContext[BindingContext.SMARTCAST, it] ?: bindingContext.getType(it) bindingContext[BindingContext.SMARTCAST, it]?.defaultType ?: bindingContext.getType(it)
} }
val parameterType = parameters[i].type val parameterType = parameters[i].type
@@ -210,7 +210,7 @@ data class ExtractionData(
val qualifiedExpression = newRef.getQualifiedExpressionForSelector() val qualifiedExpression = newRef.getQualifiedExpressionForSelector()
if (qualifiedExpression != null) { if (qualifiedExpression != null) {
val smartCastTarget = originalResolveResult.originalRefExpr.parent as KtExpression val smartCastTarget = originalResolveResult.originalRefExpr.parent as KtExpression
smartCast = originalContext[BindingContext.SMARTCAST, smartCastTarget] smartCast = originalContext[BindingContext.SMARTCAST, smartCastTarget]?.defaultType
possibleTypes = getPossibleTypes(smartCastTarget, originalResolveResult.resolvedCall, originalContext) possibleTypes = getPossibleTypes(smartCastTarget, originalResolveResult.resolvedCall, originalContext)
val receiverDescriptor = val receiverDescriptor =
(originalResolveResult.resolvedCall?.dispatchReceiver as? ImplicitReceiver)?.declarationDescriptor (originalResolveResult.resolvedCall?.dispatchReceiver as? ImplicitReceiver)?.declarationDescriptor
@@ -221,7 +221,7 @@ data class ExtractionData(
} }
else { else {
if (newRef.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } != null) continue if (newRef.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } != null) continue
smartCast = originalContext[BindingContext.SMARTCAST, originalResolveResult.originalRefExpr] smartCast = originalContext[BindingContext.SMARTCAST, originalResolveResult.originalRefExpr]?.defaultType
possibleTypes = getPossibleTypes(originalResolveResult.originalRefExpr, originalResolveResult.resolvedCall, originalContext) possibleTypes = getPossibleTypes(originalResolveResult.originalRefExpr, originalResolveResult.resolvedCall, originalContext)
shouldSkipPrimaryReceiver = false shouldSkipPrimaryReceiver = false
} }
@@ -308,7 +308,7 @@ private fun suggestParameterType(
} }
parameterExpression != null -> parameterExpression != null ->
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null) (if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression]?.defaultType else null)
?: bindingContext.getType(parameterExpression) ?: bindingContext.getType(parameterExpression)
?: (parameterExpression as? KtReferenceExpression)?.let { ?: (parameterExpression as? KtReferenceExpression)?.let {
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType (bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
+19
View File
@@ -0,0 +1,19 @@
interface I1
interface I2
interface I3
interface I4
interface I5
<info descr="null">operator</info> fun I1.component1() = 1
<info descr="null">operator</info> fun I2.component2() = 2
<info descr="null">operator</info> fun I3.component3() = 3
<info descr="null">operator</info> fun I4.component4() = 4
<info descr="null">operator</info> fun I5.component5() = 5
fun test(x: Any): Int {
if (x is I1 && x is I2 && x is I3 && x is I4 && x is I5) {
val (t1, t2, t3, t4, t5) = <info descr="Smart cast to I1 (for t1 call)"><info descr="Smart cast to I2 (for t2 call)"><info descr="Smart cast to I3 (for t3 call)"><info descr="Smart cast to I4 (for t4 call)"><info descr="Smart cast to I5 (for t5 call)">x</info></info></info></info></info>
return t1 + t2 + t3 + t4 + t5
}
else return 0
}
@@ -886,6 +886,12 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
doTestWithInfos(fileName); doTestWithInfos(fileName);
} }
@TestMetadata("multipleResolvedCalls.kt")
public void testMultipleResolvedCalls() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/multipleResolvedCalls.kt");
doTestWithInfos(fileName);
}
@TestMetadata("PropertiesWithBackingFields.kt") @TestMetadata("PropertiesWithBackingFields.kt")
public void testPropertiesWithBackingFields() throws Exception { public void testPropertiesWithBackingFields() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/PropertiesWithBackingFields.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/PropertiesWithBackingFields.kt");