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) =
expression.subjectExpression?.let { context.get(SMARTCAST, it) ?: context.getType(it) } ?: null
expression.subjectExpression?.let { context.get(SMARTCAST, it)?.defaultType ?: context.getType(it) } ?: null
@JvmStatic
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.smartcasts.DataFlowInfo;
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.constants.CompileTimeConstant;
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_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, ImplicitSmartCasts> IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice<KtExpression, ImplicitSmartCasts>(DO_NOTHING);
@@ -377,7 +377,8 @@ class CandidateResolver(
val spreadElement = argument.getSpreadElement()
if (spreadElement != null && !type.isFlexible() && type.isMarkedNullable) {
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) {
context.trace.report(Errors.SPREAD_OF_NULLABLE.on(spreadElement))
}
@@ -531,7 +532,7 @@ class CandidateResolver(
val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(
dataFlowValue, expectedReceiverParameterType,
{ possibleSmartCast -> isCandidateVisibleOrExtensionReceiver(receiverArgument, possibleSmartCast, isDispatchReceiver) },
expression, this, candidateCall.call.calleeExpression, recordExpressionType = true
expression, this, candidateCall.call, recordExpressionType = true
)
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.descriptors.DeclarationDescriptor
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.ValueArgument
import org.jetbrains.kotlin.resolve.BindingContext
@@ -114,11 +115,14 @@ class SmartCastManager {
type: KotlinType,
trace: BindingTrace,
dataFlowValue: DataFlowValue,
call: Call?,
recordExpressionType: Boolean
) {
if (KotlinBuiltIns.isNullableNothing(type)) return
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) {
//TODO
//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,
expression: KtExpression?,
c: ResolutionContext<*>,
calleeExpression: KtExpression?,
call: Call?,
recordExpressionType: Boolean
): SmartCastResult? {
return checkAndRecordPossibleCast(dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType)
return checkAndRecordPossibleCast(dataFlowValue, expectedType, null, expression, c, call, recordExpressionType)
}
fun checkAndRecordPossibleCast(
@@ -147,13 +151,14 @@ class SmartCastManager {
additionalPredicate: ((KotlinType) -> Boolean)?,
expression: KtExpression?,
c: ResolutionContext<*>,
calleeExpression: KtExpression?,
call: Call?,
recordExpressionType: Boolean
): SmartCastResult? {
val calleeExpression = call?.calleeExpression
for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue)) {
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) && (additionalPredicate == null || additionalPredicate(possibleType))) {
if (expression != null) {
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType)
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, call, recordExpressionType)
}
else if (calleeExpression != null && dataFlowValue.isStable) {
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 (!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 checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType)
return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, call, recordExpressionType)
}
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);
}
@TestMetadata("multipleResolvedCalls.kt")
public void testMultipleResolvedCalls() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/multipleResolvedCalls.kt");
doTest(fileName);
}
@TestMetadata("noErrorCheckForPackageLevelVal.kt")
public void testNoErrorCheckForPackageLevelVal() throws Exception {
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.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.scopes.receivers.ExtensionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
@@ -101,9 +102,19 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
val smartCast = bindingContext.get(SMARTCAST, expression)
if (smartCast != null) {
holder.createInfoAnnotation(getSmartCastTarget(expression),
"Smart cast to " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast))
.textAttributes = SMART_CAST_VALUE
val defaultType = smartCast.defaultType
if (defaultType != null) {
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)
@@ -160,7 +160,7 @@ private fun KtCodeFragment.markSmartCasts() {
val factory = KtPsiFactory(project)
getContentElement()?.forEachDescendantOfType<KtExpression> { expression ->
val smartCast = bindingContext.get(SMARTCAST, expression)
val smartCast = bindingContext.get(SMARTCAST, expression)?.defaultType
if (smartCast != null) {
val smartCastedExpression = factory.createExpressionByPattern(
"($0 as ${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast)})",
@@ -93,7 +93,7 @@ class AddFunctionParametersFix(
validator.addName(parameters[i].name.asString())
val argumentType = expression?.let {
val bindingContext = it.analyze()
bindingContext[BindingContext.SMARTCAST, it] ?: bindingContext.getType(it)
bindingContext[BindingContext.SMARTCAST, it]?.defaultType ?: bindingContext.getType(it)
}
val parameterType = parameters[i].type
@@ -210,7 +210,7 @@ data class ExtractionData(
val qualifiedExpression = newRef.getQualifiedExpressionForSelector()
if (qualifiedExpression != null) {
val smartCastTarget = originalResolveResult.originalRefExpr.parent as KtExpression
smartCast = originalContext[BindingContext.SMARTCAST, smartCastTarget]
smartCast = originalContext[BindingContext.SMARTCAST, smartCastTarget]?.defaultType
possibleTypes = getPossibleTypes(smartCastTarget, originalResolveResult.resolvedCall, originalContext)
val receiverDescriptor =
(originalResolveResult.resolvedCall?.dispatchReceiver as? ImplicitReceiver)?.declarationDescriptor
@@ -221,7 +221,7 @@ data class ExtractionData(
}
else {
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)
shouldSkipPrimaryReceiver = false
}
@@ -308,7 +308,7 @@ private fun suggestParameterType(
}
parameterExpression != null ->
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null)
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression]?.defaultType else null)
?: bindingContext.getType(parameterExpression)
?: (parameterExpression as? KtReferenceExpression)?.let {
(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);
}
@TestMetadata("multipleResolvedCalls.kt")
public void testMultipleResolvedCalls() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/multipleResolvedCalls.kt");
doTestWithInfos(fileName);
}
@TestMetadata("PropertiesWithBackingFields.kt")
public void testPropertiesWithBackingFields() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/PropertiesWithBackingFields.kt");