From 9001b9bcc0bb06e12dd3cf1ade83bb4ce1e7bff2 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 18 Jul 2016 15:23:48 +0300 Subject: [PATCH] DataFlowValue & DataFlowValueFactory major refactoring, get rid of DataFlowValue.id : Any? Instead, DataFlowValue now requires IdentifierInfo implementation to be created DataFlowValue is compared using IdentifierInfo --- .../calls/smartcasts/DataFlowValueFactory.kt | 121 ++++++++---------- .../expressions/PreliminaryLoopVisitor.kt | 8 +- .../resolve/calls/smartcasts/DataFlowValue.kt | 26 ++-- .../calls/smartcasts/IdentifierInfo.kt | 104 +++++++++++++++ .../DataFlowInfoUtilForCompletion.kt | 19 +-- .../kotlin/idea/core/SmartCastCalculator.kt | 37 +++--- 6 files changed, 205 insertions(+), 110 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt index 730ef130ce2..f1653c264cb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt @@ -16,31 +16,32 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts -import com.intellij.openapi.util.Pair import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.psi.psiUtil.before import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.calls.callUtil.* -import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind -import org.jetbrains.kotlin.resolve.descriptorUtil.* -import org.jetbrains.kotlin.resolve.scopes.receivers.* -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils -import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor - -import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.* +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor /** * This class is intended to create data flow values for different kind of expressions. @@ -88,9 +89,8 @@ object DataFlowValueFactory { // // But there are some problem with types built on type parameters, e.g. // fun foo(x: T) = x!!.hashCode() // there no way in type system to denote that `x!!` is not nullable - return DataFlowValue(expression, + return DataFlowValue(ExpressionIdentifierInfo(expression, false), type, - OTHER, Nullability.NOT_NULL) } @@ -99,17 +99,11 @@ object DataFlowValueFactory { } val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule) - return DataFlowValue(if (result === NO_IDENTIFIER_INFO) expression else result.id, - type, - result.kind, - type.immanentNullability) + return DataFlowValue(if (result === IdentifierInfo.NO) ExpressionIdentifierInfo(expression, false) else result, type) } @JvmStatic - fun createDataFlowValueForStableReceiver(receiver: ReceiverValue): DataFlowValue { - val type = receiver.type - return DataFlowValue(receiver, type, STABLE_VALUE, type.immanentNullability) - } + fun createDataFlowValueForStableReceiver(receiver: ReceiverValue) = DataFlowValue(IdentifierInfo.Receiver(receiver), receiver.type) @JvmStatic fun createDataFlowValue( @@ -137,52 +131,38 @@ object DataFlowValueFactory { variableDescriptor: VariableDescriptor, bindingContext: BindingContext, usageContainingModule: ModuleDescriptor? - ): DataFlowValue { - val type = variableDescriptor.type - return DataFlowValue(variableDescriptor, type, - variableKind(variableDescriptor, usageContainingModule, - bindingContext, property), - type.immanentNullability) - } + ) = DataFlowValue(IdentifierInfo.Variable(variableDescriptor, + variableKind(variableDescriptor, usageContainingModule, bindingContext, property)), + variableDescriptor.type) private fun createDataFlowValueForComplexExpression( expression: KtExpression, type: KotlinType - ) = DataFlowValue(expression, type, Kind.STABLE_COMPLEX_EXPRESSION, type.immanentNullability) + ) = DataFlowValue(ExpressionIdentifierInfo(expression, true), type) - private val KotlinType.immanentNullability: Nullability - get() = if (TypeUtils.isNullableType(this)) Nullability.UNKNOWN else Nullability.NOT_NULL + private data class PostfixIdentifierInfo(val argumentInfo: IdentifierInfo) : IdentifierInfo { + override val kind: DataFlowValue.Kind get() = argumentInfo.kind - private open class IdentifierInfo internal constructor(val id: Any?, val kind: Kind, val isPackage: Boolean) - - private val NO_IDENTIFIER_INFO = object : IdentifierInfo(null, OTHER, false) { - override fun toString() = "NO_IDENTIFIER_INFO" + override fun toString() = "$argumentInfo (postfix)" } - private fun createInfo(id: Any, kind: Kind) = IdentifierInfo(id, kind, false) + class ExpressionIdentifierInfo(val expression: KtExpression, isComplex: Boolean) : IdentifierInfo { - private fun createStableInfo(id: Any) = createInfo(id, STABLE_VALUE) + override val kind = if (isComplex) STABLE_COMPLEX_EXPRESSION else OTHER + + override fun equals(other: Any?) = other is ExpressionIdentifierInfo && expression == other.expression - private fun createPackageOrClassInfo(id: Any) = IdentifierInfo(id, STABLE_VALUE, true) + override fun hashCode() = expression.hashCode() - private fun combineInfo(receiverInfo: IdentifierInfo?, selectorInfo: IdentifierInfo) = - if (selectorInfo.id == null || receiverInfo === NO_IDENTIFIER_INFO) { - NO_IDENTIFIER_INFO - } - else if (receiverInfo == null || receiverInfo.isPackage) { - selectorInfo + override fun toString() = expression.text ?: "(empty expression)" + } + + private fun postfix(argumentInfo: IdentifierInfo) = + if (argumentInfo == IdentifierInfo.NO) { + IdentifierInfo.NO } else { - createInfo(Pair.create(receiverInfo.id, selectorInfo.id), - if (receiverInfo.kind.isStable()) selectorInfo.kind else OTHER) - } - - private fun createPostfixInfo(expression: KtPostfixExpression, argumentInfo: IdentifierInfo) = - if (argumentInfo === NO_IDENTIFIER_INFO) { - NO_IDENTIFIER_INFO - } - else { - createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.kind) + PostfixIdentifierInfo(argumentInfo) } private fun getIdForStableIdentifier( @@ -203,7 +183,7 @@ object DataFlowValueFactory { val receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule) val selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule) - combineInfo(receiverId, selectorId) + IdentifierInfo.qualified(receiverId, selectorId, expression.operationSign === KtTokens.SAFE_ACCESS) } is KtSimpleNameExpression -> getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule) @@ -214,14 +194,13 @@ object DataFlowValueFactory { is KtPostfixExpression -> { val operationType = expression.operationReference.getReferencedNameElementType() if (operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS) { - createPostfixInfo(expression, - getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule)) + postfix(getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule)) } else { - NO_IDENTIFIER_INFO + IdentifierInfo.NO } } - else -> NO_IDENTIFIER_INFO + else -> IdentifierInfo.NO } } @@ -242,12 +221,14 @@ object DataFlowValueFactory { val usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule) val receiverInfo = resolvedCall?.let { getIdForImplicitReceiver(it.dispatchReceiver, simpleNameExpression) } - combineInfo(receiverInfo, createInfo(declarationDescriptor, - variableKind(declarationDescriptor, usageModuleDescriptor, - bindingContext, simpleNameExpression))) + IdentifierInfo.qualified(receiverInfo, + IdentifierInfo.Variable(declarationDescriptor, + variableKind(declarationDescriptor, usageModuleDescriptor, + bindingContext, simpleNameExpression)), + resolvedCall?.call?.isSafeCall() ?: false) } - is PackageViewDescriptor, is ClassDescriptor -> createPackageOrClassInfo(declarationDescriptor) - else -> NO_IDENTIFIER_INFO + is PackageViewDescriptor, is ClassDescriptor -> IdentifierInfo.PackageOrClass(declarationDescriptor) + else -> IdentifierInfo.NO } } @@ -263,10 +244,10 @@ object DataFlowValueFactory { is CallableDescriptor -> { val receiverParameter = descriptorOfThisReceiver.extensionReceiverParameter ?: error("'This' refers to the callable member without a receiver parameter: $descriptorOfThisReceiver") - createStableInfo(receiverParameter.value) + IdentifierInfo.Receiver(receiverParameter.value) } - is ClassDescriptor -> createStableInfo(descriptorOfThisReceiver.thisAsReceiverParameter.value) - else -> NO_IDENTIFIER_INFO + is ClassDescriptor -> IdentifierInfo.Receiver(descriptorOfThisReceiver.thisAsReceiverParameter.value) + else -> IdentifierInfo.NO } private fun getVariableContainingDeclaration(variableDescriptor: VariableDescriptor): DeclarationDescriptor { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt index b3acae017b5..9d3d9ecbb89 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.psi.KtLoopExpression import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue +import org.jetbrains.kotlin.resolve.calls.smartcasts.IdentifierInfo import java.util.* /** @@ -34,9 +35,10 @@ class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher() val valueSetToClear = LinkedHashSet() for (value in nullabilityMap.keys) { // Only predictable variables are under interest here - val id = value.id - if (value.kind == DataFlowValue.Kind.PREDICTABLE_VARIABLE && id is LocalVariableDescriptor) { - if (hasWriters(id)) { + val identifierInfo = value.identifierInfo + if (value.kind == DataFlowValue.Kind.PREDICTABLE_VARIABLE && identifierInfo is IdentifierInfo.Variable) { + val variableDescriptor = identifierInfo.variable + if (variableDescriptor is LocalVariableDescriptor && hasWriters(variableDescriptor)) { valueSetToClear.add(value) } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.kt index 7b5f6a7c538..adedfc61192 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.kt @@ -19,12 +19,20 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils + +private val KotlinType.immanentNullability: Nullability + get() = if (TypeUtils.isNullableType(this)) Nullability.UNKNOWN else Nullability.NOT_NULL /** * This class describes an arbitrary object which has some value in data flow analysis. * In general case it's some r-value. */ -class DataFlowValue(val id: Any?, val type: KotlinType, val kind: DataFlowValue.Kind, val immanentNullability: Nullability) { +class DataFlowValue(val identifierInfo: IdentifierInfo, + val type: KotlinType, + val immanentNullability: Nullability = type.immanentNullability) { + + val kind: Kind get() = identifierInfo.kind enum class Kind(private val str: String, val description: String = str) { // Local value, or parameter, or private / internal member value without open / custom getter, @@ -69,32 +77,26 @@ class DataFlowValue(val id: Any?, val type: KotlinType, val kind: DataFlowValue. if (this === other) return true if (other !is DataFlowValue) return false - if (kind.isStable() != other.kind.isStable()) return false - if (id != other.id) return false + if (identifierInfo != other.identifierInfo) return false if (type != other.type) return false return true } - override fun toString(): String { - return kind.toString() + " " + id?.toString() + " " + immanentNullability - } + override fun toString() = "$kind $identifierInfo $immanentNullability" override fun hashCode(): Int { - var result = if (kind.isStable()) 1 else 0 + var result = identifierInfo.hashCode() result = 31 * result + type.hashCode() - result = 31 * result + (id?.hashCode() ?: 0) return result } companion object { @JvmStatic - fun nullValue(builtIns: KotlinBuiltIns) = DataFlowValue( - Object(), builtIns.nullableNothingType, Kind.OTHER, Nullability.NULL - ) + fun nullValue(builtIns: KotlinBuiltIns) = DataFlowValue(IdentifierInfo.NULL, builtIns.nullableNothingType, Nullability.NULL) @JvmField - val ERROR = DataFlowValue(Object(), ErrorUtils.createErrorType("Error type for data flow"), Kind.OTHER, Nullability.IMPOSSIBLE) + val ERROR = DataFlowValue(IdentifierInfo.ERROR, ErrorUtils.createErrorType("Error type for data flow"), Nullability.IMPOSSIBLE) } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt new file mode 100644 index 00000000000..41372cb104f --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt @@ -0,0 +1,104 @@ +/* + * 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.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PackageViewDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.OTHER +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.STABLE_VALUE +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue + +interface IdentifierInfo { + + val kind: DataFlowValue.Kind get() = OTHER + + object NO : IdentifierInfo { + override fun toString() = "NO_IDENTIFIER_INFO" + } + + object NULL : IdentifierInfo { + override fun toString() = "NULL" + } + + object ERROR : IdentifierInfo { + override fun toString() = "ERROR" + } + + class Variable(val variable: VariableDescriptor, override val kind: DataFlowValue.Kind) : IdentifierInfo { + + override fun equals(other: Any?) = + other is Variable && variable == other.variable && kind.isStable() == other.kind.isStable() + + override fun hashCode(): Int { + var result = if (kind.isStable()) 1 else 0 + result = 31 * result + variable.hashCode() + return result + } + + override fun toString() = variable.toString() + } + + data class Receiver(val value: ReceiverValue) : IdentifierInfo { + + override val kind = STABLE_VALUE + + override fun toString() = value.toString() + } + + data class PackageOrClass(val descriptor: DeclarationDescriptor) : IdentifierInfo { + + override val kind = STABLE_VALUE + + override fun toString() = descriptor.toString() + } + + class Qualified( + val receiverInfo: IdentifierInfo, val selectorInfo: IdentifierInfo, val safe: Boolean + ) : IdentifierInfo { + override val kind: DataFlowValue.Kind get() = if (receiverInfo.kind.isStable()) selectorInfo.kind else OTHER + + override fun equals(other: Any?) = other is Qualified && receiverInfo == other.receiverInfo && selectorInfo == other.selectorInfo + + override fun hashCode(): Int { + var result = receiverInfo.hashCode() + result = 31 * result + selectorInfo.hashCode() + return result + } + + override fun toString() = "$receiverInfo(?).$selectorInfo" + } + + companion object { + + fun qualified(receiverInfo: IdentifierInfo?, selectorInfo: IdentifierInfo, safe: Boolean): IdentifierInfo { + return if (selectorInfo == NO || receiverInfo === NO) { + NO + } + else if (receiverInfo == null || receiverInfo is PackageOrClass) { + selectorInfo + } + else { + Qualified(receiverInfo, selectorInfo, safe) + } + } + } +} \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DataFlowInfoUtilForCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DataFlowInfoUtilForCompletion.kt index ac3b66582a7..d50efdf57e6 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DataFlowInfoUtilForCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DataFlowInfoUtilForCompletion.kt @@ -21,20 +21,23 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.PackageViewDescriptor +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.calls.smartcasts.IdentifierInfo fun renderDataFlowValue(value: DataFlowValue): String? { // If it is not a stable identifier, there's no point in rendering it if (!value.isPredictable) return null - fun renderId(id: Any?): String? { - return when (id) { - is KtExpression -> id.text - is ImplicitReceiver -> "this@${id.declarationDescriptor.name}" - is VariableDescriptor -> id.name.asString() - is PackageViewDescriptor -> id.fqName.asString() - is com.intellij.openapi.util.Pair<*, *> -> renderId(id.first) + "." + renderId(id.second) + fun renderId(identifierInfo: IdentifierInfo): String? = with (identifierInfo) { + when (this) { + is DataFlowValueFactory.ExpressionIdentifierInfo -> expression.text + is IdentifierInfo.Receiver -> (this.value as? ImplicitReceiver)?.declarationDescriptor?.name?.let { "this@$it" } + is IdentifierInfo.Variable -> variable.name.asString() + is IdentifierInfo.PackageOrClass -> (descriptor as? PackageViewDescriptor)?.let { it.fqName.asString() } + is IdentifierInfo.Qualified -> renderId(receiverInfo) + "." + renderId(selectorInfo) else -> null } } - return renderId(value.id) + + return renderId(value.identifierInfo) } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/SmartCastCalculator.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/SmartCastCalculator.kt index 84bc48b3c83..58c61cb795f 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/SmartCastCalculator.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/SmartCastCalculator.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.idea.core -import com.intellij.openapi.util.Pair import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -28,10 +27,7 @@ import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory -import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability +import org.jetbrains.kotlin.resolve.calls.smartcasts.* import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver import org.jetbrains.kotlin.types.KotlinType @@ -85,24 +81,31 @@ class SmartCastCalculator( val dataFlowValueToEntity: (DataFlowValue) -> Any? if (receiver != null) { val receiverType = bindingContext.getType(receiver) ?: return emptyMap() - val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext, containingDeclarationOrModule).id + val receiverIdentifierInfo = DataFlowValueFactory.createDataFlowValue( + receiver, receiverType, bindingContext, containingDeclarationOrModule + ).identifierInfo dataFlowValueToEntity = { value -> - val id = value.id - if (id is Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null + val identifierInfo = value.identifierInfo + if (identifierInfo is IdentifierInfo.Qualified && identifierInfo.receiverInfo == receiverIdentifierInfo) { + (identifierInfo.selectorInfo as? IdentifierInfo.Variable)?.variable + } + else null } } else { dataFlowValueToEntity = fun (value: DataFlowValue): Any? { - val id = value.id - when(id) { - is VariableDescriptor, is ImplicitReceiver -> return id + val identifierInfo = value.identifierInfo + when(identifierInfo) { + is IdentifierInfo.Variable -> return identifierInfo.variable + is IdentifierInfo.Receiver -> return identifierInfo.value as? ImplicitReceiver - is Pair<*, *> -> { - val first = id.first - val second = id.second - if (first !is ImplicitReceiver || second !is VariableDescriptor) return null - if (resolutionScope?.findNearestReceiverForVariable(second)?.value != first) return null - return second + is IdentifierInfo.Qualified -> { + val receiverInfo = identifierInfo.receiverInfo + val selectorInfo = identifierInfo.selectorInfo + if (receiverInfo !is IdentifierInfo.Receiver || selectorInfo !is IdentifierInfo.Variable) return null + val receiverValue = receiverInfo.value as? ImplicitReceiver ?: return null + if (resolutionScope?.findNearestReceiverForVariable(selectorInfo.variable)?.value != receiverValue) return null + return selectorInfo.variable } else -> return null