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");
|
||||
|
||||
+14
-3
@@ -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)
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user