Review fixes for mutable variable smart casting (mostly refactoring).
DataFlowValue.isPredictable() introduced instead of isStableIdentifier(). Parenthesized assignments are treated correctly.
This commit is contained in:
@@ -446,7 +446,7 @@ public class CandidateResolver {
|
|||||||
if (deparenthesizedArgument == null || type == null) return type;
|
if (deparenthesizedArgument == null || type == null) return type;
|
||||||
|
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context);
|
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context);
|
||||||
if (!dataFlowValue.isStableIdentifier() && !dataFlowValue.isLocalVariable()) return type;
|
if (!dataFlowValue.isPredictable()) return type;
|
||||||
|
|
||||||
Set<JetType> possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue);
|
Set<JetType> possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue);
|
||||||
if (possibleTypes.isEmpty()) return type;
|
if (possibleTypes.isEmpty()) return type;
|
||||||
|
|||||||
+20
-6
@@ -33,15 +33,17 @@ public class DataFlowValue {
|
|||||||
public static final DataFlowValue ERROR = new DataFlowValue(new Object(), ErrorUtils.createErrorType("Error type for data flow"), false, false, Nullability.IMPOSSIBLE);
|
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 stableIdentifier;
|
||||||
private final boolean localVariable;
|
private final boolean uncapturedlocalVariable;
|
||||||
private final JetType type;
|
private final JetType type;
|
||||||
private final Object id;
|
private final Object id;
|
||||||
private final Nullability immanentNullability;
|
private final Nullability immanentNullability;
|
||||||
|
|
||||||
// Use DataFlowValueFactory
|
// Use DataFlowValueFactory
|
||||||
/*package*/ DataFlowValue(Object id, JetType type, boolean stableIdentifier, boolean localVariable, Nullability immanentNullability) {
|
/*package*/ DataFlowValue(Object id, JetType type, boolean stableIdentifier, boolean uncapturedlocalVariable, Nullability immanentNullability) {
|
||||||
|
assert !stableIdentifier || !uncapturedlocalVariable :
|
||||||
|
"data flow value cannot be together a stable identifier and an uncaptured local variable";
|
||||||
this.stableIdentifier = stableIdentifier;
|
this.stableIdentifier = stableIdentifier;
|
||||||
this.localVariable = localVariable;
|
this.uncapturedlocalVariable = uncapturedlocalVariable;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.immanentNullability = immanentNullability;
|
this.immanentNullability = immanentNullability;
|
||||||
@@ -59,16 +61,28 @@ public class DataFlowValue {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stable identifier is a non-literal value that is statically known to be immutable
|
* 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
|
||||||
*/
|
*/
|
||||||
public boolean isStableIdentifier() {
|
private boolean isStableIdentifier() {
|
||||||
return stableIdentifier;
|
return stableIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifier is considered a local variable here if it's mutable (var), local and not captured in a closure
|
* Identifier is considered a local variable here if it's mutable (var), local and not captured in a closure
|
||||||
*/
|
*/
|
||||||
public boolean isLocalVariable() {
|
public boolean isUncapturedLocalVariable() {
|
||||||
return localVariable;
|
return uncapturedlocalVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Both stable identifiers and uncaptured local variables 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.
|
||||||
|
*/
|
||||||
|
public boolean isPredictable() {
|
||||||
|
return stableIdentifier || uncapturedlocalVariable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
+14
-11
@@ -130,7 +130,7 @@ public class DataFlowValueFactory {
|
|||||||
JetType type = variableDescriptor.getType();
|
JetType type = variableDescriptor.getType();
|
||||||
return new DataFlowValue(variableDescriptor, type,
|
return new DataFlowValue(variableDescriptor, type,
|
||||||
isStableVariable(variableDescriptor, usageContainingModule),
|
isStableVariable(variableDescriptor, usageContainingModule),
|
||||||
isLocalVariable(variableDescriptor, bindingContext),
|
isUncapturedLocalVariable(variableDescriptor, bindingContext),
|
||||||
getImmanentNullability(type));
|
getImmanentNullability(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +165,11 @@ public class DataFlowValueFactory {
|
|||||||
return new IdentifierInfo(id, isStable, isLocal, false);
|
return new IdentifierInfo(id, isStable, isLocal, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static IdentifierInfo createStableInfo(Object id) {
|
||||||
|
return createInfo(id, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static IdentifierInfo createPackageInfo(Object id) {
|
private static IdentifierInfo createPackageInfo(Object id) {
|
||||||
return new IdentifierInfo(id, true, false, true);
|
return new IdentifierInfo(id, true, false, true);
|
||||||
@@ -180,6 +185,7 @@ public class DataFlowValueFactory {
|
|||||||
}
|
}
|
||||||
return createInfo(Pair.create(receiverInfo.id, selectorInfo.id),
|
return createInfo(Pair.create(receiverInfo.id, selectorInfo.id),
|
||||||
receiverInfo.isStable && selectorInfo.isStable,
|
receiverInfo.isStable && selectorInfo.isStable,
|
||||||
|
// x.y can never be a local variable
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +246,7 @@ public class DataFlowValueFactory {
|
|||||||
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
||||||
return combineInfo(receiverInfo, createInfo(variableDescriptor,
|
return combineInfo(receiverInfo, createInfo(variableDescriptor,
|
||||||
isStableVariable(variableDescriptor, usageModuleDescriptor),
|
isStableVariable(variableDescriptor, usageModuleDescriptor),
|
||||||
isLocalVariable(variableDescriptor, bindingContext)));
|
isUncapturedLocalVariable(variableDescriptor, bindingContext)));
|
||||||
}
|
}
|
||||||
if (declarationDescriptor instanceof PackageViewDescriptor) {
|
if (declarationDescriptor instanceof PackageViewDescriptor) {
|
||||||
return createPackageInfo(declarationDescriptor);
|
return createPackageInfo(declarationDescriptor);
|
||||||
@@ -267,21 +273,18 @@ public class DataFlowValueFactory {
|
|||||||
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter();
|
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter();
|
||||||
assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " +
|
assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " +
|
||||||
descriptorOfThisReceiver;
|
descriptorOfThisReceiver;
|
||||||
return createInfo(receiverParameter.getValue(), true, false);
|
return createStableInfo(receiverParameter.getValue());
|
||||||
}
|
}
|
||||||
if (descriptorOfThisReceiver instanceof ClassDescriptor) {
|
if (descriptorOfThisReceiver instanceof ClassDescriptor) {
|
||||||
return createInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue(), true, false);
|
return createStableInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue());
|
||||||
}
|
}
|
||||||
return NO_IDENTIFIER_INFO;
|
return NO_IDENTIFIER_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLocalVariable(@NotNull VariableDescriptor variableDescriptor, @NotNull BindingContext bindingContext) {
|
public static boolean isUncapturedLocalVariable(@NotNull VariableDescriptor variableDescriptor, @NotNull BindingContext bindingContext) {
|
||||||
if (variableDescriptor.isVar() && variableDescriptor instanceof LocalVariableDescriptor) {
|
return variableDescriptor.isVar()
|
||||||
if (BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor))
|
&& variableDescriptor instanceof LocalVariableDescriptor
|
||||||
return false;
|
&& !BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+17
-25
@@ -39,14 +39,12 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
|||||||
@NotNull
|
@NotNull
|
||||||
private final ImmutableMap<DataFlowValue, Nullability> nullabilityInfo;
|
private final ImmutableMap<DataFlowValue, Nullability> nullabilityInfo;
|
||||||
|
|
||||||
/**
|
// Also immutable
|
||||||
* Also immutable
|
|
||||||
*/
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final SetMultimap<DataFlowValue, JetType> typeInfo;
|
private final SetMultimap<DataFlowValue, JetType> typeInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value for which type info was cleared at this point
|
* Value for which type info was cleared or reassigned at this point
|
||||||
* so parent type info should not be in use
|
* so parent type info should not be in use
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -94,16 +92,16 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
|||||||
@NotNull
|
@NotNull
|
||||||
public SetMultimap<DataFlowValue, JetType> getCompleteTypeInfo() {
|
public SetMultimap<DataFlowValue, JetType> getCompleteTypeInfo() {
|
||||||
SetMultimap<DataFlowValue, JetType> result = newTypeInfo();
|
SetMultimap<DataFlowValue, JetType> result = newTypeInfo();
|
||||||
Set<DataFlowValue> resultCompleted = new HashSet<DataFlowValue>();
|
Set<DataFlowValue> withGivenTypeInfo = new HashSet<DataFlowValue>();
|
||||||
DelegatingDataFlowInfo info = this;
|
DelegatingDataFlowInfo info = this;
|
||||||
while (info != null) {
|
while (info != null) {
|
||||||
for (DataFlowValue key : info.typeInfo.keySet()) {
|
for (DataFlowValue key : info.typeInfo.keySet()) {
|
||||||
if (!resultCompleted.contains(key)) {
|
if (!withGivenTypeInfo.contains(key)) {
|
||||||
result.putAll(key, info.typeInfo.get(key));
|
result.putAll(key, info.typeInfo.get(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (valueWithGivenTypeInfo != null) {
|
if (valueWithGivenTypeInfo != null) {
|
||||||
resultCompleted.add(valueWithGivenTypeInfo);
|
withGivenTypeInfo.add(valueWithGivenTypeInfo);
|
||||||
}
|
}
|
||||||
info = (DelegatingDataFlowInfo) info.parent;
|
info = (DelegatingDataFlowInfo) info.parent;
|
||||||
}
|
}
|
||||||
@@ -113,7 +111,7 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public Nullability getNullability(@NotNull DataFlowValue key) {
|
public Nullability getNullability(@NotNull DataFlowValue key) {
|
||||||
if (!key.isStableIdentifier() && !key.isLocalVariable()) return key.getImmanentNullability();
|
if (!key.isPredictable()) return key.getImmanentNullability();
|
||||||
Nullability nullability = nullabilityInfo.get(key);
|
Nullability nullability = nullabilityInfo.get(key);
|
||||||
return nullability != null ? nullability :
|
return nullability != null ? nullability :
|
||||||
parent != null ? parent.getNullability(key) :
|
parent != null ? parent.getNullability(key) :
|
||||||
@@ -125,7 +123,7 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
|||||||
@NotNull DataFlowValue value,
|
@NotNull DataFlowValue value,
|
||||||
@NotNull Nullability nullability
|
@NotNull Nullability nullability
|
||||||
) {
|
) {
|
||||||
if (!value.isStableIdentifier() && !value.isLocalVariable()) return false;
|
if (!value.isPredictable()) return false;
|
||||||
map.put(value, nullability);
|
map.put(value, nullability);
|
||||||
return nullability != getNullability(value);
|
return nullability != getNullability(value);
|
||||||
}
|
}
|
||||||
@@ -160,14 +158,8 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
|||||||
@NotNull
|
@NotNull
|
||||||
public DataFlowInfo clearValueInfo(@NotNull DataFlowValue value) {
|
public DataFlowInfo clearValueInfo(@NotNull DataFlowValue value) {
|
||||||
Map<DataFlowValue, Nullability> builder = Maps.newHashMap();
|
Map<DataFlowValue, Nullability> builder = Maps.newHashMap();
|
||||||
boolean changed = putNullability(builder, value, Nullability.UNKNOWN);
|
putNullability(builder, value, Nullability.UNKNOWN);
|
||||||
// We want to clear all these types
|
return new DelegatingDataFlowInfo(
|
||||||
if (!changed) {
|
|
||||||
changed = !collectTypesFromMeAndParents(value).isEmpty();
|
|
||||||
}
|
|
||||||
return !changed
|
|
||||||
? this
|
|
||||||
: new DelegatingDataFlowInfo(
|
|
||||||
this,
|
this,
|
||||||
ImmutableMap.copyOf(builder),
|
ImmutableMap.copyOf(builder),
|
||||||
EMPTY_TYPE_INFO,
|
EMPTY_TYPE_INFO,
|
||||||
@@ -178,23 +170,23 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public DataFlowInfo assign(@NotNull DataFlowValue a, @NotNull DataFlowValue b) {
|
public DataFlowInfo assign(@NotNull DataFlowValue a, @NotNull DataFlowValue b) {
|
||||||
Map<DataFlowValue, Nullability> builder = Maps.newHashMap();
|
Map<DataFlowValue, Nullability> nullability = Maps.newHashMap();
|
||||||
Nullability nullabilityOfB = getNullability(b);
|
Nullability nullabilityOfB = getNullability(b);
|
||||||
boolean changed = putNullability(builder, a, nullabilityOfB);
|
putNullability(nullability, a, nullabilityOfB);
|
||||||
|
|
||||||
SetMultimap<DataFlowValue, JetType> newTypeInfo = newTypeInfo();
|
SetMultimap<DataFlowValue, JetType> newTypeInfo = newTypeInfo();
|
||||||
Set<JetType> typesForA = collectTypesFromMeAndParents(a);
|
|
||||||
Set<JetType> typesForB = collectTypesFromMeAndParents(b);
|
Set<JetType> typesForB = collectTypesFromMeAndParents(b);
|
||||||
|
// Own type of B must be recorded separately, e.g. for a constant
|
||||||
|
// But if its type is the same as A or it's null, there is no reason to do it
|
||||||
|
// because usually null type or own type are not saved in this set
|
||||||
if (nullabilityOfB.canBeNonNull() && !a.getType().equals(b.getType())) {
|
if (nullabilityOfB.canBeNonNull() && !a.getType().equals(b.getType())) {
|
||||||
typesForB.add(b.getType());
|
typesForB.add(b.getType());
|
||||||
}
|
}
|
||||||
newTypeInfo.putAll(a, typesForB);
|
newTypeInfo.putAll(a, typesForB);
|
||||||
changed |= !typesForA.equals(typesForB);
|
|
||||||
|
|
||||||
return !changed
|
return new DelegatingDataFlowInfo(
|
||||||
? this
|
|
||||||
: new DelegatingDataFlowInfo(
|
|
||||||
this,
|
this,
|
||||||
ImmutableMap.copyOf(builder),
|
ImmutableMap.copyOf(nullability),
|
||||||
newTypeInfo.isEmpty() ? EMPTY_TYPE_INFO : newTypeInfo,
|
newTypeInfo.isEmpty() ? EMPTY_TYPE_INFO : newTypeInfo,
|
||||||
a
|
a
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-5
@@ -179,11 +179,7 @@ public class SmartCastUtils {
|
|||||||
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
|
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context);
|
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context);
|
||||||
|
|
||||||
recordCastOrError(expression,
|
recordCastOrError(expression, smartCastSubType, context.trace, dataFlowValue.isPredictable(), true);
|
||||||
smartCastSubType,
|
|
||||||
context.trace,
|
|
||||||
dataFlowValue.isStableIdentifier() || dataFlowValue.isLocalVariable(),
|
|
||||||
true);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-8
@@ -201,9 +201,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(
|
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(
|
||||||
INDEPENDENT);
|
INDEPENDENT);
|
||||||
// Preliminary analysis
|
// Preliminary analysis
|
||||||
PreliminaryLoopVisitor loopVisitor = new PreliminaryLoopVisitor(expression);
|
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
|
||||||
loopVisitor.launch();
|
context = context.replaceDataFlowInfo(
|
||||||
context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo));
|
loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)
|
||||||
|
);
|
||||||
|
|
||||||
JetExpression condition = expression.getCondition();
|
JetExpression condition = expression.getCondition();
|
||||||
// Extract data flow info from condition itself without taking value into account
|
// Extract data flow info from condition itself without taking value into account
|
||||||
@@ -288,9 +289,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
JetExpression body = expression.getBody();
|
JetExpression body = expression.getBody();
|
||||||
JetScope conditionScope = context.scope;
|
JetScope conditionScope = context.scope;
|
||||||
// Preliminary analysis
|
// Preliminary analysis
|
||||||
PreliminaryLoopVisitor loopVisitor = new PreliminaryLoopVisitor(expression);
|
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
|
||||||
loopVisitor.launch();
|
context = context.replaceDataFlowInfo(
|
||||||
context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo));
|
loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)
|
||||||
|
);
|
||||||
// Here we must record data flow information at the end of the body (or at the first jump, to be precise) and
|
// Here we must record data flow information at the end of the body (or at the first jump, to be precise) and
|
||||||
// .and it with entrance data flow information, because do-while body is executed at least once
|
// .and it with entrance data flow information, because do-while body is executed at least once
|
||||||
// See KT-6283
|
// See KT-6283
|
||||||
@@ -354,8 +356,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
ExpressionTypingContext context =
|
ExpressionTypingContext context =
|
||||||
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
|
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
|
||||||
// Preliminary analysis
|
// Preliminary analysis
|
||||||
PreliminaryLoopVisitor loopVisitor = new PreliminaryLoopVisitor(expression);
|
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
|
||||||
loopVisitor.launch();
|
|
||||||
context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo));
|
context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo));
|
||||||
|
|
||||||
JetExpression loopRange = expression.getLoopRange();
|
JetExpression loopRange = expression.getLoopRange();
|
||||||
|
|||||||
@@ -199,11 +199,7 @@ public class DataFlowUtils {
|
|||||||
|
|
||||||
for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
||||||
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) {
|
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) {
|
||||||
SmartCastUtils.recordCastOrError(expression,
|
SmartCastUtils.recordCastOrError(expression, possibleType, c.trace, dataFlowValue.isPredictable(), false);
|
||||||
possibleType,
|
|
||||||
c.trace,
|
|
||||||
dataFlowValue.isStableIdentifier() || dataFlowValue.isLocalVariable(),
|
|
||||||
false);
|
|
||||||
return possibleType;
|
return possibleType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,7 +261,7 @@ public class DataFlowUtils {
|
|||||||
) {
|
) {
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
|
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
|
||||||
Collection<JetType> possibleTypes = Sets.newHashSet(type);
|
Collection<JetType> possibleTypes = Sets.newHashSet(type);
|
||||||
if (dataFlowValue.isStableIdentifier()) {
|
if (dataFlowValue.isPredictable()) {
|
||||||
possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
|
possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
|
||||||
}
|
}
|
||||||
return possibleTypes;
|
return possibleTypes;
|
||||||
|
|||||||
+3
@@ -149,6 +149,8 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
propertyDescriptor, context.trace.getBindingContext(),
|
propertyDescriptor, context.trace.getBindingContext(),
|
||||||
DescriptorUtils.getContainingModuleOrNull(scope.getContainingDeclaration()));
|
DescriptorUtils.getContainingModuleOrNull(scope.getContainingDeclaration()));
|
||||||
DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context);
|
DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context);
|
||||||
|
// We cannot say here anything new about initializerDataFlowValue
|
||||||
|
// except it has the same value as variableDataFlowValue
|
||||||
dataFlowInfo = dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue);
|
dataFlowInfo = dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -350,6 +352,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
if (left != null && leftType != null && rightType != null) {
|
if (left != null && leftType != null && rightType != null) {
|
||||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context);
|
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context);
|
||||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||||
|
// We cannot say here anything new about rightValue except it has the same value as leftValue
|
||||||
dataFlowInfo = dataFlowInfo.assign(leftValue, rightValue);
|
dataFlowInfo = dataFlowInfo.assign(leftValue, rightValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-16
@@ -36,24 +36,26 @@ class PreliminaryLoopVisitor extends JetTreeVisitor<Void> {
|
|||||||
// loop under analysis
|
// loop under analysis
|
||||||
private final JetLoopExpression loopExpression;
|
private final JetLoopExpression loopExpression;
|
||||||
|
|
||||||
private Set<Name> assignedNames = new LinkedHashSet<Name>();
|
private final Set<Name> assignedNames = new LinkedHashSet<Name>();
|
||||||
|
|
||||||
public PreliminaryLoopVisitor(JetLoopExpression loopExpression) {
|
private PreliminaryLoopVisitor(@NotNull JetLoopExpression loopExpression) {
|
||||||
this.loopExpression = loopExpression;
|
this.loopExpression = loopExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void launch() {
|
@NotNull
|
||||||
loopExpression.accept(this, null);
|
static public PreliminaryLoopVisitor visitLoop(@NotNull JetLoopExpression loopExpression) {
|
||||||
|
PreliminaryLoopVisitor visitor = new PreliminaryLoopVisitor(loopExpression);
|
||||||
|
loopExpression.accept(visitor, null);
|
||||||
|
return visitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataFlowInfo clearDataFlowInfoForAssignedLocalVariables(DataFlowInfo dataFlowInfo) {
|
@NotNull
|
||||||
|
public DataFlowInfo clearDataFlowInfoForAssignedLocalVariables(@NotNull DataFlowInfo dataFlowInfo) {
|
||||||
Map<DataFlowValue, Nullability> nullabilityMap = dataFlowInfo.getCompleteNullabilityInfo();
|
Map<DataFlowValue, Nullability> nullabilityMap = dataFlowInfo.getCompleteNullabilityInfo();
|
||||||
Set<DataFlowValue> valueSetToClear = new LinkedHashSet<DataFlowValue>();
|
Set<DataFlowValue> valueSetToClear = new LinkedHashSet<DataFlowValue>();
|
||||||
for (DataFlowValue value: nullabilityMap.keySet()) {
|
for (DataFlowValue value: nullabilityMap.keySet()) {
|
||||||
// Only local variables which are not stable are under interest
|
// Only uncaptured local variables are under interest here
|
||||||
if (value.isStableIdentifier() || !value.isLocalVariable())
|
if (value.isUncapturedLocalVariable() && value.getId() instanceof LocalVariableDescriptor) {
|
||||||
continue;
|
|
||||||
if (value.getId() instanceof LocalVariableDescriptor) {
|
|
||||||
LocalVariableDescriptor descriptor = (LocalVariableDescriptor)value.getId();
|
LocalVariableDescriptor descriptor = (LocalVariableDescriptor)value.getId();
|
||||||
if (assignedNames.contains(descriptor.getName())) {
|
if (assignedNames.contains(descriptor.getName())) {
|
||||||
valueSetToClear.add(value);
|
valueSetToClear.add(value);
|
||||||
@@ -66,15 +68,14 @@ class PreliminaryLoopVisitor extends JetTreeVisitor<Void> {
|
|||||||
return dataFlowInfo;
|
return dataFlowInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visitLoopExpression(@NotNull JetLoopExpression loopExpression, Void arg) {
|
|
||||||
return super.visitLoopExpression(loopExpression, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitBinaryExpression(@NotNull JetBinaryExpression binaryExpression, Void arg) {
|
public Void visitBinaryExpression(@NotNull JetBinaryExpression binaryExpression, Void arg) {
|
||||||
if (binaryExpression.getOperationToken() == JetTokens.EQ && binaryExpression.getLeft() instanceof JetNameReferenceExpression) {
|
if (binaryExpression.getOperationToken() == JetTokens.EQ) {
|
||||||
assignedNames.add(((JetNameReferenceExpression) binaryExpression.getLeft()).getReferencedNameAsName());
|
JetExpression left = JetPsiUtil.deparenthesize(binaryExpression.getLeft());
|
||||||
|
if (left instanceof JetNameReferenceExpression) {
|
||||||
|
// deparenthesize
|
||||||
|
assignedNames.add(((JetNameReferenceExpression) left).getReferencedNameAsName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ fun test() : Unit {
|
|||||||
y <!USELESS_CAST!>as Int<!> : Int
|
y <!USELESS_CAST!>as Int<!> : Int
|
||||||
x <!USELESS_CAST!>as Int?<!> : Int?
|
x <!USELESS_CAST!>as Int?<!> : Int?
|
||||||
y <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as Int?<!> : Int?
|
y <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as Int?<!> : Int?
|
||||||
x as? Int : Int?
|
x <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as? Int<!> : Int?
|
||||||
y <!USELESS_CAST!>as? Int<!> : Int?
|
y <!USELESS_CAST!>as? Int<!> : Int?
|
||||||
x <!USELESS_CAST!>as? Int?<!> : Int?
|
x <!USELESS_CAST!>as? Int?<!> : Int?
|
||||||
y <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as? Int?<!> : Int?
|
y <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as? Int?<!> : Int?
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class MyTest() {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
a = "ss"
|
a = "ss"
|
||||||
doSmth(a as String)
|
doSmth(<!DEBUG_INFO_SMARTCAST!>a<!>)
|
||||||
}
|
}
|
||||||
doSmth(a)
|
doSmth(a)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
fun x(): Boolean { return true }
|
||||||
|
|
||||||
|
public fun foo(pp: String?): Int {
|
||||||
|
var p = pp
|
||||||
|
while(true) {
|
||||||
|
p!!.length()
|
||||||
|
if (x()) break
|
||||||
|
(((p))) = null
|
||||||
|
}
|
||||||
|
// Smart cast is NOT possible here
|
||||||
|
// (we could provide it but p = null makes it much harder)
|
||||||
|
return p<!UNSAFE_CALL!>.<!>length()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun foo(/*0*/ pp: kotlin.String?): kotlin.Int
|
||||||
|
internal fun x(): kotlin.Boolean
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
fun x(): Boolean { return true }
|
||||||
|
|
||||||
|
public fun foo(pp: String?): Int {
|
||||||
|
var p = pp
|
||||||
|
while(true) {
|
||||||
|
p!!.length()
|
||||||
|
if (x()) break
|
||||||
|
(p) = null
|
||||||
|
}
|
||||||
|
// Smart cast is NOT possible here
|
||||||
|
// (we could provide it but p = null makes it much harder)
|
||||||
|
return p<!UNSAFE_CALL!>.<!>length()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun foo(/*0*/ pp: kotlin.String?): kotlin.Int
|
||||||
|
internal fun x(): kotlin.Boolean
|
||||||
@@ -11786,6 +11786,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whileTrueWithBracketSet.kt")
|
||||||
|
public void testWhileTrueWithBracketSet() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBracketSet.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whileTrueWithBrackets.kt")
|
||||||
|
public void testWhileTrueWithBrackets() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBrackets.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("whileWithBreak.kt")
|
@TestMetadata("whileWithBreak.kt")
|
||||||
public void testWhileWithBreak() throws Exception {
|
public void testWhileWithBreak() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.kt");
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
|||||||
|
|
||||||
fun renderDataFlowValue(value: DataFlowValue): String? {
|
fun renderDataFlowValue(value: DataFlowValue): String? {
|
||||||
// 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 (!value.isStableIdentifier() && !value.isLocalVariable()) return null
|
if (!value.isPredictable()) return null
|
||||||
|
|
||||||
fun renderId(id: Any?): String? {
|
fun renderId(id: Any?): String? {
|
||||||
return when (id) {
|
return when (id) {
|
||||||
|
|||||||
Reference in New Issue
Block a user