DataFlowValueFactory: converted to Kotlin

This commit is contained in:
Mikhail Glukhikh
2016-07-18 14:14:24 +03:00
committed by Mikhail Glukhikh
parent b1a2a0723c
commit 7f9b9ddb45
2 changed files with 279 additions and 351 deletions
@@ -14,90 +14,72 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.resolve.calls.smartcasts; package org.jetbrains.kotlin.resolve.calls.smartcasts
import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pair
import com.intellij.psi.PsiElement; import org.jetbrains.kotlin.KtNodeTypes
import com.intellij.psi.tree.IElementType; import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.KtNodeTypes; import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider; import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor; import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.calls.callUtil.*
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind; import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
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 java.util.Set; import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing; import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
import static org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.*
import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
import static org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.*;
/** /**
* 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.
* Then data flow values serve as keys to obtain data flow information for these expressions. * Then data flow values serve as keys to obtain data flow information for these expressions.
*/ */
public class DataFlowValueFactory { object DataFlowValueFactory {
private DataFlowValueFactory() {
}
@NotNull @JvmStatic
public static DataFlowValue createDataFlowValue( fun createDataFlowValue(
@NotNull KtExpression expression, expression: KtExpression,
@NotNull KotlinType type, type: KotlinType,
@NotNull ResolutionContext resolutionContext resolutionContext: ResolutionContext<*>
) { ) = createDataFlowValue(expression, type, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
return createDataFlowValue(expression, type, resolutionContext.trace.getBindingContext(),
resolutionContext.scope.getOwnerDescriptor());
}
private static boolean isComplexExpression(@NotNull KtExpression expression) { private fun isComplexExpression(expression: KtExpression): Boolean = when(expression) {
if (expression instanceof KtBlockExpression || is KtBlockExpression, is KtIfExpression, is KtWhenExpression -> true
expression instanceof KtIfExpression || is KtBinaryExpression -> expression.operationToken === KtTokens.ELVIS
expression instanceof KtWhenExpression || is KtParenthesizedExpression -> {
(expression instanceof KtBinaryExpression && ((KtBinaryExpression) expression).getOperationToken() == KtTokens.ELVIS)) { val deparenthesized = KtPsiUtil.deparenthesize(expression)
deparenthesized != null && isComplexExpression(deparenthesized)
return true;
} }
if (expression instanceof KtParenthesizedExpression) { else -> false
KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression);
return deparenthesized != null && isComplexExpression(deparenthesized);
}
return false;
} }
@NotNull @JvmStatic
public static DataFlowValue createDataFlowValue( fun createDataFlowValue(
@NotNull KtExpression expression, expression: KtExpression,
@NotNull KotlinType type, type: KotlinType,
@NotNull BindingContext bindingContext, bindingContext: BindingContext,
@NotNull DeclarationDescriptor containingDeclarationOrModule containingDeclarationOrModule: DeclarationDescriptor
) { ): DataFlowValue {
if (expression instanceof KtConstantExpression) { if (expression is KtConstantExpression) {
KtConstantExpression constantExpression = (KtConstantExpression) expression; if (expression.node.elementType === KtNodeTypes.NULL) {
if (constantExpression.getNode().getElementType() == KtNodeTypes.NULL) { return DataFlowValue.nullValue(containingDeclarationOrModule.builtIns)
return DataFlowValue.nullValue(DescriptorUtilsKt.getBuiltIns(containingDeclarationOrModule));
} }
} }
if (type.isError()) return DataFlowValue.ERROR; if (type.isError) return DataFlowValue.ERROR
if (isNullableNothing(type)) { if (isNullableNothing(type)) {
return DataFlowValue.nullValue(DescriptorUtilsKt.getBuiltIns(containingDeclarationOrModule)); // 'null' is the only inhabitant of 'Nothing?' return DataFlowValue.nullValue(containingDeclarationOrModule.builtIns) // 'null' is the only inhabitant of 'Nothing?'
} }
if (ExpressionTypingUtils.isExclExclExpression(KtPsiUtil.deparenthesize(expression))) { if (ExpressionTypingUtils.isExclExclExpression(KtPsiUtil.deparenthesize(expression))) {
@@ -106,368 +88,314 @@ public class 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 new DataFlowValue(expression, return DataFlowValue(expression,
type, type,
OTHER, OTHER,
Nullability.NOT_NULL); Nullability.NOT_NULL)
} }
if (isComplexExpression(expression)) { if (isComplexExpression(expression)) {
return createDataFlowValueForComplexExpression(expression, type); return createDataFlowValueForComplexExpression(expression, type)
} }
IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule); val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id, return DataFlowValue(if (result === NO_IDENTIFIER_INFO) expression else result.id,
type, type,
result.kind, result.kind,
getImmanentNullability(type)); type.immanentNullability)
} }
@NotNull @JvmStatic
public static DataFlowValue createDataFlowValueForStableReceiver(@NotNull ReceiverValue receiver) { fun createDataFlowValueForStableReceiver(receiver: ReceiverValue): DataFlowValue {
KotlinType type = receiver.getType(); val type = receiver.type
return new DataFlowValue(receiver, type, STABLE_VALUE, getImmanentNullability(type)); return DataFlowValue(receiver, type, STABLE_VALUE, type.immanentNullability)
} }
@NotNull @JvmStatic
public static DataFlowValue createDataFlowValue( fun createDataFlowValue(
@NotNull ReceiverValue receiverValue, receiverValue: ReceiverValue,
@NotNull ResolutionContext resolutionContext resolutionContext: ResolutionContext<*>
) { ) = createDataFlowValue(receiverValue, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
return createDataFlowValue(receiverValue, resolutionContext.trace.getBindingContext(),
resolutionContext.scope.getOwnerDescriptor()); @JvmStatic
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.javaClass.name)
} }
@NotNull @JvmStatic
public static DataFlowValue createDataFlowValue( fun createDataFlowValueForProperty(
@NotNull ReceiverValue receiverValue, property: KtProperty,
@NotNull BindingContext bindingContext, variableDescriptor: VariableDescriptor,
@NotNull DeclarationDescriptor containingDeclarationOrModule bindingContext: BindingContext,
) { usageContainingModule: ModuleDescriptor?
if (receiverValue instanceof TransientReceiver || receiverValue instanceof ImplicitReceiver) { ): DataFlowValue {
return createDataFlowValueForStableReceiver(receiverValue); val type = variableDescriptor.type
} return DataFlowValue(variableDescriptor, type,
else if (receiverValue instanceof ExpressionReceiver) { variableKind(variableDescriptor, usageContainingModule,
return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(), bindingContext, property),
receiverValue.getType(), type.immanentNullability)
bindingContext,
containingDeclarationOrModule);
}
else {
throw new UnsupportedOperationException("Unsupported receiver value: " + receiverValue.getClass().getName());
}
} }
@NotNull private fun createDataFlowValueForComplexExpression(
public static DataFlowValue createDataFlowValueForProperty( expression: KtExpression,
@NotNull KtProperty property, type: KotlinType
@NotNull VariableDescriptor variableDescriptor, ) = DataFlowValue(expression, type, Kind.STABLE_COMPLEX_EXPRESSION, type.immanentNullability)
@NotNull BindingContext bindingContext,
@Nullable ModuleDescriptor usageContainingModule private val KotlinType.immanentNullability: Nullability
) { get() = if (TypeUtils.isNullableType(this)) Nullability.UNKNOWN else Nullability.NOT_NULL
KotlinType type = variableDescriptor.getType();
return new DataFlowValue(variableDescriptor, type, private open class IdentifierInfo internal constructor(val id: Any?, val kind: Kind, val isPackage: Boolean)
variableKind(variableDescriptor, usageContainingModule,
bindingContext, property), private val NO_IDENTIFIER_INFO = object : IdentifierInfo(null, OTHER, false) {
getImmanentNullability(type)); override fun toString() = "NO_IDENTIFIER_INFO"
} }
@NotNull private fun createInfo(id: Any, kind: Kind) = IdentifierInfo(id, kind, false)
private static DataFlowValue createDataFlowValueForComplexExpression(
@NotNull KtExpression expression,
@NotNull KotlinType type
) {
return new DataFlowValue(expression, type, Kind.STABLE_COMPLEX_EXPRESSION, getImmanentNullability(type));
}
@NotNull private fun createStableInfo(id: Any) = createInfo(id, STABLE_VALUE)
private static Nullability getImmanentNullability(@NotNull KotlinType type) {
return TypeUtils.isNullableType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
}
private static class IdentifierInfo { private fun createPackageOrClassInfo(id: Any) = IdentifierInfo(id, STABLE_VALUE, true)
public final Object id;
public final Kind kind;
public final boolean isPackage;
private IdentifierInfo(Object id, Kind kind, boolean isPackage) { private fun combineInfo(receiverInfo: IdentifierInfo?, selectorInfo: IdentifierInfo) =
this.id = id; if (selectorInfo.id == null || receiverInfo === NO_IDENTIFIER_INFO) {
this.kind = kind; NO_IDENTIFIER_INFO
this.isPackage = isPackage; }
} else if (receiverInfo == null || receiverInfo.isPackage) {
} selectorInfo
}
else {
createInfo(Pair.create<Any, Any>(receiverInfo.id, selectorInfo.id),
if (receiverInfo.kind.isStable()) selectorInfo.kind else OTHER)
}
private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, OTHER, false) { private fun createPostfixInfo(expression: KtPostfixExpression, argumentInfo: IdentifierInfo) =
@Override if (argumentInfo === NO_IDENTIFIER_INFO) {
public String toString() { NO_IDENTIFIER_INFO
return "NO_IDENTIFIER_INFO"; }
} else {
}; createInfo(Pair.create<KtPostfixExpression, Any>(expression, argumentInfo.id), argumentInfo.kind)
}
@NotNull private fun getIdForStableIdentifier(
private static IdentifierInfo createInfo(Object id, Kind kind) { expression: KtExpression?,
return new IdentifierInfo(id, kind, false); bindingContext: BindingContext,
} containingDeclarationOrModule: DeclarationDescriptor
): IdentifierInfo {
@NotNull
private static IdentifierInfo createStableInfo(Object id) {
return createInfo(id, STABLE_VALUE);
}
@NotNull
private static IdentifierInfo createPackageOrClassInfo(Object id) {
return new IdentifierInfo(id, STABLE_VALUE, true);
}
@NotNull
private static IdentifierInfo combineInfo(@Nullable IdentifierInfo receiverInfo, @NotNull IdentifierInfo selectorInfo) {
if (selectorInfo.id == null || receiverInfo == NO_IDENTIFIER_INFO) {
return NO_IDENTIFIER_INFO;
}
if (receiverInfo == null || receiverInfo.isPackage) {
return selectorInfo;
}
return createInfo(Pair.create(receiverInfo.id, selectorInfo.id),
receiverInfo.kind.isStable() ? selectorInfo.kind : OTHER);
}
@NotNull
private static IdentifierInfo createPostfixInfo(@NotNull KtPostfixExpression expression, @NotNull IdentifierInfo argumentInfo) {
if (argumentInfo == NO_IDENTIFIER_INFO) {
return NO_IDENTIFIER_INFO;
}
return createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.kind);
}
@NotNull
private static IdentifierInfo getIdForStableIdentifier(
@Nullable KtExpression expression,
@NotNull BindingContext bindingContext,
@NotNull DeclarationDescriptor containingDeclarationOrModule
) {
if (expression != null) { if (expression != null) {
KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression); val deparenthesized = KtPsiUtil.deparenthesize(expression)
if (expression != deparenthesized) { if (expression !== deparenthesized) {
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule); return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule)
} }
} }
if (expression instanceof KtQualifiedExpression) { return when (expression) {
KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) expression; is KtQualifiedExpression -> {
KtExpression receiverExpression = qualifiedExpression.getReceiverExpression(); val receiverExpression = expression.receiverExpression
KtExpression selectorExpression = qualifiedExpression.getSelectorExpression(); val selectorExpression = expression.selectorExpression
IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule); val receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule)
IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule); val selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule)
return combineInfo(receiverId, selectorId); combineInfo(receiverId, selectorId)
}
if (expression instanceof KtSimpleNameExpression) {
return getIdForSimpleNameExpression((KtSimpleNameExpression) expression, bindingContext, containingDeclarationOrModule);
}
else if (expression instanceof KtThisExpression) {
KtThisExpression thisExpression = (KtThisExpression) expression;
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getInstanceReference());
return getIdForThisReceiver(declarationDescriptor);
}
else if (expression instanceof KtPostfixExpression) {
KtPostfixExpression postfixExpression = (KtPostfixExpression) expression;
IElementType operationType = postfixExpression.getOperationReference().getReferencedNameElementType();
if (operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS) {
return createPostfixInfo(postfixExpression,
getIdForStableIdentifier(postfixExpression.getBaseExpression(), bindingContext, containingDeclarationOrModule));
} }
} is KtSimpleNameExpression ->
return NO_IDENTIFIER_INFO; getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule)
} is KtThisExpression -> {
val declarationDescriptor = bindingContext.get(REFERENCE_TARGET, expression.instanceReference)
@NotNull getIdForThisReceiver(declarationDescriptor)
private static IdentifierInfo getIdForSimpleNameExpression( }
@NotNull KtSimpleNameExpression simpleNameExpression, is KtPostfixExpression -> {
@NotNull BindingContext bindingContext, val operationType = expression.operationReference.getReferencedNameElementType()
@NotNull DeclarationDescriptor containingDeclarationOrModule if (operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS) {
) { createPostfixInfo(expression,
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression); getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule))
if (declarationDescriptor instanceof VariableDescriptor) { }
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(simpleNameExpression, bindingContext); else {
NO_IDENTIFIER_INFO
// todo uncomment assert }
// KT-4113 }
// for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes else -> NO_IDENTIFIER_INFO
// assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for
ModuleDescriptor usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule);
IdentifierInfo receiverInfo =
resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null;
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
return combineInfo(receiverInfo,
createInfo(variableDescriptor,
variableKind(variableDescriptor, usageModuleDescriptor,
bindingContext, simpleNameExpression)));
}
if (declarationDescriptor instanceof PackageViewDescriptor || declarationDescriptor instanceof ClassDescriptor) {
return createPackageOrClassInfo(declarationDescriptor);
}
return NO_IDENTIFIER_INFO;
}
@Nullable
private static IdentifierInfo getIdForImplicitReceiver(@Nullable ReceiverValue receiverValue, @Nullable KtExpression expression) {
if (receiverValue instanceof ImplicitReceiver) {
return getIdForThisReceiver(((ImplicitReceiver) receiverValue).getDeclarationDescriptor());
}
else {
assert !(receiverValue instanceof TransientReceiver)
: "Transient receiver is implicit for an explicit expression: " + expression + ". Receiver: " + receiverValue;
// For ExpressionReceiver there is an explicit "this" expression and it was analyzed earlier
return null;
} }
} }
@NotNull private fun getIdForSimpleNameExpression(
private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) { simpleNameExpression: KtSimpleNameExpression,
if (descriptorOfThisReceiver instanceof CallableDescriptor) { bindingContext: BindingContext,
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter(); containingDeclarationOrModule: DeclarationDescriptor
assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + ): IdentifierInfo {
descriptorOfThisReceiver; val declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression)
return createStableInfo(receiverParameter.getValue()); 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 receiverInfo = resolvedCall?.let { getIdForImplicitReceiver(it.dispatchReceiver, simpleNameExpression) }
combineInfo(receiverInfo, createInfo(declarationDescriptor,
variableKind(declarationDescriptor, usageModuleDescriptor,
bindingContext, simpleNameExpression)))
}
is PackageViewDescriptor, is ClassDescriptor -> createPackageOrClassInfo(declarationDescriptor)
else -> NO_IDENTIFIER_INFO
} }
if (descriptorOfThisReceiver instanceof ClassDescriptor) {
return createStableInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue());
}
return NO_IDENTIFIER_INFO;
} }
@NotNull private fun getIdForImplicitReceiver(receiverValue: ReceiverValue?, expression: KtExpression?) =
private static DeclarationDescriptor getVariableContainingDeclaration(@NotNull VariableDescriptor variableDescriptor) { when (receiverValue) {
DeclarationDescriptor containingDeclarationDescriptor = variableDescriptor.getContainingDeclaration(); is ImplicitReceiver -> getIdForThisReceiver(receiverValue.declarationDescriptor)
if (containingDeclarationDescriptor instanceof ConstructorDescriptor is TransientReceiver ->
&& ((ConstructorDescriptor) containingDeclarationDescriptor).isPrimary()) { 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")
createStableInfo(receiverParameter.value)
}
is ClassDescriptor -> createStableInfo(descriptorOfThisReceiver.thisAsReceiverParameter.value)
else -> NO_IDENTIFIER_INFO
}
private fun getVariableContainingDeclaration(variableDescriptor: VariableDescriptor): DeclarationDescriptor {
val containingDeclarationDescriptor = variableDescriptor.containingDeclaration
return if (containingDeclarationDescriptor is ConstructorDescriptor && containingDeclarationDescriptor.isPrimary) {
// This code is necessary just because JetClassInitializer has no associated descriptor in trace // This code is necessary just because JetClassInitializer has no associated descriptor in trace
// Because of it we have to use class itself instead of initializer, // Because of it we have to use class itself instead of initializer,
// otherwise we could not find this descriptor inside isAccessedInsideClosure below // otherwise we could not find this descriptor inside isAccessedInsideClosure below
containingDeclarationDescriptor = containingDeclarationDescriptor.getContainingDeclaration(); containingDeclarationDescriptor.containingDeclaration
assert containingDeclarationDescriptor != null : "No containing declaration for primary constructor"; }
else {
containingDeclarationDescriptor
} }
return containingDeclarationDescriptor;
} }
private static boolean isAccessedInsideClosure( private fun isAccessedInsideClosure(
@NotNull DeclarationDescriptor variableContainingDeclaration, variableContainingDeclaration: DeclarationDescriptor,
@NotNull BindingContext bindingContext, bindingContext: BindingContext,
@NotNull KtElement accessElement accessElement: KtElement
) { ): Boolean {
KtDeclaration parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement); val parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement)
if (parent != null) { return if (parent != null)
DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, parent);
// Access is at the same declaration: not in closure, lower: in closure // Access is at the same declaration: not in closure, lower: in closure
return !variableContainingDeclaration.equals(descriptor); variableContainingDeclaration != bindingContext.get(DECLARATION_TO_DESCRIPTOR, parent)
} else
return false; false
} }
private static boolean isAccessedBeforeAllClosureWriters( private fun isAccessedBeforeAllClosureWriters(
@NotNull DeclarationDescriptor variableContainingDeclaration, variableContainingDeclaration: DeclarationDescriptor,
@NotNull Set<KtDeclaration> writers, writers: Set<KtDeclaration?>,
@NotNull BindingContext bindingContext, bindingContext: BindingContext,
@NotNull KtElement accessElement accessElement: KtElement
) { ): Boolean {
// All writers should be before access element, with the exception: // All writers should be before access element, with the exception:
// writer which is the same with declaration site does not count // writer which is the same with declaration site does not count
for (KtDeclaration writer : writers) { writers.filterNotNull().forEach { writer ->
DeclarationDescriptor writerDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, writer); val writerDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, writer)
// Access is after some writer // Access is after some writer
if (!variableContainingDeclaration.equals(writerDescriptor) && !PsiUtilsKt.before(accessElement, writer)) { if (variableContainingDeclaration != writerDescriptor && !accessElement.before(writer)) {
return false; return false
} }
} }
// Access is before all writers // Access is before all writers
return true; return true
} }
private static Kind propertyKind(@NotNull PropertyDescriptor propertyDescriptor, @Nullable ModuleDescriptor usageModule) { private fun propertyKind(propertyDescriptor: PropertyDescriptor, usageModule: ModuleDescriptor?): Kind {
if (propertyDescriptor.isVar()) return MUTABLE_PROPERTY; if (propertyDescriptor.isVar) return MUTABLE_PROPERTY
if (ModalityKt.isOverridable(propertyDescriptor)) return PROPERTY_WITH_GETTER; if (propertyDescriptor.isOverridable) return PROPERTY_WITH_GETTER
if (!hasDefaultGetter(propertyDescriptor)) return PROPERTY_WITH_GETTER; if (!hasDefaultGetter(propertyDescriptor)) return PROPERTY_WITH_GETTER
if (!invisibleFromOtherModules(propertyDescriptor)) { if (!invisibleFromOtherModules(propertyDescriptor)) {
ModuleDescriptor declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor); val declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor)
if (usageModule == null || !usageModule.equals(declarationModule)) { if (usageModule == null || usageModule != declarationModule) {
return ALIEN_PUBLIC_PROPERTY; return ALIEN_PUBLIC_PROPERTY
} }
} }
return STABLE_VALUE; return STABLE_VALUE
} }
private static Kind variableKind( private fun variableKind(
@NotNull VariableDescriptor variableDescriptor, variableDescriptor: VariableDescriptor,
@Nullable ModuleDescriptor usageModule, usageModule: ModuleDescriptor?,
@NotNull BindingContext bindingContext, bindingContext: BindingContext,
@NotNull KtElement accessElement accessElement: KtElement
) { ): Kind {
if (variableDescriptor instanceof PropertyDescriptor) { if (variableDescriptor is PropertyDescriptor) {
return propertyKind((PropertyDescriptor) variableDescriptor, usageModule); return propertyKind(variableDescriptor, usageModule)
} }
if (!(variableDescriptor instanceof LocalVariableDescriptor) && !(variableDescriptor instanceof ParameterDescriptor)) return OTHER; if (variableDescriptor !is LocalVariableDescriptor && variableDescriptor !is ParameterDescriptor) return OTHER
if (!variableDescriptor.isVar()) return STABLE_VALUE; if (!variableDescriptor.isVar) return STABLE_VALUE
if (variableDescriptor instanceof SyntheticFieldDescriptor) return MUTABLE_PROPERTY; if (variableDescriptor is SyntheticFieldDescriptor) return MUTABLE_PROPERTY
// Local variable classification: PREDICTABLE or UNPREDICTABLE // Local variable classification: PREDICTABLE or UNPREDICTABLE
PreliminaryDeclarationVisitor preliminaryVisitor = val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(variableDescriptor, bindingContext)
PreliminaryDeclarationVisitor.Companion.getVisitorByVariable(variableDescriptor, bindingContext); ?: return UNPREDICTABLE_VARIABLE
// A case when we just analyse an expression alone: counts as unpredictable // A case when we just analyse an expression alone: counts as unpredictable
if (preliminaryVisitor == null) return UNPREDICTABLE_VARIABLE;
// Analyze who writes variable // Analyze who writes variable
// If there is no writer: predictable // If there is no writer: predictable
Set<KtDeclaration> writers = preliminaryVisitor.writers(variableDescriptor); val writers = preliminaryVisitor.writers(variableDescriptor)
if (writers.isEmpty()) return PREDICTABLE_VARIABLE; if (writers.isEmpty()) return PREDICTABLE_VARIABLE
// If access element is inside closure: unpredictable // If access element is inside closure: unpredictable
DeclarationDescriptor variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor); val variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor)
if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return UNPREDICTABLE_VARIABLE; if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return UNPREDICTABLE_VARIABLE
// Otherwise, predictable iff considered position is BEFORE all writers except declarer itself // Otherwise, predictable iff considered position is BEFORE all writers except declarer itself
if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement)) return PREDICTABLE_VARIABLE; return if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement))
else return UNPREDICTABLE_VARIABLE; PREDICTABLE_VARIABLE
else
UNPREDICTABLE_VARIABLE
} }
/** /**
* Determines whether a variable with a given descriptor is stable or not at the given usage place. * Determines whether a variable with a given descriptor is stable or not at the given usage place.
* <p/> *
*
* Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val). * Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
* <p/> *
*
* If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND * 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 * 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). * (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". * 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 variableDescriptor descriptor of a considered variable
* *
* @param usageModule a module with a considered usage place, or null if it's not known (not recommended) * @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 * @return true if variable is stable, false otherwise
*/ */
public static boolean isStableValue( fun isStableValue(
@NotNull VariableDescriptor variableDescriptor, variableDescriptor: VariableDescriptor,
@Nullable ModuleDescriptor usageModule usageModule: ModuleDescriptor?
) { ): Boolean {
if (variableDescriptor.isVar()) return false; if (variableDescriptor.isVar) return false
if (variableDescriptor instanceof PropertyDescriptor) { return variableDescriptor !is PropertyDescriptor || propertyKind(variableDescriptor, usageModule) === STABLE_VALUE
return propertyKind((PropertyDescriptor) variableDescriptor, usageModule) == STABLE_VALUE;
}
return true;
} }
private static boolean invisibleFromOtherModules(@NotNull DeclarationDescriptorWithVisibility descriptor) { private fun invisibleFromOtherModules(descriptor: DeclarationDescriptorWithVisibility): Boolean {
if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.getVisibility())) return true; if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.visibility)) return true
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); val containingDeclaration = descriptor.containingDeclaration
if (!(containingDeclaration instanceof DeclarationDescriptorWithVisibility)) { return containingDeclaration is DeclarationDescriptorWithVisibility && invisibleFromOtherModules(containingDeclaration)
return false;
}
return invisibleFromOtherModules((DeclarationDescriptorWithVisibility) containingDeclaration);
} }
private static boolean hasDefaultGetter(PropertyDescriptor propertyDescriptor) { private fun hasDefaultGetter(propertyDescriptor: PropertyDescriptor): Boolean {
PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); val getter = propertyDescriptor.getter
return getter == null || getter.isDefault(); return getter == null || getter.isDefault
} }
} }
@@ -27,7 +27,7 @@ abstract class AssignedVariablesSearcher: KtTreeVisitorVoid() {
private val assignedNames: SetMultimap<Name, KtDeclaration?> = LinkedHashMultimap.create() private val assignedNames: SetMultimap<Name, KtDeclaration?> = LinkedHashMultimap.create()
open fun writers(variableDescriptor: VariableDescriptor) = assignedNames[variableDescriptor.name] open fun writers(variableDescriptor: VariableDescriptor): MutableSet<KtDeclaration?> = assignedNames[variableDescriptor.name]
fun hasWriters(variableDescriptor: VariableDescriptor) = writers(variableDescriptor).isNotEmpty() fun hasWriters(variableDescriptor: VariableDescriptor) = writers(variableDescriptor).isNotEmpty()