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 8552bf4efbf..a639c400ffb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.kt @@ -16,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 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, - bindingContext: BindingContext - ): Boolean { - return writers.none { (_, writerDeclaration) -> - val writerDescriptor = writerDeclaration?.let { - ControlFlowInformationProvider.getDeclarationDescriptorIncludingConstructors(bindingContext, it) - } - writerDeclaration != null && variableContainingDeclaration != writerDescriptor - } - } - - private fun isAccessedInsideClosureAfterAllWriters( - writers: Set, - accessElement: KtElement - ): Boolean { - val parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement) ?: return false - return writers.none { (assignment) -> !assignment.before(parent) } - } - - 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 - 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) - } - } -} +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactoryImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactoryImpl.kt new file mode 100644 index 00000000000..2582ae3310c --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactoryImpl.kt @@ -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 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 + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueKindUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueKindUtils.kt new file mode 100644 index 00000000000..ee3aee9c8ee --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueKindUtils.kt @@ -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, + bindingContext: BindingContext +): Boolean { + return writers.none { (_, writerDeclaration) -> + val writerDescriptor = writerDeclaration?.let { + ControlFlowInformationProvider.getDeclarationDescriptorIncludingConstructors(bindingContext, it) + } + writerDeclaration != null && variableContainingDeclaration != writerDescriptor + } +} + +private fun isAccessedInsideClosureAfterAllWriters( + writers: Set, + accessElement: KtElement +): Boolean { + val parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement) ?: return false + return writers.none { (assignment) -> !assignment.before(parent) } +} + +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 + 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 +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt index d018bf46e00..83808ab5692 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt @@ -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) } } -} \ No newline at end of file + 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) + } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt index 3baf5190157..b1c2d2cb933 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt @@ -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(