Data flow values: initializers for local variables are now stored as "bound values" with inherited nullability #KT-6840 Fixed
This commit is contained in:
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
@@ -98,6 +99,7 @@ public interface BindingContext {
|
||||
WritableSlice<KtExpression, KotlinType> EXPECTED_EXPRESSION_TYPE = new BasicWritableSlice<KtExpression, KotlinType>(DO_NOTHING);
|
||||
WritableSlice<KtFunction, KotlinType> EXPECTED_RETURN_TYPE = new BasicWritableSlice<KtFunction, KotlinType>(DO_NOTHING);
|
||||
WritableSlice<KtExpression, DataFlowInfo> DATAFLOW_INFO_AFTER_CONDITION = Slices.createSimpleSlice();
|
||||
WritableSlice<VariableDescriptor, DataFlowValue> BOUND_INITIALIZER_VALUE = Slices.createSimpleSlice();
|
||||
WritableSlice<KtExpression, LeakingThisDescriptor> LEAKING_THIS = Slices.createSimpleSlice();
|
||||
|
||||
/**
|
||||
|
||||
@@ -93,17 +93,22 @@ class LocalVariableResolver(
|
||||
typeInfo = facade.getTypeInfo(initializer, context.replaceExpectedType(outType))
|
||||
val dataFlowInfo = typeInfo.dataFlowInfo
|
||||
val type = typeInfo.type
|
||||
// At this moment we do not take initializer value into account if type is given for a property
|
||||
// We can comment first part of this condition to take them into account, like here: var s: String? = "xyz"
|
||||
// In this case s will be not-nullable until it is changed
|
||||
if (property.typeReference == null && type != null) {
|
||||
val variableDataFlowValue = DataFlowValueFactory.createDataFlowValueForProperty(
|
||||
property, propertyDescriptor, context.trace.bindingContext,
|
||||
DescriptorUtils.getContainingModuleOrNull(scope.ownerDescriptor))
|
||||
if (type != null) {
|
||||
val initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context)
|
||||
// We cannot say here anything new about initializerDataFlowValue
|
||||
// except it has the same value as variableDataFlowValue
|
||||
typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue))
|
||||
if (!propertyDescriptor.isVar) {
|
||||
context.trace.record(BindingContext.BOUND_INITIALIZER_VALUE, propertyDescriptor, initializerDataFlowValue)
|
||||
}
|
||||
// At this moment we do not take initializer value into account if type is given for a property
|
||||
// We can comment this condition to take them into account, like here: var s: String? = "xyz"
|
||||
// In this case s will be not-nullable until it is changed
|
||||
if (property.typeReference == null) {
|
||||
val variableDataFlowValue = DataFlowValueFactory.createDataFlowValueForProperty(
|
||||
property, propertyDescriptor, context.trace.bindingContext,
|
||||
DescriptorUtils.getContainingModuleOrNull(scope.ownerDescriptor))
|
||||
// We cannot say here anything new about initializerDataFlowValue
|
||||
// except it has the same value as variableDataFlowValue
|
||||
typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+8
-5
@@ -27,8 +27,8 @@ 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.DECLARATION_TO_DESCRIPTOR
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.*
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||
@@ -119,8 +119,10 @@ object DataFlowValueFactory {
|
||||
bindingContext: BindingContext,
|
||||
usageContainingModule: ModuleDescriptor?
|
||||
) = DataFlowValue(IdentifierInfo.Variable(variableDescriptor,
|
||||
variableKind(variableDescriptor, usageContainingModule, bindingContext, property)),
|
||||
variableDescriptor.type)
|
||||
variableKind(variableDescriptor, usageContainingModule,
|
||||
bindingContext, property),
|
||||
bindingContext[BOUND_INITIALIZER_VALUE, variableDescriptor]),
|
||||
variableDescriptor.type)
|
||||
|
||||
// For only ++ and -- postfix operations
|
||||
private data class PostfixIdentifierInfo(val argumentInfo: IdentifierInfo, val op: KtToken) : IdentifierInfo {
|
||||
@@ -201,7 +203,8 @@ object DataFlowValueFactory {
|
||||
val usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule)
|
||||
val selectorInfo = IdentifierInfo.Variable(declarationDescriptor,
|
||||
variableKind(declarationDescriptor, usageModuleDescriptor,
|
||||
bindingContext, simpleNameExpression))
|
||||
bindingContext, simpleNameExpression),
|
||||
bindingContext[BOUND_INITIALIZER_VALUE, declarationDescriptor])
|
||||
|
||||
val implicitReceiver = resolvedCall?.dispatchReceiver
|
||||
if (implicitReceiver == null) {
|
||||
|
||||
+11
-4
@@ -95,10 +95,17 @@ internal class DelegatingDataFlowInfo private constructor(
|
||||
map.put(value, nullability)
|
||||
|
||||
val identifierInfo = value.identifierInfo
|
||||
if (affectReceiver && !nullability.canBeNull() && identifierInfo is IdentifierInfo.Qualified) {
|
||||
val receiverType = identifierInfo.receiverType
|
||||
if (identifierInfo.safe && receiverType != null) {
|
||||
putNullability(map, DataFlowValue(identifierInfo.receiverInfo, receiverType), nullability)
|
||||
if (affectReceiver && !nullability.canBeNull()) {
|
||||
when (identifierInfo) {
|
||||
is IdentifierInfo.Qualified -> {
|
||||
val receiverType = identifierInfo.receiverType
|
||||
if (identifierInfo.safe && receiverType != null) {
|
||||
putNullability(map, DataFlowValue(identifierInfo.receiverInfo, receiverType), nullability)
|
||||
}
|
||||
}
|
||||
is IdentifierInfo.Variable -> identifierInfo.bound?.let {
|
||||
putNullability(map, it, nullability)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user