DataFlowValue & DataFlowValueFactory major refactoring, get rid of DataFlowValue.id : Any?
Instead, DataFlowValue now requires IdentifierInfo implementation to be created DataFlowValue is compared using IdentifierInfo
This commit is contained in:
committed by
Mikhail Glukhikh
parent
7f9b9ddb45
commit
9001b9bcc0
+51
-70
@@ -16,31 +16,32 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||||
|
|
||||||
import com.intellij.openapi.util.Pair
|
|
||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing
|
||||||
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
|
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
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.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.DECLARATION_TO_DESCRIPTOR
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
|
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.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.
|
* 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.
|
// But there are some problem with types built on type parameters, e.g.
|
||||||
// fun <T : Any?> foo(x: T) = x!!.hashCode() // there no way in type system to denote that `x!!` is not nullable
|
// fun <T : Any?> 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,
|
type,
|
||||||
OTHER,
|
|
||||||
Nullability.NOT_NULL)
|
Nullability.NOT_NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,17 +99,11 @@ object DataFlowValueFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
|
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
|
||||||
return DataFlowValue(if (result === NO_IDENTIFIER_INFO) expression else result.id,
|
return DataFlowValue(if (result === IdentifierInfo.NO) ExpressionIdentifierInfo(expression, false) else result, type)
|
||||||
type,
|
|
||||||
result.kind,
|
|
||||||
type.immanentNullability)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createDataFlowValueForStableReceiver(receiver: ReceiverValue): DataFlowValue {
|
fun createDataFlowValueForStableReceiver(receiver: ReceiverValue) = DataFlowValue(IdentifierInfo.Receiver(receiver), receiver.type)
|
||||||
val type = receiver.type
|
|
||||||
return DataFlowValue(receiver, type, STABLE_VALUE, type.immanentNullability)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createDataFlowValue(
|
fun createDataFlowValue(
|
||||||
@@ -137,52 +131,38 @@ object DataFlowValueFactory {
|
|||||||
variableDescriptor: VariableDescriptor,
|
variableDescriptor: VariableDescriptor,
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
usageContainingModule: ModuleDescriptor?
|
usageContainingModule: ModuleDescriptor?
|
||||||
): DataFlowValue {
|
) = DataFlowValue(IdentifierInfo.Variable(variableDescriptor,
|
||||||
val type = variableDescriptor.type
|
variableKind(variableDescriptor, usageContainingModule, bindingContext, property)),
|
||||||
return DataFlowValue(variableDescriptor, type,
|
variableDescriptor.type)
|
||||||
variableKind(variableDescriptor, usageContainingModule,
|
|
||||||
bindingContext, property),
|
|
||||||
type.immanentNullability)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createDataFlowValueForComplexExpression(
|
private fun createDataFlowValueForComplexExpression(
|
||||||
expression: KtExpression,
|
expression: KtExpression,
|
||||||
type: KotlinType
|
type: KotlinType
|
||||||
) = DataFlowValue(expression, type, Kind.STABLE_COMPLEX_EXPRESSION, type.immanentNullability)
|
) = DataFlowValue(ExpressionIdentifierInfo(expression, true), type)
|
||||||
|
|
||||||
private val KotlinType.immanentNullability: Nullability
|
private data class PostfixIdentifierInfo(val argumentInfo: IdentifierInfo) : IdentifierInfo {
|
||||||
get() = if (TypeUtils.isNullableType(this)) Nullability.UNKNOWN else Nullability.NOT_NULL
|
override val kind: DataFlowValue.Kind get() = argumentInfo.kind
|
||||||
|
|
||||||
private open class IdentifierInfo internal constructor(val id: Any?, val kind: Kind, val isPackage: Boolean)
|
override fun toString() = "$argumentInfo (postfix)"
|
||||||
|
|
||||||
private val NO_IDENTIFIER_INFO = object : IdentifierInfo(null, OTHER, false) {
|
|
||||||
override fun toString() = "NO_IDENTIFIER_INFO"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) =
|
override fun toString() = expression.text ?: "(empty expression)"
|
||||||
if (selectorInfo.id == null || receiverInfo === NO_IDENTIFIER_INFO) {
|
}
|
||||||
NO_IDENTIFIER_INFO
|
|
||||||
}
|
private fun postfix(argumentInfo: IdentifierInfo) =
|
||||||
else if (receiverInfo == null || receiverInfo.isPackage) {
|
if (argumentInfo == IdentifierInfo.NO) {
|
||||||
selectorInfo
|
IdentifierInfo.NO
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
createInfo(Pair.create<Any, Any>(receiverInfo.id, selectorInfo.id),
|
PostfixIdentifierInfo(argumentInfo)
|
||||||
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<KtPostfixExpression, Any>(expression, argumentInfo.id), argumentInfo.kind)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getIdForStableIdentifier(
|
private fun getIdForStableIdentifier(
|
||||||
@@ -203,7 +183,7 @@ object DataFlowValueFactory {
|
|||||||
val receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule)
|
val receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule)
|
||||||
val selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule)
|
val selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule)
|
||||||
|
|
||||||
combineInfo(receiverId, selectorId)
|
IdentifierInfo.qualified(receiverId, selectorId, expression.operationSign === KtTokens.SAFE_ACCESS)
|
||||||
}
|
}
|
||||||
is KtSimpleNameExpression ->
|
is KtSimpleNameExpression ->
|
||||||
getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule)
|
getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule)
|
||||||
@@ -214,14 +194,13 @@ object DataFlowValueFactory {
|
|||||||
is KtPostfixExpression -> {
|
is KtPostfixExpression -> {
|
||||||
val operationType = expression.operationReference.getReferencedNameElementType()
|
val operationType = expression.operationReference.getReferencedNameElementType()
|
||||||
if (operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS) {
|
if (operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS) {
|
||||||
createPostfixInfo(expression,
|
postfix(getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule))
|
||||||
getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule))
|
|
||||||
}
|
}
|
||||||
else {
|
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 usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule)
|
||||||
val receiverInfo = resolvedCall?.let { getIdForImplicitReceiver(it.dispatchReceiver, simpleNameExpression) }
|
val receiverInfo = resolvedCall?.let { getIdForImplicitReceiver(it.dispatchReceiver, simpleNameExpression) }
|
||||||
|
|
||||||
combineInfo(receiverInfo, createInfo(declarationDescriptor,
|
IdentifierInfo.qualified(receiverInfo,
|
||||||
variableKind(declarationDescriptor, usageModuleDescriptor,
|
IdentifierInfo.Variable(declarationDescriptor,
|
||||||
bindingContext, simpleNameExpression)))
|
variableKind(declarationDescriptor, usageModuleDescriptor,
|
||||||
|
bindingContext, simpleNameExpression)),
|
||||||
|
resolvedCall?.call?.isSafeCall() ?: false)
|
||||||
}
|
}
|
||||||
is PackageViewDescriptor, is ClassDescriptor -> createPackageOrClassInfo(declarationDescriptor)
|
is PackageViewDescriptor, is ClassDescriptor -> IdentifierInfo.PackageOrClass(declarationDescriptor)
|
||||||
else -> NO_IDENTIFIER_INFO
|
else -> IdentifierInfo.NO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,10 +244,10 @@ object DataFlowValueFactory {
|
|||||||
is CallableDescriptor -> {
|
is CallableDescriptor -> {
|
||||||
val receiverParameter = descriptorOfThisReceiver.extensionReceiverParameter
|
val receiverParameter = descriptorOfThisReceiver.extensionReceiverParameter
|
||||||
?: error("'This' refers to the callable member without a receiver parameter: $descriptorOfThisReceiver")
|
?: 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)
|
is ClassDescriptor -> IdentifierInfo.Receiver(descriptorOfThisReceiver.thisAsReceiverParameter.value)
|
||||||
else -> NO_IDENTIFIER_INFO
|
else -> IdentifierInfo.NO
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getVariableContainingDeclaration(variableDescriptor: VariableDescriptor): DeclarationDescriptor {
|
private fun getVariableContainingDeclaration(variableDescriptor: VariableDescriptor): DeclarationDescriptor {
|
||||||
|
|||||||
+5
-3
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
|||||||
import org.jetbrains.kotlin.psi.KtLoopExpression
|
import org.jetbrains.kotlin.psi.KtLoopExpression
|
||||||
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.IdentifierInfo
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,9 +35,10 @@ class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher()
|
|||||||
val valueSetToClear = LinkedHashSet<DataFlowValue>()
|
val valueSetToClear = LinkedHashSet<DataFlowValue>()
|
||||||
for (value in nullabilityMap.keys) {
|
for (value in nullabilityMap.keys) {
|
||||||
// Only predictable variables are under interest here
|
// Only predictable variables are under interest here
|
||||||
val id = value.id
|
val identifierInfo = value.identifierInfo
|
||||||
if (value.kind == DataFlowValue.Kind.PREDICTABLE_VARIABLE && id is LocalVariableDescriptor) {
|
if (value.kind == DataFlowValue.Kind.PREDICTABLE_VARIABLE && identifierInfo is IdentifierInfo.Variable) {
|
||||||
if (hasWriters(id)) {
|
val variableDescriptor = identifierInfo.variable
|
||||||
|
if (variableDescriptor is LocalVariableDescriptor && hasWriters(variableDescriptor)) {
|
||||||
valueSetToClear.add(value)
|
valueSetToClear.add(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-12
@@ -19,12 +19,20 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.types.ErrorUtils
|
import org.jetbrains.kotlin.types.ErrorUtils
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
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.
|
* This class describes an arbitrary object which has some value in data flow analysis.
|
||||||
* In general case it's some r-value.
|
* 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) {
|
enum class Kind(private val str: String, val description: String = str) {
|
||||||
// Local value, or parameter, or private / internal member value without open / custom getter,
|
// 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 (this === other) return true
|
||||||
if (other !is DataFlowValue) return false
|
if (other !is DataFlowValue) return false
|
||||||
|
|
||||||
if (kind.isStable() != other.kind.isStable()) return false
|
if (identifierInfo != other.identifierInfo) return false
|
||||||
if (id != other.id) return false
|
|
||||||
if (type != other.type) return false
|
if (type != other.type) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString() = "$kind $identifierInfo $immanentNullability"
|
||||||
return kind.toString() + " " + id?.toString() + " " + immanentNullability
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = if (kind.isStable()) 1 else 0
|
var result = identifierInfo.hashCode()
|
||||||
result = 31 * result + type.hashCode()
|
result = 31 * result + type.hashCode()
|
||||||
result = 31 * result + (id?.hashCode() ?: 0)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun nullValue(builtIns: KotlinBuiltIns) = DataFlowValue(
|
fun nullValue(builtIns: KotlinBuiltIns) = DataFlowValue(IdentifierInfo.NULL, builtIns.nullableNothingType, Nullability.NULL)
|
||||||
Object(), builtIns.nullableNothingType, Kind.OTHER, Nullability.NULL
|
|
||||||
)
|
|
||||||
|
|
||||||
@JvmField
|
@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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+104
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-8
@@ -21,20 +21,23 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
|||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
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? {
|
fun renderDataFlowValue(value: DataFlowValue): String? {
|
||||||
// If it is not a stable identifier, there's no point in rendering it
|
// If it is not a stable identifier, there's no point in rendering it
|
||||||
if (!value.isPredictable) return null
|
if (!value.isPredictable) return null
|
||||||
|
|
||||||
fun renderId(id: Any?): String? {
|
fun renderId(identifierInfo: IdentifierInfo): String? = with (identifierInfo) {
|
||||||
return when (id) {
|
when (this) {
|
||||||
is KtExpression -> id.text
|
is DataFlowValueFactory.ExpressionIdentifierInfo -> expression.text
|
||||||
is ImplicitReceiver -> "this@${id.declarationDescriptor.name}"
|
is IdentifierInfo.Receiver -> (this.value as? ImplicitReceiver)?.declarationDescriptor?.name?.let { "this@$it" }
|
||||||
is VariableDescriptor -> id.name.asString()
|
is IdentifierInfo.Variable -> variable.name.asString()
|
||||||
is PackageViewDescriptor -> id.fqName.asString()
|
is IdentifierInfo.PackageOrClass -> (descriptor as? PackageViewDescriptor)?.let { it.fqName.asString() }
|
||||||
is com.intellij.openapi.util.Pair<*, *> -> renderId(id.first) + "." + renderId(id.second)
|
is IdentifierInfo.Qualified -> renderId(receiverInfo) + "." + renderId(selectorInfo)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return renderId(value.id)
|
|
||||||
|
return renderId(value.identifierInfo)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.core
|
package org.jetbrains.kotlin.idea.core
|
||||||
|
|
||||||
import com.intellij.openapi.util.Pair
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
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.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.*
|
||||||
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.scopes.LexicalScope
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
@@ -85,24 +81,31 @@ class SmartCastCalculator(
|
|||||||
val dataFlowValueToEntity: (DataFlowValue) -> Any?
|
val dataFlowValueToEntity: (DataFlowValue) -> Any?
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
val receiverType = bindingContext.getType(receiver) ?: return emptyMap()
|
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 ->
|
dataFlowValueToEntity = { value ->
|
||||||
val id = value.id
|
val identifierInfo = value.identifierInfo
|
||||||
if (id is Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null
|
if (identifierInfo is IdentifierInfo.Qualified && identifierInfo.receiverInfo == receiverIdentifierInfo) {
|
||||||
|
(identifierInfo.selectorInfo as? IdentifierInfo.Variable)?.variable
|
||||||
|
}
|
||||||
|
else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dataFlowValueToEntity = fun (value: DataFlowValue): Any? {
|
dataFlowValueToEntity = fun (value: DataFlowValue): Any? {
|
||||||
val id = value.id
|
val identifierInfo = value.identifierInfo
|
||||||
when(id) {
|
when(identifierInfo) {
|
||||||
is VariableDescriptor, is ImplicitReceiver -> return id
|
is IdentifierInfo.Variable -> return identifierInfo.variable
|
||||||
|
is IdentifierInfo.Receiver -> return identifierInfo.value as? ImplicitReceiver
|
||||||
|
|
||||||
is Pair<*, *> -> {
|
is IdentifierInfo.Qualified -> {
|
||||||
val first = id.first
|
val receiverInfo = identifierInfo.receiverInfo
|
||||||
val second = id.second
|
val selectorInfo = identifierInfo.selectorInfo
|
||||||
if (first !is ImplicitReceiver || second !is VariableDescriptor) return null
|
if (receiverInfo !is IdentifierInfo.Receiver || selectorInfo !is IdentifierInfo.Variable) return null
|
||||||
if (resolutionScope?.findNearestReceiverForVariable(second)?.value != first) return null
|
val receiverValue = receiverInfo.value as? ImplicitReceiver ?: return null
|
||||||
return second
|
if (resolutionScope?.findNearestReceiverForVariable(selectorInfo.variable)?.value != receiverValue) return null
|
||||||
|
return selectorInfo.variable
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> return null
|
else -> return null
|
||||||
|
|||||||
Reference in New Issue
Block a user