Store map (Call to KotlinType) for SMARTCAST slice #KT-13426 Fixed
Also EA-86848 Fixed
This commit is contained in:
@@ -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) {
|
||||
|
||||
+46
@@ -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))
|
||||
}
|
||||
+12
-7
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user