Info extraction for is-conditions in if's
This commit is contained in:
@@ -1,19 +1,25 @@
|
|||||||
package org.jetbrains.jet.lang.types;
|
package org.jetbrains.jet.lang.types;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.*;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
public class DataFlowInfo {
|
public class DataFlowInfo {
|
||||||
|
|
||||||
private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.<VariableDescriptor, NullabilityFlags>of());
|
private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.<VariableDescriptor, NullabilityFlags>of(), Multimaps.forMap(Collections.<VariableDescriptor, JetType>emptyMap()));
|
||||||
|
public static final Supplier<SortedSet<JetType>> SORTED_SET_SUPPLIER = new Supplier<SortedSet<JetType>>() {
|
||||||
|
@Override
|
||||||
|
public SortedSet<JetType> get() {
|
||||||
|
return new TreeSet<JetType>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static DataFlowInfo getEmpty() {
|
public static DataFlowInfo getEmpty() {
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
@@ -40,9 +46,11 @@ public class DataFlowInfo {
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private final ImmutableMap<VariableDescriptor, NullabilityFlags> nullabilityInfo;
|
private final ImmutableMap<VariableDescriptor, NullabilityFlags> nullabilityInfo;
|
||||||
|
private final Multimap<VariableDescriptor, JetType> typeInfo;
|
||||||
|
|
||||||
public DataFlowInfo(ImmutableMap<VariableDescriptor, NullabilityFlags> nullabilityInfo) {
|
public DataFlowInfo(ImmutableMap<VariableDescriptor, NullabilityFlags> nullabilityInfo, Multimap<VariableDescriptor, JetType> typeInfo) {
|
||||||
this.nullabilityInfo = nullabilityInfo;
|
this.nullabilityInfo = nullabilityInfo;
|
||||||
|
this.typeInfo = typeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -57,27 +65,51 @@ public class DataFlowInfo {
|
|||||||
return outType;
|
return outType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Collection<JetType> getPossibleTypes(VariableDescriptor variableDescriptor) {
|
||||||
|
return typeInfo.get(variableDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
public DataFlowInfo equalsToNull(@NotNull VariableDescriptor variableDescriptor, boolean notNull) {
|
public DataFlowInfo equalsToNull(@NotNull VariableDescriptor variableDescriptor, boolean notNull) {
|
||||||
|
return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, notNull), typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableMap<VariableDescriptor, NullabilityFlags> getEqualsToNullMap(VariableDescriptor variableDescriptor, boolean notNull) {
|
||||||
Map<VariableDescriptor, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
|
Map<VariableDescriptor, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
|
||||||
builder.put(variableDescriptor, new NullabilityFlags(!notNull, notNull));
|
builder.put(variableDescriptor, new NullabilityFlags(!notNull, notNull));
|
||||||
return new DataFlowInfo(ImmutableMap.copyOf(builder));
|
return ImmutableMap.copyOf(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor variableDescriptor, @NotNull JetType type) {
|
||||||
|
Multimap<VariableDescriptor, JetType> newTypeInfo = copyTypeInfo();
|
||||||
|
newTypeInfo.put(variableDescriptor, type);
|
||||||
|
return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, false), newTypeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataFlowInfo and(DataFlowInfo other) {
|
public DataFlowInfo and(DataFlowInfo other) {
|
||||||
Map<VariableDescriptor, NullabilityFlags> builder = Maps.newHashMap();
|
Map<VariableDescriptor, NullabilityFlags> nullabilityMapBuilder = Maps.newHashMap();
|
||||||
builder.putAll(nullabilityInfo);
|
nullabilityMapBuilder.putAll(nullabilityInfo);
|
||||||
for (Map.Entry<VariableDescriptor, NullabilityFlags> entry : other.nullabilityInfo.entrySet()) {
|
for (Map.Entry<VariableDescriptor, NullabilityFlags> entry : other.nullabilityInfo.entrySet()) {
|
||||||
VariableDescriptor variableDescriptor = entry.getKey();
|
VariableDescriptor variableDescriptor = entry.getKey();
|
||||||
NullabilityFlags otherFlags = entry.getValue();
|
NullabilityFlags otherFlags = entry.getValue();
|
||||||
NullabilityFlags thisFlags = nullabilityInfo.get(variableDescriptor);
|
NullabilityFlags thisFlags = nullabilityInfo.get(variableDescriptor);
|
||||||
if (thisFlags != null) {
|
if (thisFlags != null) {
|
||||||
builder.put(variableDescriptor, thisFlags.and(otherFlags));
|
nullabilityMapBuilder.put(variableDescriptor, thisFlags.and(otherFlags));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
builder.put(variableDescriptor, otherFlags);
|
nullabilityMapBuilder.put(variableDescriptor, otherFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new DataFlowInfo(ImmutableMap.copyOf(builder));
|
|
||||||
|
Multimap<VariableDescriptor, JetType> newTypeInfo = copyTypeInfo();
|
||||||
|
newTypeInfo.putAll(other.typeInfo);
|
||||||
|
return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Multimap<VariableDescriptor, JetType> copyTypeInfo() {
|
||||||
|
Multimap<VariableDescriptor, JetType> newTypeInfo = Multimaps.newSortedSetMultimap(Maps.<VariableDescriptor, Collection<JetType>>newHashMap(), SORTED_SET_SUPPLIER);
|
||||||
|
newTypeInfo.putAll(typeInfo);
|
||||||
|
return newTypeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataFlowInfo or(DataFlowInfo other) {
|
public DataFlowInfo or(DataFlowInfo other) {
|
||||||
@@ -90,7 +122,23 @@ public class DataFlowInfo {
|
|||||||
assert (otherFlags != null);
|
assert (otherFlags != null);
|
||||||
builder.put(variableDescriptor, thisFlags.or(otherFlags));
|
builder.put(variableDescriptor, thisFlags.or(otherFlags));
|
||||||
}
|
}
|
||||||
return new DataFlowInfo(ImmutableMap.copyOf(builder));
|
|
||||||
|
Multimap<VariableDescriptor, JetType> newTypeInfo = copyTypeInfo();
|
||||||
|
|
||||||
|
Set<VariableDescriptor> keys = newTypeInfo.keySet();
|
||||||
|
keys.retainAll(other.typeInfo.keySet());
|
||||||
|
|
||||||
|
for (VariableDescriptor variableDescriptor : keys) {
|
||||||
|
Collection<JetType> thisTypes = typeInfo.get(variableDescriptor);
|
||||||
|
Collection<JetType> otherTypes = other.typeInfo.get(variableDescriptor);
|
||||||
|
|
||||||
|
Collection<JetType> newTypes = Sets.newHashSet(thisTypes);
|
||||||
|
newTypes.retainAll(otherTypes);
|
||||||
|
|
||||||
|
newTypeInfo.putAll(variableDescriptor, newTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NullabilityFlags {
|
private static class NullabilityFlags {
|
||||||
|
|||||||
@@ -1344,6 +1344,18 @@ public class JetTypeInferrer {
|
|||||||
if (condition == null) return dataFlowInfo;
|
if (condition == null) return dataFlowInfo;
|
||||||
final DataFlowInfo[] result = new DataFlowInfo[] {dataFlowInfo};
|
final DataFlowInfo[] result = new DataFlowInfo[] {dataFlowInfo};
|
||||||
condition.accept(new JetVisitor() {
|
condition.accept(new JetVisitor() {
|
||||||
|
@Override
|
||||||
|
public void visitIsExpression(JetIsExpression expression) {
|
||||||
|
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression.getLeftHandSide());
|
||||||
|
if (variableDescriptor != null) {
|
||||||
|
JetPattern pattern = expression.getPattern();
|
||||||
|
if (pattern instanceof JetTypePattern) {
|
||||||
|
JetTypePattern jetTypePattern = (JetTypePattern) pattern;
|
||||||
|
result[0] = dataFlowInfo.isInstanceOf(variableDescriptor, trace.getBindingContext().resolveTypeReference(jetTypePattern.getTypeReference()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitBinaryExpression(JetBinaryExpression expression) {
|
public void visitBinaryExpression(JetBinaryExpression expression) {
|
||||||
IElementType operationToken = expression.getOperationToken();
|
IElementType operationToken = expression.getOperationToken();
|
||||||
@@ -1612,6 +1624,20 @@ public class JetTypeInferrer {
|
|||||||
JetType receiverType = new TypeInferrerVisitorWithNamespaces(scope, false, dataFlowInfo).getType(receiverExpression);
|
JetType receiverType = new TypeInferrerVisitorWithNamespaces(scope, false, dataFlowInfo).getType(receiverExpression);
|
||||||
if (receiverType != null) {
|
if (receiverType != null) {
|
||||||
JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression);
|
JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression);
|
||||||
|
|
||||||
|
if (selectorReturnType == null) {
|
||||||
|
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(receiverExpression);
|
||||||
|
if (variableDescriptor != null) {
|
||||||
|
Collection<JetType> possibleTypes = dataFlowInfo.getPossibleTypes(variableDescriptor);
|
||||||
|
for (JetType possibleType : possibleTypes) {
|
||||||
|
selectorReturnType = getSelectorReturnType(possibleType, selectorExpression);
|
||||||
|
if (selectorReturnType != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (expression.getOperationSign() == JetTokens.QUEST) {
|
if (expression.getOperationSign() == JetTokens.QUEST) {
|
||||||
if (selectorReturnType != null && !isBoolean(selectorReturnType) && selectorExpression != null) {
|
if (selectorReturnType != null && !isBoolean(selectorReturnType) && selectorExpression != null) {
|
||||||
// TODO : more comprehensible error message
|
// TODO : more comprehensible error message
|
||||||
@@ -1637,15 +1663,24 @@ public class JetTypeInferrer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JetType enrichOutType(JetExpression receiverExpression, JetType receiverType) {
|
private JetType enrichOutType(JetExpression receiverExpression, JetType receiverType) {
|
||||||
|
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(receiverExpression);
|
||||||
|
if (variableDescriptor != null) {
|
||||||
|
return dataFlowInfo.getOutType(variableDescriptor);
|
||||||
|
}
|
||||||
|
return receiverType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private VariableDescriptor getVariableDescriptorFromSimpleName(@NotNull JetExpression receiverExpression) {
|
||||||
|
VariableDescriptor variableDescriptor = null;
|
||||||
if (receiverExpression instanceof JetSimpleNameExpression) {
|
if (receiverExpression instanceof JetSimpleNameExpression) {
|
||||||
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiverExpression;
|
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiverExpression;
|
||||||
DeclarationDescriptor declarationDescriptor = trace.getBindingContext().resolveReferenceExpression(nameExpression);
|
DeclarationDescriptor declarationDescriptor = trace.getBindingContext().resolveReferenceExpression(nameExpression);
|
||||||
if (declarationDescriptor instanceof VariableDescriptor) {
|
if (declarationDescriptor instanceof VariableDescriptor) {
|
||||||
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
||||||
return dataFlowInfo.getOutType(variableDescriptor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return receiverType;
|
return variableDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
Reference in New Issue
Block a user