Refactor DataFlowValueFactory implementation into several separate files
This commit is contained in:
+1
-415
@@ -16,35 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing
|
||||
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
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.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.AssignedVariablesSearcher.Writer
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
|
||||
/**
|
||||
* This class is intended to create data flow values for different kind of expressions.
|
||||
@@ -84,395 +61,4 @@ interface DataFlowValueFactory {
|
||||
usageContainingModule: ModuleDescriptor?
|
||||
): DataFlowValue
|
||||
|
||||
}
|
||||
class DataFlowValueFactoryImpl : DataFlowValueFactory {
|
||||
|
||||
override fun createDataFlowValue(
|
||||
expression: KtExpression,
|
||||
type: KotlinType,
|
||||
resolutionContext: ResolutionContext<*>
|
||||
) = createDataFlowValue(expression, type, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
|
||||
|
||||
private fun isComplexExpression(expression: KtExpression): Boolean = when (expression) {
|
||||
is KtBlockExpression, is KtIfExpression, is KtWhenExpression -> true
|
||||
is KtBinaryExpression -> expression.operationToken === KtTokens.ELVIS
|
||||
is KtParenthesizedExpression -> {
|
||||
val deparenthesized = KtPsiUtil.deparenthesize(expression)
|
||||
deparenthesized != null && isComplexExpression(deparenthesized)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun createDataFlowValue(
|
||||
expression: KtExpression,
|
||||
type: KotlinType,
|
||||
bindingContext: BindingContext,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
): DataFlowValue {
|
||||
if (expression is KtConstantExpression) {
|
||||
if (expression.node.elementType === KtNodeTypes.NULL) {
|
||||
return DataFlowValue.nullValue(containingDeclarationOrModule.builtIns)
|
||||
}
|
||||
}
|
||||
if (type.isError) return DataFlowValue.ERROR
|
||||
if (isNullableNothing(type)) {
|
||||
return DataFlowValue.nullValue(containingDeclarationOrModule.builtIns) // 'null' is the only inhabitant of 'Nothing?'
|
||||
}
|
||||
|
||||
if (ExpressionTypingUtils.isExclExclExpression(KtPsiUtil.deparenthesize(expression))) {
|
||||
// In most cases type of `E!!`-expression is strictly not nullable and we could get proper Nullability
|
||||
// by calling `getImmanentNullability` (as it happens below).
|
||||
//
|
||||
// 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
|
||||
return DataFlowValue(
|
||||
ExpressionIdentifierInfo(expression),
|
||||
type,
|
||||
Nullability.NOT_NULL
|
||||
)
|
||||
}
|
||||
|
||||
if (isComplexExpression(expression)) {
|
||||
return createDataFlowValueForComplexExpression(expression, type)
|
||||
}
|
||||
|
||||
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
|
||||
return DataFlowValue(if (result === IdentifierInfo.NO) ExpressionIdentifierInfo(expression) else result, type)
|
||||
}
|
||||
|
||||
override fun createDataFlowValueForStableReceiver(receiver: ReceiverValue) = DataFlowValue(IdentifierInfo.Receiver(receiver), receiver.type)
|
||||
|
||||
override fun createDataFlowValue(
|
||||
receiverValue: ReceiverValue,
|
||||
resolutionContext: ResolutionContext<*>
|
||||
) = createDataFlowValue(receiverValue, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
|
||||
|
||||
override fun createDataFlowValue(
|
||||
receiverValue: ReceiverValue,
|
||||
bindingContext: BindingContext,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
) = when (receiverValue) {
|
||||
is TransientReceiver, is ImplicitReceiver -> createDataFlowValueForStableReceiver(receiverValue)
|
||||
is ExpressionReceiver -> createDataFlowValue(
|
||||
receiverValue.expression,
|
||||
receiverValue.getType(),
|
||||
bindingContext,
|
||||
containingDeclarationOrModule
|
||||
)
|
||||
else -> throw UnsupportedOperationException("Unsupported receiver value: " + receiverValue::class.java.name)
|
||||
}
|
||||
|
||||
override fun createDataFlowValueForProperty(
|
||||
property: KtProperty,
|
||||
variableDescriptor: VariableDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
usageContainingModule: ModuleDescriptor?
|
||||
) = DataFlowValue(
|
||||
IdentifierInfo.Variable(
|
||||
variableDescriptor,
|
||||
variableKind(variableDescriptor, usageContainingModule, bindingContext, property),
|
||||
bindingContext[BOUND_INITIALIZER_VALUE, variableDescriptor]
|
||||
),
|
||||
variableDescriptor.type
|
||||
)
|
||||
|
||||
private fun createDataFlowValueForComplexExpression(
|
||||
expression: KtExpression,
|
||||
type: KotlinType
|
||||
) = DataFlowValue(ExpressionIdentifierInfo(expression, stableComplex = true), type)
|
||||
|
||||
// For only ++ and -- postfix operations
|
||||
private data class PostfixIdentifierInfo(val argumentInfo: IdentifierInfo, val op: KtToken) : IdentifierInfo {
|
||||
override val kind: DataFlowValue.Kind get() = argumentInfo.kind
|
||||
|
||||
override fun toString() = "$argumentInfo($op)"
|
||||
}
|
||||
|
||||
class ExpressionIdentifierInfo(val expression: KtExpression, stableComplex: Boolean = false) : IdentifierInfo {
|
||||
|
||||
override val kind = if (stableComplex) STABLE_COMPLEX_EXPRESSION else OTHER
|
||||
|
||||
override fun equals(other: Any?) = other is ExpressionIdentifierInfo && expression == other.expression
|
||||
|
||||
override fun hashCode() = expression.hashCode()
|
||||
|
||||
override fun toString() = expression.text ?: "(empty expression)"
|
||||
}
|
||||
|
||||
private fun postfix(argumentInfo: IdentifierInfo, op: KtToken) =
|
||||
if (argumentInfo == IdentifierInfo.NO) {
|
||||
IdentifierInfo.NO
|
||||
} else {
|
||||
PostfixIdentifierInfo(argumentInfo, op)
|
||||
}
|
||||
|
||||
private fun getIdForStableIdentifier(
|
||||
expression: KtExpression?,
|
||||
bindingContext: BindingContext,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
): IdentifierInfo {
|
||||
if (expression != null) {
|
||||
val deparenthesized = KtPsiUtil.deparenthesize(expression)
|
||||
if (expression !== deparenthesized) {
|
||||
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule)
|
||||
}
|
||||
}
|
||||
return when (expression) {
|
||||
is KtQualifiedExpression -> {
|
||||
val receiverExpression = expression.receiverExpression
|
||||
val selectorExpression = expression.selectorExpression
|
||||
val receiverInfo = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule)
|
||||
val selectorInfo = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule)
|
||||
|
||||
IdentifierInfo.qualified(
|
||||
receiverInfo, bindingContext.getType(receiverExpression),
|
||||
selectorInfo, expression.operationSign === KtTokens.SAFE_ACCESS
|
||||
)
|
||||
}
|
||||
is KtBinaryExpressionWithTypeRHS -> {
|
||||
val subjectExpression = expression.left
|
||||
val targetTypeReference = expression.right
|
||||
val operationToken = expression.operationReference.getReferencedNameElementType()
|
||||
if (operationToken == KtTokens.IS_KEYWORD || operationToken == KtTokens.AS_KEYWORD) {
|
||||
IdentifierInfo.NO
|
||||
} else {
|
||||
IdentifierInfo.SafeCast(
|
||||
getIdForStableIdentifier(subjectExpression, bindingContext, containingDeclarationOrModule),
|
||||
bindingContext.getType(subjectExpression),
|
||||
bindingContext[BindingContext.TYPE, targetTypeReference]
|
||||
)
|
||||
}
|
||||
}
|
||||
is KtSimpleNameExpression ->
|
||||
getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule)
|
||||
is KtThisExpression -> {
|
||||
val declarationDescriptor = bindingContext.get(REFERENCE_TARGET, expression.instanceReference)
|
||||
getIdForThisReceiver(declarationDescriptor)
|
||||
}
|
||||
is KtPostfixExpression -> {
|
||||
val operationType = expression.operationReference.getReferencedNameElementType()
|
||||
if (operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS) {
|
||||
postfix(
|
||||
getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule),
|
||||
operationType
|
||||
)
|
||||
} else {
|
||||
IdentifierInfo.NO
|
||||
}
|
||||
}
|
||||
else -> IdentifierInfo.NO
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIdForSimpleNameExpression(
|
||||
simpleNameExpression: KtSimpleNameExpression,
|
||||
bindingContext: BindingContext,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
): IdentifierInfo {
|
||||
val declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression)
|
||||
return when (declarationDescriptor) {
|
||||
is VariableDescriptor -> {
|
||||
val resolvedCall = simpleNameExpression.getResolvedCall(bindingContext)
|
||||
|
||||
// todo uncomment assert
|
||||
// KT-4113
|
||||
// for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
|
||||
// assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for
|
||||
val usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule)
|
||||
val selectorInfo = IdentifierInfo.Variable(
|
||||
declarationDescriptor,
|
||||
variableKind(declarationDescriptor, usageModuleDescriptor, bindingContext, simpleNameExpression),
|
||||
bindingContext[BOUND_INITIALIZER_VALUE, declarationDescriptor]
|
||||
)
|
||||
|
||||
val implicitReceiver = resolvedCall?.dispatchReceiver
|
||||
if (implicitReceiver == null) {
|
||||
selectorInfo
|
||||
} else {
|
||||
val receiverInfo = getIdForImplicitReceiver(implicitReceiver, simpleNameExpression)
|
||||
|
||||
if (receiverInfo == null) {
|
||||
selectorInfo
|
||||
} else {
|
||||
IdentifierInfo.qualified(
|
||||
receiverInfo, implicitReceiver.type,
|
||||
selectorInfo, resolvedCall.call.isSafeCall()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is PackageViewDescriptor, is ClassDescriptor -> IdentifierInfo.PackageOrClass(declarationDescriptor)
|
||||
else -> IdentifierInfo.NO
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIdForImplicitReceiver(receiverValue: ReceiverValue?, expression: KtExpression?) =
|
||||
when (receiverValue) {
|
||||
is ImplicitReceiver -> getIdForThisReceiver(receiverValue.declarationDescriptor)
|
||||
is TransientReceiver ->
|
||||
throw AssertionError("Transient receiver is implicit for an explicit expression: $expression. Receiver: $receiverValue")
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun getIdForThisReceiver(descriptorOfThisReceiver: DeclarationDescriptor?) = when (descriptorOfThisReceiver) {
|
||||
is CallableDescriptor -> {
|
||||
val receiverParameter = descriptorOfThisReceiver.extensionReceiverParameter
|
||||
?: error("'This' refers to the callable member without a receiver parameter: $descriptorOfThisReceiver")
|
||||
IdentifierInfo.Receiver(receiverParameter.value)
|
||||
}
|
||||
is ClassDescriptor -> IdentifierInfo.Receiver(descriptorOfThisReceiver.thisAsReceiverParameter.value)
|
||||
else -> IdentifierInfo.NO
|
||||
}
|
||||
|
||||
private fun isAccessedInsideClosure(
|
||||
variableContainingDeclaration: DeclarationDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
accessElement: KtElement
|
||||
): Boolean {
|
||||
val parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement)
|
||||
return if (parent != null)
|
||||
// Access is at the same declaration: not in closure, lower: in closure
|
||||
ControlFlowInformationProvider.getDeclarationDescriptorIncludingConstructors(bindingContext, parent) !=
|
||||
variableContainingDeclaration
|
||||
else
|
||||
false
|
||||
}
|
||||
|
||||
private fun hasNoWritersInClosures(
|
||||
variableContainingDeclaration: DeclarationDescriptor,
|
||||
writers: Set<Writer>,
|
||||
bindingContext: BindingContext
|
||||
): Boolean {
|
||||
return writers.none { (_, writerDeclaration) ->
|
||||
val writerDescriptor = writerDeclaration?.let {
|
||||
ControlFlowInformationProvider.getDeclarationDescriptorIncludingConstructors(bindingContext, it)
|
||||
}
|
||||
writerDeclaration != null && variableContainingDeclaration != writerDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAccessedInsideClosureAfterAllWriters(
|
||||
writers: Set<Writer>,
|
||||
accessElement: KtElement
|
||||
): Boolean {
|
||||
val parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement) ?: return false
|
||||
return writers.none { (assignment) -> !assignment.before(parent) }
|
||||
}
|
||||
|
||||
private fun isAccessedBeforeAllClosureWriters(
|
||||
variableContainingDeclaration: DeclarationDescriptor,
|
||||
writers: Set<Writer>,
|
||||
bindingContext: BindingContext,
|
||||
accessElement: KtElement
|
||||
): Boolean {
|
||||
// All writers should be before access element, with the exception:
|
||||
// writer which is the same with declaration site does not count
|
||||
writers.mapNotNull { it.declaration }.forEach { writerDeclaration ->
|
||||
val writerDescriptor = ControlFlowInformationProvider.getDeclarationDescriptorIncludingConstructors(
|
||||
bindingContext, writerDeclaration
|
||||
)
|
||||
// Access is after some writerDeclaration
|
||||
if (variableContainingDeclaration != writerDescriptor && !accessElement.before(writerDeclaration)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Access is before all writers
|
||||
return true
|
||||
}
|
||||
|
||||
private fun variableKind(
|
||||
variableDescriptor: VariableDescriptor,
|
||||
usageModule: ModuleDescriptor?,
|
||||
bindingContext: BindingContext,
|
||||
accessElement: KtElement
|
||||
): Kind {
|
||||
if (variableDescriptor is PropertyDescriptor) {
|
||||
return propertyKind(variableDescriptor, usageModule)
|
||||
}
|
||||
if (variableDescriptor !is LocalVariableDescriptor && variableDescriptor !is ParameterDescriptor) return OTHER
|
||||
if (!variableDescriptor.isVar) return STABLE_VALUE
|
||||
if (variableDescriptor is SyntheticFieldDescriptor) return MUTABLE_PROPERTY
|
||||
|
||||
// Local variable classification: STABLE or CAPTURED
|
||||
val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(variableDescriptor, bindingContext)
|
||||
// A case when we just analyse an expression alone: counts as captured
|
||||
?: return CAPTURED_VARIABLE
|
||||
|
||||
// Analyze who writes variable
|
||||
// If there is no writer: stable
|
||||
val writers = preliminaryVisitor.writers(variableDescriptor)
|
||||
if (writers.isEmpty()) return STABLE_VARIABLE
|
||||
|
||||
// If access element is inside closure: captured
|
||||
val variableContainingDeclaration = variableDescriptor.containingDeclaration
|
||||
if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) {
|
||||
// stable iff we have no writers in closures AND this closure is AFTER all writers
|
||||
return if (preliminaryVisitor.languageVersionSettings.supportsFeature(LanguageFeature.CapturedInClosureSmartCasts) &&
|
||||
hasNoWritersInClosures(variableContainingDeclaration, writers, bindingContext) &&
|
||||
isAccessedInsideClosureAfterAllWriters(writers, accessElement)) {
|
||||
STABLE_VARIABLE
|
||||
} else {
|
||||
CAPTURED_VARIABLE
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, stable iff considered position is BEFORE all writers except declarer itself
|
||||
return if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement))
|
||||
STABLE_VARIABLE
|
||||
else
|
||||
CAPTURED_VARIABLE
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Determines whether a variable with a given descriptor is stable or not at the given usage place.
|
||||
*
|
||||
*
|
||||
* Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
|
||||
*
|
||||
*
|
||||
* If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND
|
||||
* the default getter is in use (otherwise nobody can guarantee that a getter is consistent) AND
|
||||
* (it's private OR internal OR used at the same module where it's defined).
|
||||
* The last check corresponds to a risk of changing property definition in another module, e.g. from "val" to "var".
|
||||
|
||||
* @param variableDescriptor descriptor of a considered variable
|
||||
* *
|
||||
* @param usageModule a module with a considered usage place, or null if it's not known (not recommended)
|
||||
* *
|
||||
* @return true if variable is stable, false otherwise
|
||||
*/
|
||||
fun isStableValue(
|
||||
variableDescriptor: VariableDescriptor,
|
||||
usageModule: ModuleDescriptor?
|
||||
): Boolean {
|
||||
if (variableDescriptor.isVar) return false
|
||||
return variableDescriptor !is PropertyDescriptor || propertyKind(variableDescriptor, usageModule) === STABLE_VALUE
|
||||
}
|
||||
|
||||
private fun propertyKind(propertyDescriptor: PropertyDescriptor, usageModule: ModuleDescriptor?): Kind {
|
||||
if (propertyDescriptor.isVar) return MUTABLE_PROPERTY
|
||||
if (propertyDescriptor.isOverridable) return PROPERTY_WITH_GETTER
|
||||
if (!hasDefaultGetter(propertyDescriptor)) return PROPERTY_WITH_GETTER
|
||||
if (!invisibleFromOtherModules(propertyDescriptor)) {
|
||||
val declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor)
|
||||
if (usageModule == null || usageModule != declarationModule) {
|
||||
return ALIEN_PUBLIC_PROPERTY
|
||||
}
|
||||
}
|
||||
return STABLE_VALUE
|
||||
}
|
||||
|
||||
private fun hasDefaultGetter(propertyDescriptor: PropertyDescriptor): Boolean {
|
||||
val getter = propertyDescriptor.getter
|
||||
return getter == null || getter.isDefault
|
||||
}
|
||||
|
||||
private fun invisibleFromOtherModules(descriptor: DeclarationDescriptorWithVisibility): Boolean {
|
||||
if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.visibility)) return true
|
||||
|
||||
val containingDeclaration = descriptor.containingDeclaration
|
||||
return containingDeclaration is DeclarationDescriptorWithVisibility && invisibleFromOtherModules(containingDeclaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
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.isError
|
||||
|
||||
@Deprecated("Please, avoid to use that implementation explicitly. If you need DataFlowValueFactory, use injection")
|
||||
class DataFlowValueFactoryImpl : DataFlowValueFactory {
|
||||
|
||||
// Receivers
|
||||
override fun createDataFlowValue(
|
||||
receiverValue: ReceiverValue,
|
||||
resolutionContext: ResolutionContext<*>
|
||||
) = createDataFlowValue(receiverValue, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
|
||||
|
||||
override fun createDataFlowValue(
|
||||
receiverValue: ReceiverValue,
|
||||
bindingContext: BindingContext,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
) = when (receiverValue) {
|
||||
is TransientReceiver, is ImplicitReceiver -> createDataFlowValueForStableReceiver(receiverValue)
|
||||
is ExpressionReceiver -> createDataFlowValue(
|
||||
receiverValue.expression,
|
||||
receiverValue.getType(),
|
||||
bindingContext,
|
||||
containingDeclarationOrModule
|
||||
)
|
||||
else -> throw UnsupportedOperationException("Unsupported receiver value: " + receiverValue::class.java.name)
|
||||
}
|
||||
|
||||
override fun createDataFlowValueForStableReceiver(receiver: ReceiverValue) =
|
||||
DataFlowValue(IdentifierInfo.Receiver(receiver), receiver.type)
|
||||
|
||||
|
||||
// Property
|
||||
override fun createDataFlowValueForProperty(
|
||||
property: KtProperty,
|
||||
variableDescriptor: VariableDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
usageContainingModule: ModuleDescriptor?
|
||||
): DataFlowValue {
|
||||
val identifierInfo = IdentifierInfo.Variable(
|
||||
variableDescriptor,
|
||||
variableDescriptor.variableKind(usageContainingModule, bindingContext, property),
|
||||
bindingContext[BindingContext.BOUND_INITIALIZER_VALUE, variableDescriptor]
|
||||
)
|
||||
return DataFlowValue(identifierInfo, variableDescriptor.type)
|
||||
}
|
||||
|
||||
|
||||
// Expressions
|
||||
override fun createDataFlowValue(
|
||||
expression: KtExpression,
|
||||
type: KotlinType,
|
||||
resolutionContext: ResolutionContext<*>
|
||||
) = createDataFlowValue(expression, type, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
|
||||
|
||||
override fun createDataFlowValue(
|
||||
expression: KtExpression,
|
||||
type: KotlinType,
|
||||
bindingContext: BindingContext,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
): DataFlowValue {
|
||||
return when {
|
||||
expression is KtConstantExpression && expression.node.elementType === KtNodeTypes.NULL ->
|
||||
DataFlowValue.nullValue(containingDeclarationOrModule.builtIns)
|
||||
|
||||
type.isError -> DataFlowValue.ERROR
|
||||
|
||||
KotlinBuiltIns.isNullableNothing(type) ->
|
||||
DataFlowValue.nullValue(containingDeclarationOrModule.builtIns) // 'null' is the only inhabitant of 'Nothing?'
|
||||
|
||||
// In most cases type of `E!!`-expression is strictly not nullable and we could get proper Nullability
|
||||
// by calling `getImmanentNullability` (as it happens below).
|
||||
//
|
||||
// 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
|
||||
ExpressionTypingUtils.isExclExclExpression(KtPsiUtil.deparenthesize(expression)) ->
|
||||
DataFlowValue(IdentifierInfo.Expression(expression), type, Nullability.NOT_NULL)
|
||||
|
||||
isComplexExpression(expression) ->
|
||||
DataFlowValue(IdentifierInfo.Expression(expression, stableComplex = true), type)
|
||||
|
||||
else -> {
|
||||
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
|
||||
DataFlowValue(if (result === IdentifierInfo.NO) IdentifierInfo.Expression(expression) else result, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isComplexExpression(expression: KtExpression): Boolean = when (expression) {
|
||||
is KtBlockExpression, is KtIfExpression, is KtWhenExpression -> true
|
||||
|
||||
is KtBinaryExpression -> expression.operationToken === KtTokens.ELVIS
|
||||
|
||||
is KtParenthesizedExpression -> {
|
||||
val deparenthesized = KtPsiUtil.deparenthesize(expression)
|
||||
deparenthesized != null && isComplexExpression(deparenthesized)
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||
|
||||
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.psiUtil.before
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.expressions.AssignedVariablesSearcher
|
||||
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
|
||||
|
||||
/**
|
||||
* Determines whether a variable with a given descriptor is stable or not at the given usage place.
|
||||
*
|
||||
*
|
||||
* Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
|
||||
*
|
||||
*
|
||||
* If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND
|
||||
* the default getter is in use (otherwise nobody can guarantee that a getter is consistent) AND
|
||||
* (it's private OR internal OR used at the same module where it's defined).
|
||||
* The last check corresponds to a risk of changing property definition in another module, e.g. from "val" to "var".
|
||||
|
||||
* @param variableDescriptor descriptor of a considered variable
|
||||
* *
|
||||
* @param usageModule a module with a considered usage place, or null if it's not known (not recommended)
|
||||
* *
|
||||
* @return true if variable is stable, false otherwise
|
||||
*/
|
||||
fun isStableValue(
|
||||
variableDescriptor: VariableDescriptor,
|
||||
usageModule: ModuleDescriptor?
|
||||
): Boolean {
|
||||
if (variableDescriptor.isVar) return false
|
||||
return variableDescriptor !is PropertyDescriptor || variableDescriptor.propertyKind(usageModule) === DataFlowValue.Kind.STABLE_VALUE
|
||||
}
|
||||
|
||||
internal fun PropertyDescriptor.propertyKind(usageModule: ModuleDescriptor?): DataFlowValue.Kind {
|
||||
if (isVar) return DataFlowValue.Kind.MUTABLE_PROPERTY
|
||||
if (isOverridable) return DataFlowValue.Kind.PROPERTY_WITH_GETTER
|
||||
if (!hasDefaultGetter()) return DataFlowValue.Kind.PROPERTY_WITH_GETTER
|
||||
if (!isInvisibleFromOtherModules()) {
|
||||
val declarationModule = DescriptorUtils.getContainingModule(this)
|
||||
if (usageModule == null || usageModule != declarationModule) {
|
||||
return DataFlowValue.Kind.ALIEN_PUBLIC_PROPERTY
|
||||
}
|
||||
}
|
||||
return DataFlowValue.Kind.STABLE_VALUE
|
||||
}
|
||||
|
||||
internal fun VariableDescriptor.variableKind(
|
||||
usageModule: ModuleDescriptor?,
|
||||
bindingContext: BindingContext,
|
||||
accessElement: KtElement
|
||||
): DataFlowValue.Kind {
|
||||
if (this is PropertyDescriptor) {
|
||||
return propertyKind(usageModule)
|
||||
}
|
||||
if (this !is LocalVariableDescriptor && this !is ParameterDescriptor) return DataFlowValue.Kind.OTHER
|
||||
if (!isVar) return DataFlowValue.Kind.STABLE_VALUE
|
||||
if (this is SyntheticFieldDescriptor) return DataFlowValue.Kind.MUTABLE_PROPERTY
|
||||
|
||||
// Local variable classification: STABLE or CAPTURED
|
||||
val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(this, bindingContext)
|
||||
// A case when we just analyse an expression alone: counts as captured
|
||||
?: return DataFlowValue.Kind.CAPTURED_VARIABLE
|
||||
|
||||
// Analyze who writes variable
|
||||
// If there is no writer: stable
|
||||
val writers = preliminaryVisitor.writers(this)
|
||||
if (writers.isEmpty()) return DataFlowValue.Kind.STABLE_VARIABLE
|
||||
|
||||
// If access element is inside closure: captured
|
||||
val variableContainingDeclaration = this.containingDeclaration
|
||||
if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) {
|
||||
// stable iff we have no writers in closures AND this closure is AFTER all writers
|
||||
return if (preliminaryVisitor.languageVersionSettings.supportsFeature(LanguageFeature.CapturedInClosureSmartCasts) &&
|
||||
hasNoWritersInClosures(variableContainingDeclaration, writers, bindingContext) &&
|
||||
isAccessedInsideClosureAfterAllWriters(writers, accessElement)
|
||||
) {
|
||||
DataFlowValue.Kind.STABLE_VARIABLE
|
||||
} else {
|
||||
DataFlowValue.Kind.CAPTURED_VARIABLE
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, stable iff considered position is BEFORE all writers except declarer itself
|
||||
return if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement))
|
||||
DataFlowValue.Kind.STABLE_VARIABLE
|
||||
else
|
||||
DataFlowValue.Kind.CAPTURED_VARIABLE
|
||||
}
|
||||
|
||||
|
||||
private fun hasNoWritersInClosures(
|
||||
variableContainingDeclaration: DeclarationDescriptor,
|
||||
writers: Set<AssignedVariablesSearcher.Writer>,
|
||||
bindingContext: BindingContext
|
||||
): Boolean {
|
||||
return writers.none { (_, writerDeclaration) ->
|
||||
val writerDescriptor = writerDeclaration?.let {
|
||||
ControlFlowInformationProvider.getDeclarationDescriptorIncludingConstructors(bindingContext, it)
|
||||
}
|
||||
writerDeclaration != null && variableContainingDeclaration != writerDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAccessedInsideClosureAfterAllWriters(
|
||||
writers: Set<AssignedVariablesSearcher.Writer>,
|
||||
accessElement: KtElement
|
||||
): Boolean {
|
||||
val parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement) ?: return false
|
||||
return writers.none { (assignment) -> !assignment.before(parent) }
|
||||
}
|
||||
|
||||
private fun isAccessedBeforeAllClosureWriters(
|
||||
variableContainingDeclaration: DeclarationDescriptor,
|
||||
writers: Set<AssignedVariablesSearcher.Writer>,
|
||||
bindingContext: BindingContext,
|
||||
accessElement: KtElement
|
||||
): Boolean {
|
||||
// All writers should be before access element, with the exception:
|
||||
// writer which is the same with declaration site does not count
|
||||
writers.mapNotNull { it.declaration }.forEach { writerDeclaration ->
|
||||
val writerDescriptor = ControlFlowInformationProvider.getDeclarationDescriptorIncludingConstructors(
|
||||
bindingContext, writerDeclaration
|
||||
)
|
||||
// Access is after some writerDeclaration
|
||||
if (variableContainingDeclaration != writerDescriptor && !accessElement.before(writerDeclaration)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Access is before all writers
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private fun DeclarationDescriptorWithVisibility.isInvisibleFromOtherModules(): Boolean {
|
||||
if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(visibility)) return true
|
||||
|
||||
val containingDeclaration = containingDeclaration
|
||||
return containingDeclaration is DeclarationDescriptorWithVisibility && containingDeclaration.isInvisibleFromOtherModules()
|
||||
}
|
||||
|
||||
private fun PropertyDescriptor.hasDefaultGetter(): Boolean {
|
||||
val getter = getter
|
||||
return getter == null || getter.isDefault
|
||||
}
|
||||
|
||||
private fun isAccessedInsideClosure(
|
||||
variableContainingDeclaration: DeclarationDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
accessElement: KtElement
|
||||
): Boolean {
|
||||
val parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement)
|
||||
return if (parent != null) // Access is at the same declaration: not in closure, lower: in closure
|
||||
ControlFlowInformationProvider.getDeclarationDescriptorIncludingConstructors(bindingContext, parent) !=
|
||||
variableContainingDeclaration
|
||||
else
|
||||
false
|
||||
}
|
||||
+161
-21
@@ -16,11 +16,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
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.smartcasts.DataFlowValue.Kind.OTHER
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.STABLE_VALUE
|
||||
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
|
||||
|
||||
interface IdentifierInfo {
|
||||
@@ -46,7 +54,6 @@ interface IdentifierInfo {
|
||||
override val kind: DataFlowValue.Kind,
|
||||
val bound: DataFlowValue?
|
||||
) : IdentifierInfo {
|
||||
|
||||
override val canBeBound
|
||||
get() = kind == STABLE_VALUE
|
||||
|
||||
@@ -58,14 +65,12 @@ interface IdentifierInfo {
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -77,6 +82,7 @@ interface IdentifierInfo {
|
||||
val safe: Boolean,
|
||||
val receiverType: KotlinType?
|
||||
) : IdentifierInfo {
|
||||
|
||||
override val kind: DataFlowValue.Kind get() = if (receiverInfo.kind == STABLE_VALUE) selectorInfo.kind else OTHER
|
||||
|
||||
override val canBeBound
|
||||
@@ -89,11 +95,7 @@ interface IdentifierInfo {
|
||||
override fun toString() = "$receiverInfo${if (safe) "?." else "."}$selectorInfo"
|
||||
}
|
||||
|
||||
data class SafeCast(
|
||||
val subjectInfo: IdentifierInfo,
|
||||
val subjectType: KotlinType?,
|
||||
val targetType: KotlinType?
|
||||
) : IdentifierInfo {
|
||||
data class SafeCast(val subjectInfo: IdentifierInfo, val subjectType: KotlinType?, val targetType: KotlinType?) : IdentifierInfo {
|
||||
override val kind get() = OTHER
|
||||
|
||||
override val canBeBound get() = subjectInfo.canBeBound
|
||||
@@ -101,17 +103,155 @@ interface IdentifierInfo {
|
||||
override fun toString() = "$subjectInfo as? ${targetType ?: "???"}"
|
||||
}
|
||||
|
||||
companion object {
|
||||
class Expression(val expression: KtExpression, stableComplex: Boolean = false) : IdentifierInfo {
|
||||
override val kind = if (stableComplex) DataFlowValue.Kind.STABLE_COMPLEX_EXPRESSION else OTHER
|
||||
|
||||
fun qualified(
|
||||
receiverInfo: IdentifierInfo,
|
||||
receiverType: KotlinType?,
|
||||
selectorInfo: IdentifierInfo,
|
||||
safe: Boolean
|
||||
) = when (receiverInfo) {
|
||||
NO -> NO
|
||||
is PackageOrClass -> selectorInfo
|
||||
else -> Qualified(receiverInfo, selectorInfo, safe, receiverType)
|
||||
override fun equals(other: Any?) = other is Expression && expression == other.expression
|
||||
|
||||
override fun hashCode() = expression.hashCode()
|
||||
|
||||
override fun toString() = expression.text ?: "(empty expression)"
|
||||
}
|
||||
|
||||
// For only ++ and -- postfix operations
|
||||
data class PostfixIdentifierInfo(val argumentInfo: IdentifierInfo, val op: KtToken) : IdentifierInfo {
|
||||
override val kind: DataFlowValue.Kind get() = argumentInfo.kind
|
||||
|
||||
override fun toString() = "$argumentInfo($op)"
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getIdForStableIdentifier(
|
||||
expression: KtExpression?,
|
||||
bindingContext: BindingContext,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
): IdentifierInfo {
|
||||
if (expression != null) {
|
||||
val deparenthesized = KtPsiUtil.deparenthesize(expression)
|
||||
if (expression !== deparenthesized) {
|
||||
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule)
|
||||
}
|
||||
}
|
||||
}
|
||||
return when (expression) {
|
||||
is KtQualifiedExpression -> {
|
||||
val receiverExpression = expression.receiverExpression
|
||||
val selectorExpression = expression.selectorExpression
|
||||
val receiverInfo = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule)
|
||||
val selectorInfo = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule)
|
||||
|
||||
qualified(
|
||||
receiverInfo, bindingContext.getType(receiverExpression),
|
||||
selectorInfo, expression.operationSign === KtTokens.SAFE_ACCESS
|
||||
)
|
||||
}
|
||||
|
||||
is KtBinaryExpressionWithTypeRHS -> {
|
||||
val subjectExpression = expression.left
|
||||
val targetTypeReference = expression.right
|
||||
val operationToken = expression.operationReference.getReferencedNameElementType()
|
||||
if (operationToken == KtTokens.IS_KEYWORD || operationToken == KtTokens.AS_KEYWORD) {
|
||||
IdentifierInfo.NO
|
||||
} else {
|
||||
IdentifierInfo.SafeCast(
|
||||
getIdForStableIdentifier(subjectExpression, bindingContext, containingDeclarationOrModule),
|
||||
bindingContext.getType(subjectExpression),
|
||||
bindingContext[BindingContext.TYPE, targetTypeReference]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is KtSimpleNameExpression ->
|
||||
getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule)
|
||||
|
||||
is KtThisExpression -> {
|
||||
val declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expression.instanceReference)
|
||||
getIdForThisReceiver(declarationDescriptor)
|
||||
}
|
||||
|
||||
is KtPostfixExpression -> {
|
||||
val operationType = expression.operationReference.getReferencedNameElementType()
|
||||
if (operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS)
|
||||
postfix(getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule), operationType)
|
||||
else
|
||||
IdentifierInfo.NO
|
||||
}
|
||||
|
||||
else -> IdentifierInfo.NO
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIdForSimpleNameExpression(
|
||||
simpleNameExpression: KtSimpleNameExpression,
|
||||
bindingContext: BindingContext,
|
||||
containingDeclarationOrModule: DeclarationDescriptor
|
||||
): IdentifierInfo {
|
||||
val declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, simpleNameExpression)
|
||||
return when (declarationDescriptor) {
|
||||
is VariableDescriptor -> {
|
||||
val resolvedCall = simpleNameExpression.getResolvedCall(bindingContext)
|
||||
|
||||
// todo uncomment assert
|
||||
// KT-4113
|
||||
// for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
|
||||
// assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for
|
||||
val usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule)
|
||||
val selectorInfo = IdentifierInfo.Variable(
|
||||
declarationDescriptor,
|
||||
declarationDescriptor.variableKind(usageModuleDescriptor, bindingContext, simpleNameExpression),
|
||||
bindingContext[BindingContext.BOUND_INITIALIZER_VALUE, declarationDescriptor]
|
||||
)
|
||||
|
||||
val implicitReceiver = resolvedCall?.dispatchReceiver
|
||||
if (implicitReceiver == null) {
|
||||
selectorInfo
|
||||
} else {
|
||||
val receiverInfo = getIdForImplicitReceiver(implicitReceiver, simpleNameExpression)
|
||||
|
||||
if (receiverInfo == null) {
|
||||
selectorInfo
|
||||
} else {
|
||||
qualified(
|
||||
receiverInfo, implicitReceiver.type,
|
||||
selectorInfo, resolvedCall.call.isSafeCall()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is PackageViewDescriptor, is ClassDescriptor -> IdentifierInfo.PackageOrClass(declarationDescriptor)
|
||||
|
||||
else -> IdentifierInfo.NO
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIdForImplicitReceiver(receiverValue: ReceiverValue?, expression: KtExpression?) =
|
||||
when (receiverValue) {
|
||||
is ImplicitReceiver -> getIdForThisReceiver(receiverValue.declarationDescriptor)
|
||||
|
||||
is TransientReceiver ->
|
||||
throw AssertionError("Transient receiver is implicit for an explicit expression: $expression. Receiver: $receiverValue")
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun getIdForThisReceiver(descriptorOfThisReceiver: DeclarationDescriptor?) = when (descriptorOfThisReceiver) {
|
||||
is CallableDescriptor -> {
|
||||
val receiverParameter = descriptorOfThisReceiver.extensionReceiverParameter
|
||||
?: error("'This' refers to the callable member without a receiver parameter: $descriptorOfThisReceiver")
|
||||
IdentifierInfo.Receiver(receiverParameter.value)
|
||||
}
|
||||
|
||||
is ClassDescriptor -> IdentifierInfo.Receiver(descriptorOfThisReceiver.thisAsReceiverParameter.value)
|
||||
|
||||
else -> IdentifierInfo.NO
|
||||
}
|
||||
|
||||
private fun postfix(argumentInfo: IdentifierInfo, op: KtToken): IdentifierInfo =
|
||||
if (argumentInfo == IdentifierInfo.NO) IdentifierInfo.NO else IdentifierInfo.PostfixIdentifierInfo(argumentInfo, op)
|
||||
|
||||
private fun qualified(receiverInfo: IdentifierInfo, receiverType: KotlinType?, selectorInfo: IdentifierInfo, safe: Boolean) =
|
||||
when (receiverInfo) {
|
||||
IdentifierInfo.NO -> IdentifierInfo.NO
|
||||
is IdentifierInfo.PackageOrClass -> selectorInfo
|
||||
else -> IdentifierInfo.Qualified(receiverInfo, selectorInfo, safe, receiverType)
|
||||
}
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.isStableValue
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
@@ -163,7 +163,7 @@ fun KtExpression.isStable(context: BindingContext = this.analyze()): Boolean {
|
||||
if (this is KtConstantExpression || this is KtThisExpression) return true
|
||||
val descriptor = BindingContextUtils.extractVariableDescriptorFromReference(context, this)
|
||||
return descriptor is VariableDescriptor &&
|
||||
DataFlowValueFactoryImpl.isStableValue(descriptor, DescriptorUtils.getContainingModule(descriptor))
|
||||
isStableValue(descriptor, DescriptorUtils.getContainingModule(descriptor))
|
||||
}
|
||||
|
||||
data class IfThenToSelectData(
|
||||
|
||||
Reference in New Issue
Block a user