Data flow values refactoring: removed DataFlowValue.kind.isStable, renamed DataFlowValue.isPredictable -> DataFlowValue.isStable

This commit is contained in:
Mikhail Glukhikh
2016-07-27 16:16:38 +03:00
parent 915e36cb02
commit 6e391854a0
22 changed files with 70 additions and 75 deletions
@@ -81,7 +81,7 @@ object RuntimeAssertionsTypeChecker : AdditionalTypeChecker {
expressionType, expressionType,
object : RuntimeAssertionInfo.DataFlowExtras { object : RuntimeAssertionInfo.DataFlowExtras {
override val canBeNull: Boolean override val canBeNull: Boolean
get() = c.dataFlowInfo.getPredictableNullability(dataFlowValue).canBeNull() get() = c.dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()
override val possibleTypes: Set<KotlinType> override val possibleTypes: Set<KotlinType>
get() = c.dataFlowInfo.getCollectedTypes(dataFlowValue) get() = c.dataFlowInfo.getCollectedTypes(dataFlowValue)
override val presentableText: String override val presentableText: String
@@ -46,7 +46,7 @@ object ProtectedSyntheticExtensionCallChecker : CallChecker {
if (resolvedCall.dispatchReceiver != null && resolvedCall.extensionReceiver !is ReceiverValue) return if (resolvedCall.dispatchReceiver != null && resolvedCall.extensionReceiver !is ReceiverValue) return
val receiverValue = resolvedCall.extensionReceiver as ReceiverValue val receiverValue = resolvedCall.extensionReceiver as ReceiverValue
val receiverTypes = listOf(receiverValue.type) + context.dataFlowInfo.getPredictableTypes( val receiverTypes = listOf(receiverValue.type) + context.dataFlowInfo.getStableTypes(
DataFlowValueFactory.createDataFlowValue(receiverValue, context.trace.bindingContext, context.scope.ownerDescriptor) DataFlowValueFactory.createDataFlowValue(receiverValue, context.trace.bindingContext, context.scope.ownerDescriptor)
) )
@@ -319,7 +319,7 @@ class CallExpressionResolver(
var initialDataFlowInfoForArguments = context.dataFlowInfo var initialDataFlowInfoForArguments = context.dataFlowInfo
val receiverDataFlowValue = (receiver as? ReceiverValue)?.let { DataFlowValueFactory.createDataFlowValue(it, context) } val receiverDataFlowValue = (receiver as? ReceiverValue)?.let { DataFlowValueFactory.createDataFlowValue(it, context) }
val receiverCanBeNull = receiverDataFlowValue != null && val receiverCanBeNull = receiverDataFlowValue != null &&
initialDataFlowInfoForArguments.getPredictableNullability(receiverDataFlowValue).canBeNull() initialDataFlowInfoForArguments.getStableNullability(receiverDataFlowValue).canBeNull()
if (receiverDataFlowValue != null && element.safe) { if (receiverDataFlowValue != null && element.safe) {
// Additional "receiver != null" information should be applied if we consider a safe call // Additional "receiver != null" information should be applied if we consider a safe call
if (receiverCanBeNull) { if (receiverCanBeNull) {
@@ -506,7 +506,7 @@ class CandidateResolver(
val outerCallReceiver = call.outerCall.explicitReceiver val outerCallReceiver = call.outerCall.explicitReceiver
if (outerCallReceiver != call.explicitReceiver && outerCallReceiver is ReceiverValue) { if (outerCallReceiver != call.explicitReceiver && outerCallReceiver is ReceiverValue) {
val outerReceiverDataFlowValue = DataFlowValueFactory.createDataFlowValue(outerCallReceiver, this) val outerReceiverDataFlowValue = DataFlowValueFactory.createDataFlowValue(outerCallReceiver, this)
val outerReceiverNullability = dataFlowInfo.getPredictableNullability(outerReceiverDataFlowValue) val outerReceiverNullability = dataFlowInfo.getStableNullability(outerReceiverDataFlowValue)
if (outerReceiverNullability.canBeNull() && !TypeUtils.isNullableType(expectedReceiverParameterType)) { if (outerReceiverNullability.canBeNull() && !TypeUtils.isNullableType(expectedReceiverParameterType)) {
nullableImplicitInvokeReceiver = true nullableImplicitInvokeReceiver = true
receiverArgumentType = TypeUtils.makeNullable(receiverArgumentType) receiverArgumentType = TypeUtils.makeNullable(receiverArgumentType)
@@ -515,7 +515,7 @@ class CandidateResolver(
} }
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this)
val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue) val nullability = dataFlowInfo.getStableNullability(dataFlowValue)
val expression = (receiverArgument as? ExpressionReceiver)?.expression val expression = (receiverArgument as? ExpressionReceiver)?.expression
if (nullability.canBeNull() && !nullability.canBeNonNull()) { if (nullability.canBeNull() && !nullability.canBeNonNull()) {
if (!TypeUtils.isNullableType(expectedReceiverParameterType)) { if (!TypeUtils.isNullableType(expectedReceiverParameterType)) {
@@ -195,7 +195,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
if (deparenthesizedArgument == null || type == null) return type if (deparenthesizedArgument == null || type == null) return type
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context)
if (!dataFlowValue.isPredictable) return type if (!dataFlowValue.isStable) return type
val possibleTypes = context.dataFlowInfo.getCollectedTypes(dataFlowValue) val possibleTypes = context.dataFlowInfo.getCollectedTypes(dataFlowValue)
if (possibleTypes.isEmpty()) return type if (possibleTypes.isEmpty()) return type
@@ -82,7 +82,7 @@ private fun ResolvedCall<*>.hasSafeNullableReceiver(context: CallResolutionConte
if (!call.isSafeCall()) return false if (!call.isSafeCall()) return false
val receiverValue = getExplicitReceiverValue()?.let { DataFlowValueFactory.createDataFlowValue(it, context) } val receiverValue = getExplicitReceiverValue()?.let { DataFlowValueFactory.createDataFlowValue(it, context) }
?: return false ?: return false
return context.dataFlowInfo.getPredictableNullability(receiverValue).canBeNull() return context.dataFlowInfo.getStableNullability(receiverValue).canBeNull()
} }
fun ResolvedCall<*>.makeNullableTypeIfSafeReceiver(type: KotlinType?, context: CallResolutionContext<*>) = fun ResolvedCall<*>.makeNullableTypeIfSafeReceiver(type: KotlinType?, context: CallResolutionContext<*>) =
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.resolve.calls.smartcasts package org.jetbrains.kotlin.resolve.calls.smartcasts
import com.google.common.collect.ImmutableMap
import com.google.common.collect.SetMultimap import com.google.common.collect.SetMultimap
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
@@ -31,18 +30,18 @@ interface DataFlowInfo {
val completeTypeInfo: SetMultimap<DataFlowValue, KotlinType> val completeTypeInfo: SetMultimap<DataFlowValue, KotlinType>
/** /**
* Returns collected nullability for the given value, NOT taking its predictability into account. * Returns collected nullability for the given value, NOT taking its stability into account.
*/ */
fun getCollectedNullability(key: DataFlowValue): Nullability fun getCollectedNullability(key: DataFlowValue): Nullability
/** /**
* Returns collected nullability for the given value if it's predictable. * Returns collected nullability for the given value if it's stable.
* Otherwise basic value nullability is returned * Otherwise basic value nullability is returned
*/ */
fun getPredictableNullability(key: DataFlowValue): Nullability fun getStableNullability(key: DataFlowValue): Nullability
/** /**
* Returns possible types for the given value, NOT taking its predictability into account. * Returns possible types for the given value, NOT taking its stability into account.
* *
* IMPORTANT: by default, the original (native) type for this value * IMPORTANT: by default, the original (native) type for this value
* are NOT included. So it's quite possible to get an empty set here. * are NOT included. So it's quite possible to get an empty set here.
@@ -50,13 +49,13 @@ interface DataFlowInfo {
fun getCollectedTypes(key: DataFlowValue): Set<KotlinType> fun getCollectedTypes(key: DataFlowValue): Set<KotlinType>
/** /**
* Returns possible types for the given value if it's predictable. * Returns possible types for the given value if it's stable.
* Otherwise, basic value type is returned. * Otherwise, basic value type is returned.
* *
* IMPORTANT: by default, the original (native) type for this value * IMPORTANT: by default, the original (native) type for this value
* are NOT included. So it's quite possible to get an empty set here. * are NOT included. So it's quite possible to get an empty set here.
*/ */
fun getPredictableTypes(key: DataFlowValue): Set<KotlinType> fun getStableTypes(key: DataFlowValue): Set<KotlinType>
/** /**
* Call this function to clear all data flow information about * Call this function to clear all data flow information about
@@ -316,25 +316,25 @@ object DataFlowValueFactory {
if (!variableDescriptor.isVar) return STABLE_VALUE if (!variableDescriptor.isVar) return STABLE_VALUE
if (variableDescriptor is SyntheticFieldDescriptor) return MUTABLE_PROPERTY if (variableDescriptor is SyntheticFieldDescriptor) return MUTABLE_PROPERTY
// Local variable classification: PREDICTABLE or UNPREDICTABLE // Local variable classification: STABLE or CAPTURED
val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(variableDescriptor, bindingContext) val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(variableDescriptor, bindingContext)
?: return UNPREDICTABLE_VARIABLE // A case when we just analyse an expression alone: counts as captured
// A case when we just analyse an expression alone: counts as unpredictable ?: return CAPTURED_VARIABLE
// Analyze who writes variable // Analyze who writes variable
// If there is no writer: predictable // If there is no writer: stable
val writers = preliminaryVisitor.writers(variableDescriptor) val writers = preliminaryVisitor.writers(variableDescriptor)
if (writers.isEmpty()) return PREDICTABLE_VARIABLE if (writers.isEmpty()) return STABLE_VARIABLE
// If access element is inside closure: unpredictable // If access element is inside closure: captured
val variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor) val variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor)
if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return UNPREDICTABLE_VARIABLE if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return CAPTURED_VARIABLE
// Otherwise, predictable iff considered position is BEFORE all writers except declarer itself // Otherwise, stable iff considered position is BEFORE all writers except declarer itself
return if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement)) return if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement))
PREDICTABLE_VARIABLE STABLE_VARIABLE
else else
UNPREDICTABLE_VARIABLE CAPTURED_VARIABLE
} }
/** /**
@@ -75,10 +75,10 @@ internal class DelegatingDataFlowInfo private constructor(
override fun getCollectedNullability(key: DataFlowValue) = getNullability(key, false) override fun getCollectedNullability(key: DataFlowValue) = getNullability(key, false)
override fun getPredictableNullability(key: DataFlowValue) = getNullability(key, true) override fun getStableNullability(key: DataFlowValue) = getNullability(key, true)
private fun getNullability(key: DataFlowValue, predictableOnly: Boolean) = private fun getNullability(key: DataFlowValue, stableOnly: Boolean) =
if (predictableOnly && !key.isPredictable) { if (stableOnly && !key.isStable) {
key.immanentNullability key.immanentNullability
} }
else { else {
@@ -132,10 +132,10 @@ internal class DelegatingDataFlowInfo private constructor(
return enrichedTypes return enrichedTypes
} }
override fun getPredictableTypes(key: DataFlowValue) = getPredictableTypes(key, true) override fun getStableTypes(key: DataFlowValue) = getStableTypes(key, true)
private fun getPredictableTypes(key: DataFlowValue, enrichWithNotNull: Boolean) = private fun getStableTypes(key: DataFlowValue, enrichWithNotNull: Boolean) =
if (!key.isPredictable) LinkedHashSet() else getCollectedTypes(key, enrichWithNotNull) if (!key.isStable) LinkedHashSet() else getCollectedTypes(key, enrichWithNotNull)
/** /**
* Call this function to clear all data flow information about * Call this function to clear all data flow information about
@@ -151,11 +151,11 @@ internal class DelegatingDataFlowInfo private constructor(
override fun assign(a: DataFlowValue, b: DataFlowValue): DataFlowInfo { override fun assign(a: DataFlowValue, b: DataFlowValue): DataFlowInfo {
val nullability = Maps.newHashMap<DataFlowValue, Nullability>() val nullability = Maps.newHashMap<DataFlowValue, Nullability>()
val nullabilityOfB = getPredictableNullability(b) val nullabilityOfB = getStableNullability(b)
putNullability(nullability, a, nullabilityOfB, affectReceiver = false) putNullability(nullability, a, nullabilityOfB, affectReceiver = false)
val newTypeInfo = newTypeInfo() val newTypeInfo = newTypeInfo()
var typesForB = getPredictableTypes(b) var typesForB = getStableTypes(b)
// Own type of B must be recorded separately, e.g. for a constant // Own type of B must be recorded separately, e.g. for a constant
// But if its type is the same as A, there is no reason to do it // But if its type is the same as A, there is no reason to do it
// because own type is not saved in this set // because own type is not saved in this set
@@ -170,8 +170,8 @@ internal class DelegatingDataFlowInfo private constructor(
override fun equate(a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean): DataFlowInfo { override fun equate(a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean): DataFlowInfo {
val builder = Maps.newHashMap<DataFlowValue, Nullability>() val builder = Maps.newHashMap<DataFlowValue, Nullability>()
val nullabilityOfA = getPredictableNullability(a) val nullabilityOfA = getStableNullability(a)
val nullabilityOfB = getPredictableNullability(b) val nullabilityOfB = getStableNullability(b)
var changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)) or var changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)) or
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA)) putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA))
@@ -179,8 +179,8 @@ internal class DelegatingDataFlowInfo private constructor(
// NB: == has no guarantees of type equality, see KT-11280 for the example // NB: == has no guarantees of type equality, see KT-11280 for the example
val newTypeInfo = newTypeInfo() val newTypeInfo = newTypeInfo()
if (sameTypes) { if (sameTypes) {
newTypeInfo.putAll(a, getPredictableTypes(b, false)) newTypeInfo.putAll(a, getStableTypes(b, false))
newTypeInfo.putAll(b, getPredictableTypes(a, false)) newTypeInfo.putAll(b, getStableTypes(a, false))
if (a.type != b.type) { if (a.type != b.type) {
// To avoid recording base types of own type // To avoid recording base types of own type
if (!a.type.isSubtypeOf(b.type)) { if (!a.type.isSubtypeOf(b.type)) {
@@ -226,8 +226,8 @@ internal class DelegatingDataFlowInfo private constructor(
override fun disequate(a: DataFlowValue, b: DataFlowValue): DataFlowInfo { override fun disequate(a: DataFlowValue, b: DataFlowValue): DataFlowInfo {
val builder = Maps.newHashMap<DataFlowValue, Nullability>() val builder = Maps.newHashMap<DataFlowValue, Nullability>()
val nullabilityOfA = getPredictableNullability(a) val nullabilityOfA = getStableNullability(a)
val nullabilityOfB = getPredictableNullability(b) val nullabilityOfB = getStableNullability(b)
val changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())) or val changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())) or
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert())) putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert()))
@@ -116,7 +116,7 @@ class SmartCastManager {
recordExpressionType: Boolean recordExpressionType: Boolean
) { ) {
if (KotlinBuiltIns.isNullableNothing(type)) return if (KotlinBuiltIns.isNullableNothing(type)) return
if (dataFlowValue.isPredictable) { if (dataFlowValue.isStable) {
trace.record(SMARTCAST, expression, type) trace.record(SMARTCAST, expression, type)
if (recordExpressionType) { if (recordExpressionType) {
//TODO //TODO
@@ -154,10 +154,10 @@ class SmartCastManager {
if (expression != null) { if (expression != null) {
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType) recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType)
} }
else if (calleeExpression != null && dataFlowValue.isPredictable) { else if (calleeExpression != null && dataFlowValue.isStable) {
c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType) c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType)
} }
return SmartCastResult(possibleType, dataFlowValue.isPredictable) return SmartCastResult(possibleType, dataFlowValue.isStable)
} }
} }
@@ -183,7 +183,7 @@ class SmartCastManager {
recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, recordExpressionType) recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, recordExpressionType)
} }
return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isPredictable) return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isStable)
} }
return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType) return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType)
} }
@@ -58,7 +58,7 @@ private class DataFlowDecoratorImpl(private val resolutionContext: ResolutionCon
override fun getDataFlowValue(receiver: ReceiverValue): DataFlowValue = getSmartCastInfo(receiver).dataFlowValue override fun getDataFlowValue(receiver: ReceiverValue): DataFlowValue = getSmartCastInfo(receiver).dataFlowValue
override fun isStableReceiver(receiver: ReceiverValue): Boolean = getSmartCastInfo(receiver).dataFlowValue.isPredictable override fun isStableReceiver(receiver: ReceiverValue): Boolean = getSmartCastInfo(receiver).dataFlowValue.isStable
override fun getSmartCastTypes(receiver: ReceiverValue): Set<KotlinType> = getSmartCastInfo(receiver).possibleTypes override fun getSmartCastTypes(receiver: ReceiverValue): Set<KotlinType> = getSmartCastInfo(receiver).possibleTypes
@@ -142,7 +142,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
// Receivers are normally analyzed at resolve, with an exception of KT-10175 // Receivers are normally analyzed at resolve, with an exception of KT-10175
if (type != null && !type.isError() && !isLValueOrUnsafeReceiver(expression)) { if (type != null && !type.isError() && !isLValueOrUnsafeReceiver(expression)) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context); DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context);
Nullability nullability = context.dataFlowInfo.getPredictableNullability(dataFlowValue); Nullability nullability = context.dataFlowInfo.getStableNullability(dataFlowValue);
if (!nullability.canBeNonNull() && nullability.canBeNull()) { if (!nullability.canBeNonNull() && nullability.canBeNull()) {
if (isDangerousWithNull(expression, context)) { if (isDangerousWithNull(expression, context)) {
context.trace.report(ALWAYS_NULL.on(expression)); context.trace.report(ALWAYS_NULL.on(expression));
@@ -878,7 +878,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
private static boolean isKnownToBeNotNull(KtExpression expression, KotlinType jetType, ExpressionTypingContext context) { private static boolean isKnownToBeNotNull(KtExpression expression, KotlinType jetType, ExpressionTypingContext context) {
DataFlowValue dataFlowValue = createDataFlowValue(expression, jetType, context); DataFlowValue dataFlowValue = createDataFlowValue(expression, jetType, context);
return !context.dataFlowInfo.getPredictableNullability(dataFlowValue).canBeNull(); return !context.dataFlowInfo.getStableNullability(dataFlowValue).canBeNull();
} }
/** /**
@@ -1221,7 +1221,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
boolean jumpInRight = KotlinBuiltIns.isNothing(rightType); boolean jumpInRight = KotlinBuiltIns.isNothing(rightType);
DataFlowValue nullValue = DataFlowValue.nullValue(components.builtIns); DataFlowValue nullValue = DataFlowValue.nullValue(components.builtIns);
// left argument is considered not-null if it's not-null also in right part or if we have jump in right part // left argument is considered not-null if it's not-null also in right part or if we have jump in right part
if (jumpInRight || !rightDataFlowInfo.getPredictableNullability(leftValue).canBeNull()) { if (jumpInRight || !rightDataFlowInfo.getStableNullability(leftValue).canBeNull()) {
dataFlowInfo = dataFlowInfo.disequate(leftValue, nullValue); dataFlowInfo = dataFlowInfo.disequate(leftValue, nullValue);
if (left instanceof KtBinaryExpressionWithTypeRHS) { if (left instanceof KtBinaryExpressionWithTypeRHS) {
dataFlowInfo = establishSubtypingForTypeRHS((KtBinaryExpressionWithTypeRHS) left, dataFlowInfo, context); dataFlowInfo = establishSubtypingForTypeRHS((KtBinaryExpressionWithTypeRHS) left, dataFlowInfo, context);
@@ -1360,7 +1360,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
new Function1<DataFlowValue, Nullability>() { new Function1<DataFlowValue, Nullability>() {
@Override @Override
public Nullability invoke(DataFlowValue value) { public Nullability invoke(DataFlowValue value) {
return context.dataFlowInfo.getPredictableNullability(value); return context.dataFlowInfo.getStableNullability(value);
} }
}); });
} }
@@ -341,7 +341,7 @@ public class DataFlowAnalyzer {
) { ) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c); DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
Collection<KotlinType> possibleTypes = Sets.newHashSet(type); Collection<KotlinType> possibleTypes = Sets.newHashSet(type);
possibleTypes.addAll(dataFlowInfo.getPredictableTypes(dataFlowValue)); possibleTypes.addAll(dataFlowInfo.getStableTypes(dataFlowValue));
return possibleTypes; return possibleTypes;
} }
@@ -34,9 +34,9 @@ class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher()
val nullabilityMap = resultFlowInfo.completeNullabilityInfo val nullabilityMap = resultFlowInfo.completeNullabilityInfo
val valueSetToClear = LinkedHashSet<DataFlowValue>() val valueSetToClear = LinkedHashSet<DataFlowValue>()
for (value in nullabilityMap.keys) { for (value in nullabilityMap.keys) {
// Only predictable variables are under interest here // Only stable variables are under interest here
val identifierInfo = value.identifierInfo val identifierInfo = value.identifierInfo
if (value.kind == DataFlowValue.Kind.PREDICTABLE_VARIABLE && identifierInfo is IdentifierInfo.Variable) { if (value.kind == DataFlowValue.Kind.STABLE_VARIABLE && identifierInfo is IdentifierInfo.Variable) {
val variableDescriptor = identifierInfo.variable val variableDescriptor = identifierInfo.variable
if (variableDescriptor is LocalVariableDescriptor && hasWriters(variableDescriptor)) { if (variableDescriptor is LocalVariableDescriptor && hasWriters(variableDescriptor)) {
valueSetToClear.add(value) valueSetToClear.add(value)
@@ -38,7 +38,7 @@ class DataFlowValue(val identifierInfo: IdentifierInfo,
// Local value, or parameter, or private / internal member value without open / custom getter, // Local value, or parameter, or private / internal member value without open / custom getter,
// or protected / public member value from the same module without open / custom getter // or protected / public member value from the same module without open / custom getter
// Smart casts are completely safe // Smart casts are completely safe
STABLE_VALUE("stable"), STABLE_VALUE("stable val"),
// Member value with open / custom getter // Member value with open / custom getter
// Smart casts are not safe // Smart casts are not safe
PROPERTY_WITH_GETTER("custom getter", "property that has open or custom getter"), PROPERTY_WITH_GETTER("custom getter", "property that has open or custom getter"),
@@ -47,10 +47,10 @@ class DataFlowValue(val identifierInfo: IdentifierInfo,
ALIEN_PUBLIC_PROPERTY("alien public", "public API property declared in different module"), ALIEN_PUBLIC_PROPERTY("alien public", "public API property declared in different module"),
// Local variable not yet captured by a changing closure // Local variable not yet captured by a changing closure
// Smart casts are safe but possible changes in loops / closures ahead must be taken into account // Smart casts are safe but possible changes in loops / closures ahead must be taken into account
PREDICTABLE_VARIABLE("predictable", "local variable that can be changed since the check in a loop"), STABLE_VARIABLE("stable var", "local variable that can be changed since the check in a loop"),
// Local variable already captured by a changing closure // Local variable already captured by a changing closure
// Smart casts are not safe // Smart casts are not safe
UNPREDICTABLE_VARIABLE("unpredictable", "local variable that is captured by a changing closure"), CAPTURED_VARIABLE("captured var", "local variable that is captured by a changing closure"),
// Member variable regardless of its visibility // Member variable regardless of its visibility
// Smart casts are not safe // Smart casts are not safe
MUTABLE_PROPERTY("member", "mutable property that could have been changed by this time"), MUTABLE_PROPERTY("member", "mutable property that could have been changed by this time"),
@@ -59,17 +59,13 @@ class DataFlowValue(val identifierInfo: IdentifierInfo,
OTHER("other", "complex expression"); OTHER("other", "complex expression");
override fun toString() = str override fun toString() = str
fun isStable() = this == STABLE_VALUE
} }
/** /**
* Both stable values and predictable local variables are considered "predictable". * Stable means here we do not expect some sudden change of their values,
* Predictable means here we do not expect some sudden change of their values,
* like accessing mutable properties in another thread, so smart casts can be used safely. * like accessing mutable properties in another thread, so smart casts can be used safely.
*/ */
val isPredictable = (kind == Kind.STABLE_VALUE || kind == Kind.PREDICTABLE_VARIABLE) val isStable = (kind == Kind.STABLE_VALUE || kind == Kind.STABLE_VARIABLE)
@JvmName("isPredictable") get
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@@ -72,7 +72,7 @@ interface IdentifierInfo {
val safe: Boolean, val safe: Boolean,
val receiverType: KotlinType? val receiverType: KotlinType?
) : IdentifierInfo { ) : IdentifierInfo {
override val kind: DataFlowValue.Kind get() = if (receiverInfo.kind.isStable()) selectorInfo.kind else OTHER override val kind: DataFlowValue.Kind get() = if (receiverInfo.kind == STABLE_VALUE) selectorInfo.kind else OTHER
override fun equals(other: Any?) = other is Qualified && receiverInfo == other.receiverInfo && selectorInfo == other.selectorInfo override fun equals(other: Any?) = other is Qualified && receiverInfo == other.receiverInfo && selectorInfo == other.selectorInfo
@@ -186,7 +186,7 @@ class ReferenceVariantsHelper(
listOf(useReceiverType) listOf(useReceiverType)
} }
else { else {
callTypeAndReceiver.receiverTypes(bindingContext, contextElement, moduleDescriptor, resolutionFacade, predictableSmartCastsOnly = false)!! callTypeAndReceiver.receiverTypes(bindingContext, contextElement, moduleDescriptor, resolutionFacade, stableSmartCastsOnly = false)!!
} }
descriptors.processAll(implicitReceiverTypes, explicitReceiverTypes, resolutionScope, callType, kindFilter, nameFilter) descriptors.processAll(implicitReceiverTypes, explicitReceiverTypes, resolutionScope, callType, kindFilter, nameFilter)
@@ -244,7 +244,7 @@ class ReferenceVariantsHelper(
listOf(useReceiverType) listOf(useReceiverType)
} }
else { else {
callTypeAndReceiver.receiverTypes(bindingContext, contextElement, moduleDescriptor, resolutionFacade, predictableSmartCastsOnly = false)!! callTypeAndReceiver.receiverTypes(bindingContext, contextElement, moduleDescriptor, resolutionFacade, stableSmartCastsOnly = false)!!
} }
val constructorFilter = { descriptor: ClassDescriptor -> if (isStatic) true else descriptor.isInner } val constructorFilter = { descriptor: ClassDescriptor -> if (isStatic) true else descriptor.isInner }
@@ -213,7 +213,7 @@ fun CallTypeAndReceiver<*, *>.receiverTypes(
contextElement: PsiElement, contextElement: PsiElement,
moduleDescriptor: ModuleDescriptor, moduleDescriptor: ModuleDescriptor,
resolutionFacade: ResolutionFacade, resolutionFacade: ResolutionFacade,
predictableSmartCastsOnly: Boolean stableSmartCastsOnly: Boolean
): Collection<KotlinType>? { ): Collection<KotlinType>? {
val receiverExpression: KtExpression? val receiverExpression: KtExpression?
when (this) { when (this) {
@@ -225,7 +225,7 @@ fun CallTypeAndReceiver<*, *>.receiverTypes(
is DoubleColonLHS.Expression -> { is DoubleColonLHS.Expression -> {
val receiverValue = ExpressionReceiver.create(receiver, lhs.type, bindingContext) val receiverValue = ExpressionReceiver.create(receiver, lhs.type, bindingContext)
return receiverValueTypes(receiverValue, lhs.dataFlowInfo, bindingContext, moduleDescriptor, predictableSmartCastsOnly) return receiverValueTypes(receiverValue, lhs.dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly)
} }
} }
} }
@@ -281,7 +281,7 @@ fun CallTypeAndReceiver<*, *>.receiverTypes(
val dataFlowInfo = bindingContext.getDataFlowInfo(contextElement) val dataFlowInfo = bindingContext.getDataFlowInfo(contextElement)
return receiverValues.flatMap { return receiverValues.flatMap {
receiverValueTypes(it, dataFlowInfo, bindingContext, moduleDescriptor, predictableSmartCastsOnly) receiverValueTypes(it, dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly)
} }
} }
@@ -290,10 +290,10 @@ private fun receiverValueTypes(
dataFlowInfo: DataFlowInfo, dataFlowInfo: DataFlowInfo,
bindingContext: BindingContext, bindingContext: BindingContext,
moduleDescriptor: ModuleDescriptor, moduleDescriptor: ModuleDescriptor,
predictableSmartCastsOnly: Boolean stableSmartCastsOnly: Boolean
): List<KotlinType> { ): List<KotlinType> {
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor) val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor)
return if (dataFlowValue.isPredictable || !predictableSmartCastsOnly) { // we don't include smart cast receiver types for "unpredictable" receiver value to mark members grayed return if (dataFlowValue.isStable || !stableSmartCastsOnly) { // we don't include smart cast receiver types for "unstable" receiver value to mark members grayed
SmartCastManager().getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo) SmartCastManager().getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo)
} }
else { else {
@@ -439,7 +439,7 @@ abstract class CompletionSession(
var receiverTypes = callTypeAndReceiver.receiverTypes( var receiverTypes = callTypeAndReceiver.receiverTypes(
bindingContext, nameExpression, moduleDescriptor, resolutionFacade, bindingContext, nameExpression, moduleDescriptor, resolutionFacade,
predictableSmartCastsOnly = true /* we don't include smart cast receiver types for "unpredictable" receiver value to mark members grayed */) stableSmartCastsOnly = true /* we don't include smart cast receiver types for "unstable" receiver value to mark members grayed */)
if (callTypeAndReceiver is CallTypeAndReceiver.SAFE || isDebuggerContext) { if (callTypeAndReceiver is CallTypeAndReceiver.SAFE || isDebuggerContext) {
receiverTypes = receiverTypes?.map { it.makeNotNullable() } receiverTypes = receiverTypes?.map { it.makeNotNullable() }
@@ -132,7 +132,7 @@ class KotlinIndicesHelper(
bindingContext: BindingContext, bindingContext: BindingContext,
nameFilter: (String) -> Boolean nameFilter: (String) -> Boolean
): Collection<CallableDescriptor> { ): Collection<CallableDescriptor> {
val receiverTypes = callTypeAndReceiver.receiverTypes(bindingContext, position, moduleDescriptor, resolutionFacade, predictableSmartCastsOnly = false) val receiverTypes = callTypeAndReceiver.receiverTypes(bindingContext, position, moduleDescriptor, resolutionFacade, stableSmartCastsOnly = false)
?: return emptyList() ?: return emptyList()
return getCallableTopLevelExtensions(callTypeAndReceiver, receiverTypes, nameFilter) return getCallableTopLevelExtensions(callTypeAndReceiver, receiverTypes, nameFilter)
} }
@@ -67,7 +67,7 @@ class SurroundWithNullCheckFix(
else -> return null else -> return null
} as? KtReferenceExpression ?: return null } as? KtReferenceExpression ?: return null
if (!nullableExpression.isPredictable()) return null if (!nullableExpression.isStable()) return null
return SurroundWithNullCheckFix(expression, nullableExpression) return SurroundWithNullCheckFix(expression, nullableExpression)
} }
@@ -80,7 +80,7 @@ class SurroundWithNullCheckFix(
val forExpression = nullableExpression.parent?.parent as? KtForExpression ?: return null val forExpression = nullableExpression.parent?.parent as? KtForExpression ?: return null
if (forExpression.parent !is KtBlockExpression) return null if (forExpression.parent !is KtBlockExpression) return null
if (!nullableExpression.isPredictable()) return null if (!nullableExpression.isStable()) return null
return SurroundWithNullCheckFix(forExpression, nullableExpression) return SurroundWithNullCheckFix(forExpression, nullableExpression)
} }
@@ -97,16 +97,16 @@ class SurroundWithNullCheckFix(
if (!isNullabilityMismatch(expected = typeMismatch.a, actual = typeMismatch.b)) return null if (!isNullabilityMismatch(expected = typeMismatch.a, actual = typeMismatch.b)) return null
if (!nullableExpression.isPredictable()) return null if (!nullableExpression.isStable()) return null
return SurroundWithNullCheckFix(call, nullableExpression) return SurroundWithNullCheckFix(call, nullableExpression)
} }
} }
} }
private fun KtExpression.isPredictable(): Boolean { private fun KtExpression.isStable(): Boolean {
val context = this.analyze() val context = this.analyze()
val nullableType = this.getType(context) ?: return false val nullableType = this.getType(context) ?: return false
val containingDescriptor = this.getResolutionScope(context, this.getResolutionFacade()).ownerDescriptor val containingDescriptor = this.getResolutionScope(context, this.getResolutionFacade()).ownerDescriptor
return DataFlowValueFactory.createDataFlowValue(this, nullableType, context, containingDescriptor).isPredictable return DataFlowValueFactory.createDataFlowValue(this, nullableType, context, containingDescriptor).isStable
} }
@@ -47,7 +47,7 @@ abstract class AbstractDataFlowValueRenderingTest: KotlinLightCodeInsightFixture
private fun DataFlowValue.render() = private fun DataFlowValue.render() =
// If it is not a stable identifier, there's no point in rendering it // If it is not a stable identifier, there's no point in rendering it
if (!isPredictable) null if (!isStable) null
else identifierInfo.render() else identifierInfo.render()
override fun getTestDataPath() : String { override fun getTestDataPath() : String {