Data flow values: initializers for local variables are now stored as "bound values" with inherited nullability #KT-6840 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-07-25 12:03:55 +03:00
parent 7090abddcd
commit 4f7d8e34b0
9 changed files with 74 additions and 26 deletions
@@ -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 {
@@ -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) {
@@ -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)
}
}
}