diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt index 301201f981d..730ef130ce2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt @@ -14,90 +14,72 @@ * 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.psi.PsiElement; -import com.intellij.psi.tree.IElementType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.KtNodeTypes; -import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider; -import org.jetbrains.kotlin.descriptors.*; -import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; -import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor; -import org.jetbrains.kotlin.lexer.KtTokens; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; -import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.resolve.DescriptorUtils; -import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; -import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind; -import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; -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 com.intellij.openapi.util.Pair +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.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 java.util.Set; - -import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing; -import static org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR; -import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET; -import static org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.*; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing +import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR +import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.* /** * 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. */ -public class DataFlowValueFactory { - private DataFlowValueFactory() { - } +object DataFlowValueFactory { - @NotNull - public static DataFlowValue createDataFlowValue( - @NotNull KtExpression expression, - @NotNull KotlinType type, - @NotNull ResolutionContext resolutionContext - ) { - return createDataFlowValue(expression, type, resolutionContext.trace.getBindingContext(), - resolutionContext.scope.getOwnerDescriptor()); - } + @JvmStatic + fun createDataFlowValue( + expression: KtExpression, + type: KotlinType, + resolutionContext: ResolutionContext<*> + ) = createDataFlowValue(expression, type, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor) - private static boolean isComplexExpression(@NotNull KtExpression expression) { - if (expression instanceof KtBlockExpression || - expression instanceof KtIfExpression || - expression instanceof KtWhenExpression || - (expression instanceof KtBinaryExpression && ((KtBinaryExpression) expression).getOperationToken() == KtTokens.ELVIS)) { - - return true; + 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) } - if (expression instanceof KtParenthesizedExpression) { - KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression); - return deparenthesized != null && isComplexExpression(deparenthesized); - } - return false; + else -> false } - @NotNull - public static DataFlowValue createDataFlowValue( - @NotNull KtExpression expression, - @NotNull KotlinType type, - @NotNull BindingContext bindingContext, - @NotNull DeclarationDescriptor containingDeclarationOrModule - ) { - if (expression instanceof KtConstantExpression) { - KtConstantExpression constantExpression = (KtConstantExpression) expression; - if (constantExpression.getNode().getElementType() == KtNodeTypes.NULL) { - return DataFlowValue.nullValue(DescriptorUtilsKt.getBuiltIns(containingDeclarationOrModule)); + @JvmStatic + 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 (type.isError) return DataFlowValue.ERROR 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))) { @@ -106,368 +88,314 @@ public class DataFlowValueFactory { // // But there are some problem with types built on type parameters, e.g. // fun foo(x: T) = x!!.hashCode() // there no way in type system to denote that `x!!` is not nullable - return new DataFlowValue(expression, - type, - OTHER, - Nullability.NOT_NULL); + return DataFlowValue(expression, + type, + OTHER, + Nullability.NOT_NULL) } if (isComplexExpression(expression)) { - return createDataFlowValueForComplexExpression(expression, type); + return createDataFlowValueForComplexExpression(expression, type) } - IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule); - return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id, - type, - result.kind, - getImmanentNullability(type)); + val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule) + return DataFlowValue(if (result === NO_IDENTIFIER_INFO) expression else result.id, + type, + result.kind, + type.immanentNullability) } - @NotNull - public static DataFlowValue createDataFlowValueForStableReceiver(@NotNull ReceiverValue receiver) { - KotlinType type = receiver.getType(); - return new DataFlowValue(receiver, type, STABLE_VALUE, getImmanentNullability(type)); + @JvmStatic + fun createDataFlowValueForStableReceiver(receiver: ReceiverValue): DataFlowValue { + val type = receiver.type + return DataFlowValue(receiver, type, STABLE_VALUE, type.immanentNullability) } - @NotNull - public static DataFlowValue createDataFlowValue( - @NotNull ReceiverValue receiverValue, - @NotNull ResolutionContext resolutionContext - ) { - return createDataFlowValue(receiverValue, resolutionContext.trace.getBindingContext(), - resolutionContext.scope.getOwnerDescriptor()); + @JvmStatic + fun createDataFlowValue( + receiverValue: ReceiverValue, + resolutionContext: ResolutionContext<*> + ) = createDataFlowValue(receiverValue, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor) + + @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 - public static DataFlowValue createDataFlowValue( - @NotNull ReceiverValue receiverValue, - @NotNull BindingContext bindingContext, - @NotNull DeclarationDescriptor containingDeclarationOrModule - ) { - if (receiverValue instanceof TransientReceiver || receiverValue instanceof ImplicitReceiver) { - return createDataFlowValueForStableReceiver(receiverValue); - } - else if (receiverValue instanceof ExpressionReceiver) { - return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(), - receiverValue.getType(), - bindingContext, - containingDeclarationOrModule); - } - else { - throw new UnsupportedOperationException("Unsupported receiver value: " + receiverValue.getClass().getName()); - } + @JvmStatic + fun createDataFlowValueForProperty( + property: KtProperty, + variableDescriptor: VariableDescriptor, + bindingContext: BindingContext, + usageContainingModule: ModuleDescriptor? + ): DataFlowValue { + val type = variableDescriptor.type + return DataFlowValue(variableDescriptor, type, + variableKind(variableDescriptor, usageContainingModule, + bindingContext, property), + type.immanentNullability) } - @NotNull - public static DataFlowValue createDataFlowValueForProperty( - @NotNull KtProperty property, - @NotNull VariableDescriptor variableDescriptor, - @NotNull BindingContext bindingContext, - @Nullable ModuleDescriptor usageContainingModule - ) { - KotlinType type = variableDescriptor.getType(); - return new DataFlowValue(variableDescriptor, type, - variableKind(variableDescriptor, usageContainingModule, - bindingContext, property), - getImmanentNullability(type)); + private fun createDataFlowValueForComplexExpression( + expression: KtExpression, + type: KotlinType + ) = DataFlowValue(expression, type, Kind.STABLE_COMPLEX_EXPRESSION, type.immanentNullability) + + private val KotlinType.immanentNullability: Nullability + get() = if (TypeUtils.isNullableType(this)) Nullability.UNKNOWN else Nullability.NOT_NULL + + private open class IdentifierInfo internal constructor(val id: Any?, val kind: Kind, val isPackage: Boolean) + + private val NO_IDENTIFIER_INFO = object : IdentifierInfo(null, OTHER, false) { + override fun toString() = "NO_IDENTIFIER_INFO" } - @NotNull - private static DataFlowValue createDataFlowValueForComplexExpression( - @NotNull KtExpression expression, - @NotNull KotlinType type - ) { - return new DataFlowValue(expression, type, Kind.STABLE_COMPLEX_EXPRESSION, getImmanentNullability(type)); - } + private fun createInfo(id: Any, kind: Kind) = IdentifierInfo(id, kind, false) - @NotNull - private static Nullability getImmanentNullability(@NotNull KotlinType type) { - return TypeUtils.isNullableType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL; - } + private fun createStableInfo(id: Any) = createInfo(id, STABLE_VALUE) - private static class IdentifierInfo { - public final Object id; - public final Kind kind; - public final boolean isPackage; + private fun createPackageOrClassInfo(id: Any) = IdentifierInfo(id, STABLE_VALUE, true) - private IdentifierInfo(Object id, Kind kind, boolean isPackage) { - this.id = id; - this.kind = kind; - this.isPackage = isPackage; - } - } + private fun combineInfo(receiverInfo: IdentifierInfo?, selectorInfo: IdentifierInfo) = + if (selectorInfo.id == null || receiverInfo === NO_IDENTIFIER_INFO) { + NO_IDENTIFIER_INFO + } + else if (receiverInfo == null || receiverInfo.isPackage) { + selectorInfo + } + else { + createInfo(Pair.create(receiverInfo.id, selectorInfo.id), + if (receiverInfo.kind.isStable()) selectorInfo.kind else OTHER) + } - private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, OTHER, false) { - @Override - public String toString() { - return "NO_IDENTIFIER_INFO"; - } - }; + private fun createPostfixInfo(expression: KtPostfixExpression, argumentInfo: IdentifierInfo) = + if (argumentInfo === NO_IDENTIFIER_INFO) { + NO_IDENTIFIER_INFO + } + else { + createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.kind) + } - @NotNull - private static IdentifierInfo createInfo(Object id, Kind kind) { - return new IdentifierInfo(id, kind, false); - } - - @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 - ) { + private fun getIdForStableIdentifier( + expression: KtExpression?, + bindingContext: BindingContext, + containingDeclarationOrModule: DeclarationDescriptor + ): IdentifierInfo { if (expression != null) { - KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression); - if (expression != deparenthesized) { - return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule); + val deparenthesized = KtPsiUtil.deparenthesize(expression) + if (expression !== deparenthesized) { + return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule) } } - if (expression instanceof KtQualifiedExpression) { - KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) expression; - KtExpression receiverExpression = qualifiedExpression.getReceiverExpression(); - KtExpression selectorExpression = qualifiedExpression.getSelectorExpression(); - IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule); - IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule); + return when (expression) { + is KtQualifiedExpression -> { + val receiverExpression = expression.receiverExpression + val selectorExpression = expression.selectorExpression + val receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule) + val selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule) - return 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)); + combineInfo(receiverId, selectorId) } - } - return NO_IDENTIFIER_INFO; - } - - @NotNull - private static IdentifierInfo getIdForSimpleNameExpression( - @NotNull KtSimpleNameExpression simpleNameExpression, - @NotNull BindingContext bindingContext, - @NotNull DeclarationDescriptor containingDeclarationOrModule - ) { - DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression); - if (declarationDescriptor instanceof VariableDescriptor) { - ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(simpleNameExpression, 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 - 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; + 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) { + createPostfixInfo(expression, + getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule)) + } + else { + NO_IDENTIFIER_INFO + } + } + else -> NO_IDENTIFIER_INFO } } - @NotNull - private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) { - if (descriptorOfThisReceiver instanceof CallableDescriptor) { - ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter(); - assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + - descriptorOfThisReceiver; - return createStableInfo(receiverParameter.getValue()); + 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 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 static DeclarationDescriptor getVariableContainingDeclaration(@NotNull VariableDescriptor variableDescriptor) { - DeclarationDescriptor containingDeclarationDescriptor = variableDescriptor.getContainingDeclaration(); - if (containingDeclarationDescriptor instanceof ConstructorDescriptor - && ((ConstructorDescriptor) containingDeclarationDescriptor).isPrimary()) { + 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") + 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 // Because of it we have to use class itself instead of initializer, // otherwise we could not find this descriptor inside isAccessedInsideClosure below - containingDeclarationDescriptor = containingDeclarationDescriptor.getContainingDeclaration(); - assert containingDeclarationDescriptor != null : "No containing declaration for primary constructor"; + containingDeclarationDescriptor.containingDeclaration + } + else { + containingDeclarationDescriptor } - return containingDeclarationDescriptor; } - private static boolean isAccessedInsideClosure( - @NotNull DeclarationDescriptor variableContainingDeclaration, - @NotNull BindingContext bindingContext, - @NotNull KtElement accessElement - ) { - KtDeclaration parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement); - if (parent != null) { - DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, parent); + 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 - return !variableContainingDeclaration.equals(descriptor); - } - return false; + variableContainingDeclaration != bindingContext.get(DECLARATION_TO_DESCRIPTOR, parent) + else + false } - private static boolean isAccessedBeforeAllClosureWriters( - @NotNull DeclarationDescriptor variableContainingDeclaration, - @NotNull Set writers, - @NotNull BindingContext bindingContext, - @NotNull KtElement accessElement - ) { + private fun isAccessedBeforeAllClosureWriters( + variableContainingDeclaration: DeclarationDescriptor, + writers: Set, + 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 - for (KtDeclaration writer : writers) { - DeclarationDescriptor writerDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, writer); + writers.filterNotNull().forEach { writer -> + val writerDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, writer) // Access is after some writer - if (!variableContainingDeclaration.equals(writerDescriptor) && !PsiUtilsKt.before(accessElement, writer)) { - return false; + if (variableContainingDeclaration != writerDescriptor && !accessElement.before(writer)) { + return false } } // Access is before all writers - return true; + return true } - private static Kind propertyKind(@NotNull PropertyDescriptor propertyDescriptor, @Nullable ModuleDescriptor usageModule) { - if (propertyDescriptor.isVar()) return MUTABLE_PROPERTY; - if (ModalityKt.isOverridable(propertyDescriptor)) return PROPERTY_WITH_GETTER; - if (!hasDefaultGetter(propertyDescriptor)) return PROPERTY_WITH_GETTER; + 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)) { - ModuleDescriptor declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor); - if (usageModule == null || !usageModule.equals(declarationModule)) { - return ALIEN_PUBLIC_PROPERTY; + val declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor) + if (usageModule == null || usageModule != declarationModule) { + return ALIEN_PUBLIC_PROPERTY } } - return STABLE_VALUE; + return STABLE_VALUE } - private static Kind variableKind( - @NotNull VariableDescriptor variableDescriptor, - @Nullable ModuleDescriptor usageModule, - @NotNull BindingContext bindingContext, - @NotNull KtElement accessElement - ) { - if (variableDescriptor instanceof PropertyDescriptor) { - return propertyKind((PropertyDescriptor) variableDescriptor, usageModule); + private fun variableKind( + variableDescriptor: VariableDescriptor, + usageModule: ModuleDescriptor?, + bindingContext: BindingContext, + accessElement: KtElement + ): Kind { + if (variableDescriptor is PropertyDescriptor) { + return propertyKind(variableDescriptor, usageModule) } - if (!(variableDescriptor instanceof LocalVariableDescriptor) && !(variableDescriptor instanceof ParameterDescriptor)) return OTHER; - if (!variableDescriptor.isVar()) return STABLE_VALUE; - if (variableDescriptor instanceof SyntheticFieldDescriptor) return MUTABLE_PROPERTY; + 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: PREDICTABLE or UNPREDICTABLE - PreliminaryDeclarationVisitor preliminaryVisitor = - PreliminaryDeclarationVisitor.Companion.getVisitorByVariable(variableDescriptor, bindingContext); + val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(variableDescriptor, bindingContext) + ?: return UNPREDICTABLE_VARIABLE // A case when we just analyse an expression alone: counts as unpredictable - if (preliminaryVisitor == null) return UNPREDICTABLE_VARIABLE; // Analyze who writes variable // If there is no writer: predictable - Set writers = preliminaryVisitor.writers(variableDescriptor); - if (writers.isEmpty()) return PREDICTABLE_VARIABLE; + val writers = preliminaryVisitor.writers(variableDescriptor) + if (writers.isEmpty()) return PREDICTABLE_VARIABLE // If access element is inside closure: unpredictable - DeclarationDescriptor variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor); - if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return UNPREDICTABLE_VARIABLE; + val variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor) + if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return UNPREDICTABLE_VARIABLE // Otherwise, predictable iff considered position is BEFORE all writers except declarer itself - if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement)) return PREDICTABLE_VARIABLE; - else return UNPREDICTABLE_VARIABLE; + return if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement)) + PREDICTABLE_VARIABLE + else + UNPREDICTABLE_VARIABLE } /** * 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 */ - public static boolean isStableValue( - @NotNull VariableDescriptor variableDescriptor, - @Nullable ModuleDescriptor usageModule - ) { - if (variableDescriptor.isVar()) return false; - if (variableDescriptor instanceof PropertyDescriptor) { - return propertyKind((PropertyDescriptor) variableDescriptor, usageModule) == STABLE_VALUE; - } - return true; + fun isStableValue( + variableDescriptor: VariableDescriptor, + usageModule: ModuleDescriptor? + ): Boolean { + if (variableDescriptor.isVar) return false + return variableDescriptor !is PropertyDescriptor || propertyKind(variableDescriptor, usageModule) === STABLE_VALUE } - private static boolean invisibleFromOtherModules(@NotNull DeclarationDescriptorWithVisibility descriptor) { - if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.getVisibility())) return true; + private fun invisibleFromOtherModules(descriptor: DeclarationDescriptorWithVisibility): Boolean { + if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.visibility)) return true - DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); - if (!(containingDeclaration instanceof DeclarationDescriptorWithVisibility)) { - return false; - } - - return invisibleFromOtherModules((DeclarationDescriptorWithVisibility) containingDeclaration); + val containingDeclaration = descriptor.containingDeclaration + return containingDeclaration is DeclarationDescriptorWithVisibility && invisibleFromOtherModules(containingDeclaration) } - private static boolean hasDefaultGetter(PropertyDescriptor propertyDescriptor) { - PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); - return getter == null || getter.isDefault(); + private fun hasDefaultGetter(propertyDescriptor: PropertyDescriptor): Boolean { + val getter = propertyDescriptor.getter + return getter == null || getter.isDefault } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/AssignedVariablesSearcher.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/AssignedVariablesSearcher.kt index 83211919dbb..6e70e5786d6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/AssignedVariablesSearcher.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/AssignedVariablesSearcher.kt @@ -27,7 +27,7 @@ abstract class AssignedVariablesSearcher: KtTreeVisitorVoid() { private val assignedNames: SetMultimap = LinkedHashMultimap.create() - open fun writers(variableDescriptor: VariableDescriptor) = assignedNames[variableDescriptor.name] + open fun writers(variableDescriptor: VariableDescriptor): MutableSet = assignedNames[variableDescriptor.name] fun hasWriters(variableDescriptor: VariableDescriptor) = writers(variableDescriptor).isNotEmpty()