Refactoring: DataFlowValue.Kind introduced
This commit is contained in:
+36
-32
@@ -27,23 +27,40 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
* In general case it's some r-value.
|
||||
*/
|
||||
public class DataFlowValue {
|
||||
|
||||
public static final DataFlowValue NULL = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableNothingType(), false, false, Nullability.NULL);
|
||||
public static final DataFlowValue NULLABLE = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableAnyType(), false, false, Nullability.UNKNOWN);
|
||||
public static final DataFlowValue ERROR = new DataFlowValue(new Object(), ErrorUtils.createErrorType("Error type for data flow"), false, false, Nullability.IMPOSSIBLE);
|
||||
|
||||
private final boolean stableIdentifier;
|
||||
private final boolean uncapturedLocalVariable;
|
||||
public enum Kind {
|
||||
STABLE_VALUE("stable"),
|
||||
PREDICTABLE_VARIABLE("predictable"),
|
||||
UNPREDICTABLE_VARIABLE("unpredictable"),
|
||||
OTHER("other");
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
Kind(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private final String name;
|
||||
|
||||
public boolean isStable() {
|
||||
return this == STABLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
public static final DataFlowValue NULL = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableNothingType(), Kind.OTHER, Nullability.NULL);
|
||||
public static final DataFlowValue ERROR = new DataFlowValue(new Object(), ErrorUtils.createErrorType("Error type for data flow"), Kind.OTHER, Nullability.IMPOSSIBLE);
|
||||
|
||||
private final Kind kind;
|
||||
private final JetType type;
|
||||
private final Object id;
|
||||
private final Nullability immanentNullability;
|
||||
|
||||
// Use DataFlowValueFactory
|
||||
/*package*/ DataFlowValue(Object id, JetType type, boolean stableIdentifier, boolean uncapturedLocalVariable, Nullability immanentNullability) {
|
||||
assert !stableIdentifier || !uncapturedLocalVariable :
|
||||
"data flow value for object " + id + " cannot be together a stable identifier and an uncaptured local variable";
|
||||
this.stableIdentifier = stableIdentifier;
|
||||
this.uncapturedLocalVariable = uncapturedLocalVariable;
|
||||
/*package*/ DataFlowValue(Object id, JetType type, Kind kind, Nullability immanentNullability) {
|
||||
this.kind = kind;
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.immanentNullability = immanentNullability;
|
||||
@@ -59,30 +76,17 @@ public class DataFlowValue {
|
||||
return immanentNullability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stable identifier is a non-literal value that is statically known to be immutable
|
||||
*
|
||||
* NB: this function is no longer public!
|
||||
* If you are checking for a possible smart cast, probably you need isPredictable() instead
|
||||
*/
|
||||
private boolean isStableIdentifier() {
|
||||
return stableIdentifier;
|
||||
public Kind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifier is considered a local variable here if it's mutable (var), local and not captured in a closure
|
||||
*/
|
||||
public boolean isUncapturedLocalVariable() {
|
||||
return uncapturedLocalVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Both stable identifiers and uncaptured local variables are considered "predictable".
|
||||
* Both stable values and local variables (regardless captured or not) are considered "predictable".
|
||||
* Predictable means here we do not expect some sudden change of their values,
|
||||
* like accessing mutable properties in another thread or mutable variables from closures.
|
||||
* like accessing mutable properties in another thread.
|
||||
*/
|
||||
public boolean isPredictable() {
|
||||
return stableIdentifier || uncapturedLocalVariable;
|
||||
return kind == Kind.STABLE_VALUE || kind == Kind.PREDICTABLE_VARIABLE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -97,7 +101,7 @@ public class DataFlowValue {
|
||||
|
||||
DataFlowValue that = (DataFlowValue) o;
|
||||
|
||||
if (stableIdentifier != that.stableIdentifier) return false;
|
||||
if (kind.isStable() != that.kind.isStable()) return false;
|
||||
if (id != null ? !id.equals(that.id) : that.id != null) return false;
|
||||
if (type != null ? !type.equals(that.type) : that.type != null) return false;
|
||||
|
||||
@@ -106,12 +110,12 @@ public class DataFlowValue {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (stableIdentifier ? "stable " : "unstable ") + (id == null ? null : id.toString()) + " " + immanentNullability;
|
||||
return kind.toString() + (id == null ? null : id.toString()) + " " + immanentNullability;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (stableIdentifier ? 1 : 0);
|
||||
int result = kind.isStable() ? 1 : 0;
|
||||
result = 31 * result + (type != null ? type.hashCode() : 0);
|
||||
result = 31 * result + (id != null ? id.hashCode() : 0);
|
||||
return result;
|
||||
|
||||
+30
-28
@@ -81,23 +81,21 @@ public class DataFlowValueFactory {
|
||||
// fun <T : Any?> foo(x: T) = x!!.hashCode() // there no way in type system to denote that `x!!` is not nullable
|
||||
return new DataFlowValue(expression,
|
||||
type,
|
||||
/* stableIdentifier = */false,
|
||||
/* uncapturedLocalVariable = */false,
|
||||
DataFlowValue.Kind.OTHER,
|
||||
Nullability.NOT_NULL);
|
||||
}
|
||||
|
||||
IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule);
|
||||
return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id,
|
||||
type,
|
||||
result.isStable,
|
||||
result.isLocal,
|
||||
result.kind,
|
||||
getImmanentNullability(type));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DataFlowValue createDataFlowValue(@NotNull ThisReceiver receiver) {
|
||||
JetType type = receiver.getType();
|
||||
return new DataFlowValue(receiver, type, true, false, getImmanentNullability(type));
|
||||
return new DataFlowValue(receiver, type, DataFlowValue.Kind.STABLE_VALUE, getImmanentNullability(type));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -118,7 +116,7 @@ public class DataFlowValueFactory {
|
||||
if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) {
|
||||
// SCRIPT: smartcasts data flow
|
||||
JetType type = receiverValue.getType();
|
||||
return new DataFlowValue(receiverValue, type, true, false, getImmanentNullability(type));
|
||||
return new DataFlowValue(receiverValue, type, DataFlowValue.Kind.STABLE_VALUE, getImmanentNullability(type));
|
||||
}
|
||||
else if (receiverValue instanceof ClassReceiver || receiverValue instanceof ExtensionReceiver) {
|
||||
return createDataFlowValue((ThisReceiver) receiverValue);
|
||||
@@ -145,8 +143,7 @@ public class DataFlowValueFactory {
|
||||
) {
|
||||
JetType type = variableDescriptor.getType();
|
||||
return new DataFlowValue(variableDescriptor, type,
|
||||
isStableVariable(variableDescriptor, usageContainingModule),
|
||||
isUncapturedLocalVariable(variableDescriptor, bindingContext),
|
||||
variableKind(variableDescriptor, usageContainingModule, bindingContext),
|
||||
getImmanentNullability(type));
|
||||
}
|
||||
|
||||
@@ -157,20 +154,17 @@ public class DataFlowValueFactory {
|
||||
|
||||
private static class IdentifierInfo {
|
||||
public final Object id;
|
||||
public final boolean isStable;
|
||||
public final boolean isLocal;
|
||||
public final DataFlowValue.Kind kind;
|
||||
public final boolean isPackage;
|
||||
|
||||
private IdentifierInfo(Object id, boolean isStable, boolean isLocal, boolean isPackage) {
|
||||
assert !isStable || !isLocal : "Identifier info for object " + id + " cannot be stable and local at one time";
|
||||
private IdentifierInfo(Object id, DataFlowValue.Kind kind, boolean isPackage) {
|
||||
this.id = id;
|
||||
this.isStable = isStable;
|
||||
this.isLocal = isLocal;
|
||||
this.kind = kind;
|
||||
this.isPackage = isPackage;
|
||||
}
|
||||
}
|
||||
|
||||
private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, false, false, false) {
|
||||
private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, DataFlowValue.Kind.OTHER, false) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NO_IDENTIFIER_INFO";
|
||||
@@ -178,18 +172,18 @@ public class DataFlowValueFactory {
|
||||
};
|
||||
|
||||
@NotNull
|
||||
private static IdentifierInfo createInfo(Object id, boolean isStable, boolean isLocal) {
|
||||
return new IdentifierInfo(id, isStable, isLocal, false);
|
||||
private static IdentifierInfo createInfo(Object id, DataFlowValue.Kind kind) {
|
||||
return new IdentifierInfo(id, kind, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static IdentifierInfo createStableInfo(Object id) {
|
||||
return createInfo(id, true, false);
|
||||
return createInfo(id, DataFlowValue.Kind.STABLE_VALUE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static IdentifierInfo createPackageOrClassInfo(Object id) {
|
||||
return new IdentifierInfo(id, true, false, true);
|
||||
return new IdentifierInfo(id, DataFlowValue.Kind.STABLE_VALUE, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -201,9 +195,10 @@ public class DataFlowValueFactory {
|
||||
return selectorInfo;
|
||||
}
|
||||
return createInfo(Pair.create(receiverInfo.id, selectorInfo.id),
|
||||
receiverInfo.isStable && selectorInfo.isStable,
|
||||
receiverInfo.kind.isStable() && selectorInfo.kind.isStable()
|
||||
? DataFlowValue.Kind.STABLE_VALUE
|
||||
// x.y can never be a local variable
|
||||
false);
|
||||
: DataFlowValue.Kind.OTHER);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -211,7 +206,7 @@ public class DataFlowValueFactory {
|
||||
if (argumentInfo == NO_IDENTIFIER_INFO) {
|
||||
return NO_IDENTIFIER_INFO;
|
||||
}
|
||||
return createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.isStable, argumentInfo.isLocal);
|
||||
return createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.kind);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -278,8 +273,7 @@ public class DataFlowValueFactory {
|
||||
|
||||
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
||||
return combineInfo(receiverInfo, createInfo(variableDescriptor,
|
||||
isStableVariable(variableDescriptor, usageModuleDescriptor),
|
||||
isUncapturedLocalVariable(variableDescriptor, bindingContext)));
|
||||
variableKind(variableDescriptor, usageModuleDescriptor, bindingContext)));
|
||||
}
|
||||
if (declarationDescriptor instanceof PackageViewDescriptor || declarationDescriptor instanceof ClassDescriptor) {
|
||||
return createPackageOrClassInfo(declarationDescriptor);
|
||||
@@ -314,10 +308,18 @@ public class DataFlowValueFactory {
|
||||
return NO_IDENTIFIER_INFO;
|
||||
}
|
||||
|
||||
public static boolean isUncapturedLocalVariable(@NotNull VariableDescriptor variableDescriptor, @NotNull BindingContext bindingContext) {
|
||||
return variableDescriptor.isVar()
|
||||
&& variableDescriptor instanceof LocalVariableDescriptor
|
||||
&& !BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor);
|
||||
public static DataFlowValue.Kind variableKind(
|
||||
@NotNull VariableDescriptor variableDescriptor,
|
||||
@Nullable ModuleDescriptor usageModule,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
if (isStableVariable(variableDescriptor, usageModule)) return DataFlowValue.Kind.STABLE_VALUE;
|
||||
boolean isLocalVar = variableDescriptor.isVar() && variableDescriptor instanceof LocalVariableDescriptor;
|
||||
if (!isLocalVar) return DataFlowValue.Kind.OTHER;
|
||||
if (BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor)) {
|
||||
return DataFlowValue.Kind.UNPREDICTABLE_VARIABLE;
|
||||
}
|
||||
return DataFlowValue.Kind.PREDICTABLE_VARIABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -43,8 +43,8 @@ class PreliminaryLoopVisitor extends AssignedVariablesSearcher {
|
||||
Map<DataFlowValue, Nullability> nullabilityMap = dataFlowInfo.getCompleteNullabilityInfo();
|
||||
Set<DataFlowValue> valueSetToClear = new LinkedHashSet<DataFlowValue>();
|
||||
for (DataFlowValue value: nullabilityMap.keySet()) {
|
||||
// Only uncaptured local variables are under interest here
|
||||
if (value.isUncapturedLocalVariable() && value.getId() instanceof LocalVariableDescriptor) {
|
||||
// Only predictable variables are under interest here
|
||||
if (value.getKind() == DataFlowValue.Kind.PREDICTABLE_VARIABLE && value.getId() instanceof LocalVariableDescriptor) {
|
||||
LocalVariableDescriptor descriptor = (LocalVariableDescriptor)value.getId();
|
||||
if (getAssignedNames().contains(descriptor.getName())) {
|
||||
valueSetToClear.add(value);
|
||||
|
||||
Reference in New Issue
Block a user