Autocasts for 'this' and stable qualified expressions
This commit is contained in:
@@ -10,7 +10,7 @@ import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetModifierList;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallMaker;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallResolver;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
@@ -36,7 +36,7 @@ public class AnnotationResolver {
|
||||
this.trace = trace;
|
||||
// this.typeInferrer = new JetTypeInferrer(JetFlowInformationProvider.THROW_EXCEPTION, semanticServices);
|
||||
// this.services = typeInferrer.getServices(this.trace);
|
||||
this.callResolver = new CallResolver(semanticServices, DataFlowInfo.getEmpty());
|
||||
this.callResolver = new CallResolver(semanticServices, DataFlowInfo.EMPTY);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallMaker;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallResolver;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
|
||||
@@ -176,7 +176,7 @@ public class BodyResolver {
|
||||
JetTypeReference typeReference = call.getTypeReference();
|
||||
if (typeReference != null) {
|
||||
if (descriptor.getUnsubstitutedPrimaryConstructor() != null) {
|
||||
JetType supertype = new CallResolver(context.getSemanticServices(), DataFlowInfo.getEmpty()).resolveCall(
|
||||
JetType supertype = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY).resolveCall(
|
||||
context.getTrace(), scopeForConstructor,
|
||||
CallMaker.makeCall(ReceiverDescriptor.NO_RECEIVER, null, call), NO_EXPECTED_TYPE);
|
||||
if (supertype != null) {
|
||||
@@ -322,7 +322,7 @@ public class BodyResolver {
|
||||
private void resolveSecondaryConstructorBody(JetConstructor declaration, final ConstructorDescriptor descriptor, final JetScope declaringScope) {
|
||||
final JetScope functionInnerScope = getInnerScopeForConstructor(descriptor, declaringScope, false);
|
||||
|
||||
final CallResolver callResolver = new CallResolver(context.getSemanticServices(), DataFlowInfo.getEmpty()); // TODO: dataFlowInfo
|
||||
final CallResolver callResolver = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY); // TODO: dataFlowInfo
|
||||
|
||||
JetClass containingClass = PsiTreeUtil.getParentOfType(declaration, JetClass.class);
|
||||
assert containingClass != null : "This must be guaranteed by the parser";
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetPsiUtil;
|
||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetThisExpression;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.JetTypeChecker;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.AUTOCAST_IMPOSSIBLE;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class AutoCastUtils {
|
||||
|
||||
private AutoCastUtils() {}
|
||||
|
||||
public static List<ReceiverDescriptor> getAutoCastVariants(@NotNull final BindingContext bindingContext, @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast) {
|
||||
return receiverToCast.accept(new ReceiverDescriptorVisitor<List<ReceiverDescriptor>, Object>() {
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitNoReceiver(ReceiverDescriptor noReceiver, Object data) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitTransientReceiver(TransientReceiver receiver, Object data) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitExtensionReceiver(ExtensionReceiver receiver, Object data) {
|
||||
return castThis(dataFlowInfo, receiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitClassReceiver(ClassReceiver receiver, Object data) {
|
||||
return castThis(dataFlowInfo, receiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitExpressionReceiver(ExpressionReceiver receiver, Object data) {
|
||||
JetExpression expression = receiver.getExpression();
|
||||
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(bindingContext, expression);
|
||||
if (variableDescriptor != null) {
|
||||
List<ReceiverDescriptor> result = Lists.newArrayList();
|
||||
for (JetType possibleType : dataFlowInfo.getPossibleTypesForVariable(variableDescriptor)) {
|
||||
result.add(new AutoCastReceiver(receiver, possibleType, isAutoCastable(variableDescriptor)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (expression instanceof JetThisExpression) {
|
||||
return castThis(dataFlowInfo, receiver);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
private static List<ReceiverDescriptor> castThis(@NotNull DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiver) {
|
||||
assert receiver.exists();
|
||||
List<ReceiverDescriptor> result = Lists.newArrayList();
|
||||
for (JetType possibleType : dataFlowInfo.getPossibleTypesForReceiver(receiver)) {
|
||||
result.add(new AutoCastReceiver(receiver, possibleType, true));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetType castExpression(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) {
|
||||
JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
|
||||
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(trace.getBindingContext(), expression);
|
||||
if (variableDescriptor != null) {
|
||||
List<JetType> possibleTypes = Lists.newArrayList(dataFlowInfo.getPossibleTypesForVariable(variableDescriptor));
|
||||
Collections.reverse(possibleTypes);
|
||||
for (JetType possibleType : possibleTypes) {
|
||||
if (typeChecker.isSubtypeOf(possibleType, expectedType)) {
|
||||
if (isAutoCastable(variableDescriptor)) {
|
||||
trace.record(AUTOCAST, expression, possibleType);
|
||||
}
|
||||
else {
|
||||
trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText()));
|
||||
}
|
||||
return possibleType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VariableDescriptor getVariableDescriptorFromSimpleName(@NotNull BindingContext bindingContext, @NotNull JetExpression expression) {
|
||||
JetExpression receiver = JetPsiUtil.deparenthesize(expression);
|
||||
VariableDescriptor variableDescriptor = null;
|
||||
if (receiver instanceof JetSimpleNameExpression) {
|
||||
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiver;
|
||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, nameExpression);
|
||||
if (declarationDescriptor instanceof VariableDescriptor) {
|
||||
variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
||||
}
|
||||
}
|
||||
return variableDescriptor;
|
||||
}
|
||||
|
||||
public static boolean isAutoCastable(@NotNull VariableDescriptor variableDescriptor) {
|
||||
if (variableDescriptor.isVar()) return false;
|
||||
if (variableDescriptor instanceof PropertyDescriptor) {
|
||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
|
||||
DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
|
||||
if (containingDeclaration instanceof ClassDescriptor) {
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
|
||||
if (classDescriptor.getModality().isOverridable() && propertyDescriptor.getModality().isOverridable()) return false;
|
||||
}
|
||||
else {
|
||||
assert !propertyDescriptor.getModality().isOverridable() : "Property outside a class must not be overridable";
|
||||
}
|
||||
PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
|
||||
if (getter == null || !getter.isDefault()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import org.jetbrains.jet.lang.JetSemanticServices;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
@@ -149,7 +151,7 @@ public class CallResolver {
|
||||
trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn));
|
||||
return checkArgumentTypesAndFail(trace, scope, call);
|
||||
}
|
||||
prioritizedTasks.add(new ResolutionTask<FunctionDescriptor>(TaskPrioritizer.convertWithImpliedThis(scope, Collections.<ReceiverDescriptor>singletonList(NO_RECEIVER), constructors), call, DataFlowInfo.getEmpty()));
|
||||
prioritizedTasks.add(new ResolutionTask<FunctionDescriptor>(TaskPrioritizer.convertWithImpliedThis(scope, Collections.<ReceiverDescriptor>singletonList(NO_RECEIVER), constructors), call, DataFlowInfo.EMPTY));
|
||||
}
|
||||
else {
|
||||
// trace.getErrorHandler().genericError(calleeExpression.getNode(), "Not a class");
|
||||
@@ -169,7 +171,7 @@ public class CallResolver {
|
||||
trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn));
|
||||
return checkArgumentTypesAndFail(trace, scope, call);
|
||||
}
|
||||
prioritizedTasks = Collections.singletonList(new ResolutionTask<FunctionDescriptor>(ResolvedCall.convertCollection(constructors), call, DataFlowInfo.getEmpty()));
|
||||
prioritizedTasks = Collections.singletonList(new ResolutionTask<FunctionDescriptor>(ResolvedCall.convertCollection(constructors), call, DataFlowInfo.EMPTY));
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("Type argument inference not implemented for " + call);
|
||||
@@ -301,7 +303,10 @@ public class CallResolver {
|
||||
JetBinaryExpression binaryExpression = (JetBinaryExpression) callElement;
|
||||
JetSimpleNameExpression operationReference = binaryExpression.getOperationReference();
|
||||
String operationString = operationReference.getReferencedNameElementType() == JetTokens.IDENTIFIER ? operationReference.getText() : OperatorConventions.getNameForOperationSymbol(operationReference.getReferencedNameElementType());
|
||||
trace.report(UNSAFE_INFIX_CALL.on(reference, binaryExpression.getLeft().getText(), operationString, binaryExpression.getRight().getText()));
|
||||
JetExpression right = binaryExpression.getRight();
|
||||
if (right != null) {
|
||||
trace.report(UNSAFE_INFIX_CALL.on(reference, binaryExpression.getLeft().getText(), operationString, right.getText()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace.report(UNSAFE_CALL.on(reference, type));
|
||||
@@ -618,7 +623,7 @@ public class CallResolver {
|
||||
// }
|
||||
// }
|
||||
// if (autoCastType != null) {
|
||||
// if (AutoCastUtils.isAutoCastable(variableDescriptor)) {
|
||||
// if (AutoCastUtils.isStableVariable(variableDescriptor)) {
|
||||
// temporaryTrace.record(AUTOCAST, argumentExpression, autoCastType);
|
||||
// }
|
||||
// else {
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||
import org.jetbrains.jet.util.CommonSuppliers;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class DataFlowInfo {
|
||||
|
||||
public static abstract class CompositionOperator {
|
||||
|
||||
public abstract DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b);
|
||||
}
|
||||
public static final CompositionOperator AND = new CompositionOperator() {
|
||||
@Override
|
||||
public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) {
|
||||
return a.and(b);
|
||||
}
|
||||
};
|
||||
|
||||
public static final CompositionOperator OR = new CompositionOperator() {
|
||||
@Override
|
||||
public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) {
|
||||
return a.or(b);
|
||||
}
|
||||
};
|
||||
|
||||
private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.<Object, NullabilityFlags>of(), Multimaps.newListMultimap(Collections.<Object, Collection<JetType>>emptyMap(), CommonSuppliers.<JetType>getArrayListSupplier()));
|
||||
|
||||
public static DataFlowInfo getEmpty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private final ImmutableMap<Object, NullabilityFlags> nullabilityInfo;
|
||||
|
||||
private final ListMultimap<Object, JetType> typeInfo;
|
||||
|
||||
private DataFlowInfo(ImmutableMap<Object, NullabilityFlags> nullabilityInfo, ListMultimap<Object, JetType> typeInfo) {
|
||||
this.nullabilityInfo = nullabilityInfo;
|
||||
this.typeInfo = typeInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JetType getOutType(@NotNull VariableDescriptor variableDescriptor) {
|
||||
JetType outType = variableDescriptor.getOutType();
|
||||
if (outType == null) return null;
|
||||
if (!outType.isNullable()) return outType;
|
||||
NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor);
|
||||
if (nullabilityFlags != null && !nullabilityFlags.canBeNull()) {
|
||||
return TypeUtils.makeNotNullable(outType);
|
||||
}
|
||||
return outType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<JetType> getPossibleTypes(Object key, @NotNull JetType originalType) {
|
||||
List<JetType> types = typeInfo.get(key);
|
||||
NullabilityFlags nullabilityFlags = nullabilityInfo.get(key);
|
||||
if (nullabilityFlags == null || nullabilityFlags.canBeNull()) {
|
||||
return types;
|
||||
}
|
||||
List<JetType> enrichedTypes = Lists.newArrayListWithCapacity(types.size());
|
||||
if (originalType.isNullable()) {
|
||||
enrichedTypes.add(TypeUtils.makeNotNullable(originalType));
|
||||
}
|
||||
for (JetType type: types) {
|
||||
if (type.isNullable()) {
|
||||
enrichedTypes.add(TypeUtils.makeNotNullable(type));
|
||||
}
|
||||
else {
|
||||
enrichedTypes.add(type);
|
||||
}
|
||||
}
|
||||
return enrichedTypes;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<JetType> getPossibleTypesForVariable(@NotNull VariableDescriptor variableDescriptor) {
|
||||
return getPossibleTypes(variableDescriptor, variableDescriptor.getOutType());
|
||||
}
|
||||
|
||||
public List<JetType> getPossibleTypesForReceiver(@NotNull ReceiverDescriptor receiver) {
|
||||
return getPossibleTypes(receiver, receiver.getType());
|
||||
}
|
||||
|
||||
public DataFlowInfo equalsToNull(@NotNull VariableDescriptor variableDescriptor, boolean notNull) {
|
||||
return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, notNull), typeInfo);
|
||||
}
|
||||
|
||||
private ImmutableMap<Object, NullabilityFlags> getEqualsToNullMap(VariableDescriptor variableDescriptor, boolean notNull) {
|
||||
Map<Object, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
|
||||
NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor);
|
||||
boolean varNotNull = notNull || (nullabilityFlags != null && !nullabilityFlags.canBeNull);
|
||||
builder.put(variableDescriptor, new NullabilityFlags(!varNotNull, varNotNull));
|
||||
return ImmutableMap.copyOf(builder);
|
||||
}
|
||||
|
||||
private ImmutableMap<Object, NullabilityFlags> getEqualsToNullMap(VariableDescriptor[] variableDescriptors, boolean notNull) {
|
||||
if (variableDescriptors.length == 0) return nullabilityInfo;
|
||||
Map<Object, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
|
||||
for (VariableDescriptor variableDescriptor : variableDescriptors) {
|
||||
if (variableDescriptor != null) {
|
||||
NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor);
|
||||
boolean varNotNull = notNull || (nullabilityFlags != null && !nullabilityFlags.canBeNull);
|
||||
builder.put(variableDescriptor, new NullabilityFlags(!varNotNull, varNotNull));
|
||||
}
|
||||
}
|
||||
return ImmutableMap.copyOf(builder);
|
||||
}
|
||||
|
||||
public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor variableDescriptor, @NotNull JetType type) {
|
||||
ListMultimap<Object, JetType> newTypeInfo = copyTypeInfo();
|
||||
newTypeInfo.put(variableDescriptor, type);
|
||||
return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, !type.isNullable()), newTypeInfo);
|
||||
}
|
||||
|
||||
public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor[] variableDescriptors, @NotNull JetType type) {
|
||||
if (variableDescriptors.length == 0) return this;
|
||||
ListMultimap<Object, JetType> newTypeInfo = copyTypeInfo();
|
||||
for (VariableDescriptor variableDescriptor : variableDescriptors) {
|
||||
if (variableDescriptor != null) {
|
||||
newTypeInfo.put(variableDescriptor, type);
|
||||
}
|
||||
}
|
||||
return new DataFlowInfo(getEqualsToNullMap(variableDescriptors, !type.isNullable()), newTypeInfo);
|
||||
}
|
||||
|
||||
public DataFlowInfo and(DataFlowInfo other) {
|
||||
Map<Object, NullabilityFlags> nullabilityMapBuilder = Maps.newHashMap();
|
||||
nullabilityMapBuilder.putAll(nullabilityInfo);
|
||||
for (Map.Entry<Object, NullabilityFlags> entry : other.nullabilityInfo.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
NullabilityFlags otherFlags = entry.getValue();
|
||||
NullabilityFlags thisFlags = nullabilityInfo.get(key);
|
||||
if (thisFlags != null) {
|
||||
nullabilityMapBuilder.put(key, thisFlags.and(otherFlags));
|
||||
}
|
||||
else {
|
||||
nullabilityMapBuilder.put(key, otherFlags);
|
||||
}
|
||||
}
|
||||
|
||||
ListMultimap<Object, JetType> newTypeInfo = copyTypeInfo();
|
||||
newTypeInfo.putAll(other.typeInfo);
|
||||
return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
|
||||
}
|
||||
|
||||
private ListMultimap<Object, JetType> copyTypeInfo() {
|
||||
ListMultimap<Object, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<Object, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
|
||||
newTypeInfo.putAll(typeInfo);
|
||||
return newTypeInfo;
|
||||
}
|
||||
|
||||
public DataFlowInfo or(DataFlowInfo other) {
|
||||
Map<Object, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
|
||||
builder.keySet().retainAll(other.nullabilityInfo.keySet());
|
||||
for (Map.Entry<Object, NullabilityFlags> entry : builder.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
NullabilityFlags thisFlags = entry.getValue();
|
||||
NullabilityFlags otherFlags = other.nullabilityInfo.get(key);
|
||||
assert (otherFlags != null);
|
||||
builder.put(key, thisFlags.or(otherFlags));
|
||||
}
|
||||
|
||||
ListMultimap<Object, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<Object, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
|
||||
|
||||
Set<Object> keys = newTypeInfo.keySet();
|
||||
keys.retainAll(other.typeInfo.keySet());
|
||||
|
||||
for (Object key : keys) {
|
||||
Collection<JetType> thisTypes = typeInfo.get(key);
|
||||
Collection<JetType> otherTypes = other.typeInfo.get(key);
|
||||
|
||||
Collection<JetType> newTypes = Sets.newHashSet(thisTypes);
|
||||
newTypes.retainAll(otherTypes);
|
||||
|
||||
newTypeInfo.putAll(key, newTypes);
|
||||
}
|
||||
|
||||
return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo);
|
||||
}
|
||||
|
||||
public DataFlowInfo nullabilityOnly() {
|
||||
return new DataFlowInfo(nullabilityInfo, EMPTY.copyTypeInfo());
|
||||
}
|
||||
|
||||
private static class NullabilityFlags {
|
||||
private final boolean canBeNull;
|
||||
private final boolean canBeNonNull;
|
||||
|
||||
private NullabilityFlags(boolean canBeNull, boolean canBeNonNull) {
|
||||
this.canBeNull = canBeNull;
|
||||
this.canBeNonNull = canBeNonNull;
|
||||
}
|
||||
|
||||
public boolean canBeNull() {
|
||||
return canBeNull;
|
||||
}
|
||||
|
||||
public boolean canBeNonNull() {
|
||||
return canBeNonNull;
|
||||
}
|
||||
|
||||
public NullabilityFlags and(NullabilityFlags other) {
|
||||
return new NullabilityFlags(this.canBeNull && other.canBeNull, this.canBeNonNull && other.canBeNonNull);
|
||||
}
|
||||
|
||||
public NullabilityFlags or(NullabilityFlags other) {
|
||||
return new NullabilityFlags(this.canBeNull || other.canBeNull, this.canBeNonNull || other.canBeNonNull);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.Call;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.Call;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastService;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls;
|
||||
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
@@ -13,7 +13,7 @@ public interface AutoCastService {
|
||||
AutoCastService NO_AUTO_CASTS = new AutoCastService() {
|
||||
@Override
|
||||
public DataFlowInfo getDataFlowInfo() {
|
||||
return DataFlowInfo.getEmpty();
|
||||
return DataFlowInfo.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls;
|
||||
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.calls.AutoCastReceiver;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class AutoCastUtils {
|
||||
|
||||
private AutoCastUtils() {}
|
||||
|
||||
public static List<ReceiverDescriptor> getAutoCastVariants(@NotNull final BindingContext bindingContext, @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast) {
|
||||
return receiverToCast.accept(new ReceiverDescriptorVisitor<List<ReceiverDescriptor>, Object>() {
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitNoReceiver(ReceiverDescriptor noReceiver, Object data) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitTransientReceiver(TransientReceiver receiver, Object data) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitExtensionReceiver(ExtensionReceiver receiver, Object data) {
|
||||
return castThis(dataFlowInfo, receiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitClassReceiver(ClassReceiver receiver, Object data) {
|
||||
return castThis(dataFlowInfo, receiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReceiverDescriptor> visitExpressionReceiver(ExpressionReceiver receiver, Object data) {
|
||||
// JetExpression expression = receiver.getExpression();
|
||||
// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(bindingContext, expression);
|
||||
// if (variableDescriptor != null) {
|
||||
// List<ReceiverDescriptor> result = Lists.newArrayList();
|
||||
// for (JetType possibleType : dataFlowInfo.getPossibleTypesForVariable(variableDescriptor)) {
|
||||
// result.add(new AutoCastReceiver(receiver, possibleType, DataFlowValueFactory.isStableVariable(variableDescriptor)));
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
// else if (expression instanceof JetThisExpression) {
|
||||
// return castThis(dataFlowInfo, receiver);
|
||||
// }
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(receiver.getExpression(),receiver.getType(), bindingContext);
|
||||
List<ReceiverDescriptor> result = Lists.newArrayList();
|
||||
for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
||||
result.add(new AutoCastReceiver(receiver, possibleType, dataFlowValue.isStableIdentifier()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
private static List<ReceiverDescriptor> castThis(@NotNull DataFlowInfo dataFlowInfo, @NotNull ThisReceiverDescriptor receiver) {
|
||||
assert receiver.exists();
|
||||
List<ReceiverDescriptor> result = Lists.newArrayList();
|
||||
for (JetType possibleType : dataFlowInfo.getPossibleTypes(DataFlowValueFactory.INSTANCE.createDataFlowValue(receiver))) {
|
||||
result.add(new AutoCastReceiver(receiver, possibleType, true));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// @Nullable
|
||||
// public static JetType castExpression(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) {
|
||||
// JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
|
||||
// DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, trace.getBindingContext());
|
||||
// for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
||||
// if (typeChecker.isSubtypeOf(possibleType, expectedType)) {
|
||||
// if (dataFlowValue.isStableIdentifier()) {
|
||||
// trace.record(AUTOCAST, expression, possibleType);
|
||||
// }
|
||||
// else {
|
||||
// trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText()));
|
||||
// }
|
||||
// return possibleType;
|
||||
// }
|
||||
// }
|
||||
//// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(trace.getBindingContext(), expression);
|
||||
//// if (variableDescriptor != null) {
|
||||
//// List<JetType> possibleTypes = Lists.newArrayList(dataFlowInfo.getPossibleTypes(variableDescriptor));
|
||||
//// Collections.reverse(possibleTypes);
|
||||
//// for (JetType possibleType : possibleTypes) {
|
||||
//// if (typeChecker.isSubtypeOf(possibleType, expectedType)) {
|
||||
//// if (DataFlowValueFactory.isStableVariable(variableDescriptor)) {
|
||||
//// trace.record(AUTOCAST, expression, possibleType);
|
||||
//// }
|
||||
//// else {
|
||||
//// trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText()));
|
||||
//// }
|
||||
//// return possibleType;
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
}
|
||||
+390
@@ -0,0 +1,390 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||
import org.jetbrains.jet.util.CommonSuppliers;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability.NOT_NULL;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
|
||||
public class DataFlowInfo {
|
||||
|
||||
public static abstract class CompositionOperator {
|
||||
public abstract DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b);
|
||||
}
|
||||
|
||||
public static final CompositionOperator AND = new CompositionOperator() {
|
||||
@Override
|
||||
public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) {
|
||||
return a.and(b);
|
||||
}
|
||||
};
|
||||
|
||||
public static final CompositionOperator OR = new CompositionOperator() {
|
||||
@Override
|
||||
public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) {
|
||||
return a.or(b);
|
||||
}
|
||||
};
|
||||
|
||||
public static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.<DataFlowValue, Nullability>of(), Multimaps.newListMultimap(Collections.<DataFlowValue, Collection<JetType>>emptyMap(), CommonSuppliers.<JetType>getArrayListSupplier()));
|
||||
|
||||
private final ImmutableMap<DataFlowValue, Nullability> nullabilityInfo;
|
||||
private final ListMultimap<DataFlowValue, JetType> typeInfo;
|
||||
|
||||
private DataFlowInfo(ImmutableMap<DataFlowValue, Nullability> nullabilityInfo, ListMultimap<DataFlowValue, JetType> typeInfo) {
|
||||
this.nullabilityInfo = nullabilityInfo;
|
||||
this.typeInfo = typeInfo;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Nullability getNullability(@NotNull DataFlowValue a) {
|
||||
if (!a.isStableIdentifier()) return a.getImmanentNullability();
|
||||
Nullability nullability = nullabilityInfo.get(a);
|
||||
if (nullability == null) {
|
||||
nullability = a.getImmanentNullability();
|
||||
}
|
||||
return nullability;
|
||||
}
|
||||
|
||||
private boolean putNullability(@NotNull Map<DataFlowValue, Nullability> map, @NotNull DataFlowValue value, @NotNull Nullability nullability) {
|
||||
if (!value.isStableIdentifier()) return false;
|
||||
return map.put(value, nullability) != nullability;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<JetType> getPossibleTypes(DataFlowValue key) {
|
||||
JetType originalType = key.getType();
|
||||
List<JetType> types = typeInfo.get(key);
|
||||
Nullability nullability = getNullability(key);
|
||||
if (nullability.canBeNull()) {
|
||||
return types;
|
||||
}
|
||||
List<JetType> enrichedTypes = Lists.newArrayListWithCapacity(types.size());
|
||||
if (originalType.isNullable()) {
|
||||
enrichedTypes.add(TypeUtils.makeNotNullable(originalType));
|
||||
}
|
||||
for (JetType type: types) {
|
||||
if (type.isNullable()) {
|
||||
enrichedTypes.add(TypeUtils.makeNotNullable(type));
|
||||
}
|
||||
else {
|
||||
enrichedTypes.add(type);
|
||||
}
|
||||
}
|
||||
return enrichedTypes;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) {
|
||||
Map<DataFlowValue, Nullability> builder = Maps.newHashMap(nullabilityInfo);
|
||||
Nullability nullabilityOfA = getNullability(a);
|
||||
Nullability nullabilityOfB = getNullability(b);
|
||||
|
||||
boolean changed = false;
|
||||
changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB));
|
||||
changed |= putNullability(builder, b, nullabilityOfA.refine(nullabilityOfA));
|
||||
return changed ? new DataFlowInfo(ImmutableMap.copyOf(builder), typeInfo) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataFlowInfo disequate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) {
|
||||
Map<DataFlowValue, Nullability> builder = Maps.newHashMap(nullabilityInfo);
|
||||
Nullability nullabilityOfA = getNullability(a);
|
||||
Nullability nullabilityOfB = getNullability(b);
|
||||
|
||||
boolean changed = false;
|
||||
changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert()));
|
||||
changed |= putNullability(builder, b, nullabilityOfA.refine(nullabilityOfA.invert()));
|
||||
return changed ? new DataFlowInfo(ImmutableMap.copyOf(builder), typeInfo) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataFlowInfo establishSubtyping(@NotNull DataFlowValue[] values, @NotNull JetType type) {
|
||||
ListMultimap<DataFlowValue, JetType> newTypeInfo = copyTypeInfo();
|
||||
Map<DataFlowValue, Nullability> newNullabilityInfo = Maps.newHashMap(nullabilityInfo);
|
||||
boolean changed = false;
|
||||
for (DataFlowValue value : values) {
|
||||
// if (!value.isStableIdentifier()) continue;
|
||||
changed = true;
|
||||
newTypeInfo.put(value, type);
|
||||
if (!type.isNullable()) {
|
||||
putNullability(newNullabilityInfo, value, NOT_NULL);
|
||||
}
|
||||
}
|
||||
if (!changed) return this;
|
||||
return new DataFlowInfo(ImmutableMap.copyOf(newNullabilityInfo), newTypeInfo);
|
||||
}
|
||||
|
||||
private ListMultimap<DataFlowValue, JetType> copyTypeInfo() {
|
||||
ListMultimap<DataFlowValue, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<DataFlowValue, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
|
||||
newTypeInfo.putAll(typeInfo);
|
||||
return newTypeInfo;
|
||||
}
|
||||
|
||||
public DataFlowInfo and(DataFlowInfo other) {
|
||||
Map<DataFlowValue, Nullability> nullabilityMapBuilder = Maps.newHashMap();
|
||||
nullabilityMapBuilder.putAll(nullabilityInfo);
|
||||
for (Map.Entry<DataFlowValue, Nullability> entry : other.nullabilityInfo.entrySet()) {
|
||||
DataFlowValue key = entry.getKey();
|
||||
Nullability otherFlags = entry.getValue();
|
||||
Nullability thisFlags = nullabilityInfo.get(key);
|
||||
if (thisFlags != null) {
|
||||
nullabilityMapBuilder.put(key, thisFlags.and(otherFlags));
|
||||
}
|
||||
else {
|
||||
nullabilityMapBuilder.put(key, otherFlags);
|
||||
}
|
||||
}
|
||||
|
||||
ListMultimap<DataFlowValue, JetType> newTypeInfo = copyTypeInfo();
|
||||
newTypeInfo.putAll(other.typeInfo);
|
||||
return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
|
||||
}
|
||||
|
||||
public DataFlowInfo or(DataFlowInfo other) {
|
||||
Map<DataFlowValue, Nullability> builder = Maps.newHashMap(nullabilityInfo);
|
||||
builder.keySet().retainAll(other.nullabilityInfo.keySet());
|
||||
for (Map.Entry<DataFlowValue, Nullability> entry : builder.entrySet()) {
|
||||
DataFlowValue key = entry.getKey();
|
||||
Nullability thisFlags = entry.getValue();
|
||||
Nullability otherFlags = other.nullabilityInfo.get(key);
|
||||
assert (otherFlags != null);
|
||||
builder.put(key, thisFlags.or(otherFlags));
|
||||
}
|
||||
|
||||
ListMultimap<DataFlowValue, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<DataFlowValue, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
|
||||
|
||||
Set<DataFlowValue> keys = newTypeInfo.keySet();
|
||||
keys.retainAll(other.typeInfo.keySet());
|
||||
|
||||
for (DataFlowValue key : keys) {
|
||||
Collection<JetType> thisTypes = typeInfo.get(key);
|
||||
Collection<JetType> otherTypes = other.typeInfo.get(key);
|
||||
|
||||
Collection<JetType> newTypes = Sets.newHashSet(thisTypes);
|
||||
newTypes.retainAll(otherTypes);
|
||||
|
||||
newTypeInfo.putAll(key, newTypes);
|
||||
}
|
||||
|
||||
return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//public class DataFlowInfo {
|
||||
//
|
||||
// public static abstract class CompositionOperator {
|
||||
//
|
||||
// public abstract DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b);
|
||||
// }
|
||||
// public static final CompositionOperator AND = new CompositionOperator() {
|
||||
// @Override
|
||||
// public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) {
|
||||
// return a.and(b);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// public static final CompositionOperator OR = new CompositionOperator() {
|
||||
// @Override
|
||||
// public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) {
|
||||
// return a.or(b);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.<Object, NullabilityFlags>of(), Multimaps.newListMultimap(Collections.<Object, Collection<JetType>>emptyMap(), CommonSuppliers.<JetType>getArrayListSupplier()));
|
||||
//
|
||||
// public static DataFlowInfo getEmpty() {
|
||||
// return EMPTY;
|
||||
// }
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// private final ImmutableMap<Object, NullabilityFlags> nullabilityInfo;
|
||||
//
|
||||
// private final ListMultimap<Object, JetType> typeInfo;
|
||||
//
|
||||
// private DataFlowInfo(ImmutableMap<Object, NullabilityFlags> nullabilityInfo, ListMultimap<Object, JetType> typeInfo) {
|
||||
// this.nullabilityInfo = nullabilityInfo;
|
||||
// this.typeInfo = typeInfo;
|
||||
// }
|
||||
//
|
||||
// @Nullable
|
||||
// public JetType getOutType(@NotNull VariableDescriptor variableDescriptor) {
|
||||
// JetType outType = variableDescriptor.getOutType();
|
||||
// if (outType == null) return null;
|
||||
// if (!outType.isNullable()) return outType;
|
||||
// NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor);
|
||||
// if (nullabilityFlags != null && !nullabilityFlags.canBeNull()) {
|
||||
// return TypeUtils.makeNotNullable(outType);
|
||||
// }
|
||||
// return outType;
|
||||
// }
|
||||
//
|
||||
// @NotNull
|
||||
// private List<JetType> getPossibleTypes(Object key, @NotNull JetType originalType) {
|
||||
// List<JetType> types = typeInfo.get(key);
|
||||
// NullabilityFlags nullabilityFlags = nullabilityInfo.get(key);
|
||||
// if (nullabilityFlags == null || nullabilityFlags.canBeNull()) {
|
||||
// return types;
|
||||
// }
|
||||
// List<JetType> enrichedTypes = Lists.newArrayListWithCapacity(types.size());
|
||||
// if (originalType.isNullable()) {
|
||||
// enrichedTypes.add(TypeUtils.makeNotNullable(originalType));
|
||||
// }
|
||||
// for (JetType type: types) {
|
||||
// if (type.isNullable()) {
|
||||
// enrichedTypes.add(TypeUtils.makeNotNullable(type));
|
||||
// }
|
||||
// else {
|
||||
// enrichedTypes.add(type);
|
||||
// }
|
||||
// }
|
||||
// return enrichedTypes;
|
||||
// }
|
||||
//
|
||||
// @NotNull
|
||||
// public List<JetType> getPossibleTypesForVariable(@NotNull VariableDescriptor variableDescriptor) {
|
||||
// return getPossibleTypes(variableDescriptor, variableDescriptor.getOutType());
|
||||
// }
|
||||
//
|
||||
// public List<JetType> getPossibleTypesForReceiver(@NotNull ReceiverDescriptor receiver) {
|
||||
// return getPossibleTypes(receiver, receiver.getType());
|
||||
// }
|
||||
//
|
||||
// public DataFlowInfo establishEqualityToNull(@NotNull VariableDescriptor variableDescriptor, boolean notNull) {
|
||||
// return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, notNull), typeInfo);
|
||||
// }
|
||||
//
|
||||
// private ImmutableMap<Object, NullabilityFlags> getEqualsToNullMap(VariableDescriptor variableDescriptor, boolean notNull) {
|
||||
// Map<Object, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
|
||||
// NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor);
|
||||
// boolean varNotNull = notNull || (nullabilityFlags != null && !nullabilityFlags.canBeNull);
|
||||
// builder.put(variableDescriptor, new NullabilityFlags(!varNotNull, varNotNull));
|
||||
// return ImmutableMap.copyOf(builder);
|
||||
// }
|
||||
//
|
||||
// private ImmutableMap<Object, NullabilityFlags> getEqualsToNullMap(VariableDescriptor[] variableDescriptors, boolean notNull) {
|
||||
// if (variableDescriptors.length == 0) return nullabilityInfo;
|
||||
// Map<Object, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
|
||||
// for (VariableDescriptor variableDescriptor : variableDescriptors) {
|
||||
// if (variableDescriptor != null) {
|
||||
// NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor);
|
||||
// boolean varNotNull = notNull || (nullabilityFlags != null && !nullabilityFlags.canBeNull);
|
||||
// builder.put(variableDescriptor, new NullabilityFlags(!varNotNull, varNotNull));
|
||||
// }
|
||||
// }
|
||||
// return ImmutableMap.copyOf(builder);
|
||||
// }
|
||||
//
|
||||
// public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor variableDescriptor, @NotNull JetType type) {
|
||||
// ListMultimap<Object, JetType> newTypeInfo = copyTypeInfo();
|
||||
// newTypeInfo.put(variableDescriptor, type);
|
||||
// return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, !type.isNullable()), newTypeInfo);
|
||||
// }
|
||||
//
|
||||
// public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor[] variableDescriptors, @NotNull JetType type) {
|
||||
// if (variableDescriptors.length == 0) return this;
|
||||
// ListMultimap<Object, JetType> newTypeInfo = copyTypeInfo();
|
||||
// for (VariableDescriptor variableDescriptor : variableDescriptors) {
|
||||
// if (variableDescriptor != null) {
|
||||
// newTypeInfo.put(variableDescriptor, type);
|
||||
// }
|
||||
// }
|
||||
// return new DataFlowInfo(getEqualsToNullMap(variableDescriptors, !type.isNullable()), newTypeInfo);
|
||||
// }
|
||||
//
|
||||
// public DataFlowInfo and(DataFlowInfo other) {
|
||||
// Map<Object, NullabilityFlags> nullabilityMapBuilder = Maps.newHashMap();
|
||||
// nullabilityMapBuilder.putAll(nullabilityInfo);
|
||||
// for (Map.Entry<Object, NullabilityFlags> entry : other.nullabilityInfo.entrySet()) {
|
||||
// Object key = entry.getKey();
|
||||
// NullabilityFlags otherFlags = entry.getValue();
|
||||
// NullabilityFlags thisFlags = nullabilityInfo.get(key);
|
||||
// if (thisFlags != null) {
|
||||
// nullabilityMapBuilder.put(key, thisFlags.and(otherFlags));
|
||||
// }
|
||||
// else {
|
||||
// nullabilityMapBuilder.put(key, otherFlags);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ListMultimap<Object, JetType> newTypeInfo = copyTypeInfo();
|
||||
// newTypeInfo.putAll(other.typeInfo);
|
||||
// return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
|
||||
// }
|
||||
//
|
||||
// private ListMultimap<Object, JetType> copyTypeInfo() {
|
||||
// ListMultimap<Object, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<Object, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
|
||||
// newTypeInfo.putAll(typeInfo);
|
||||
// return newTypeInfo;
|
||||
// }
|
||||
//
|
||||
// public DataFlowInfo or(DataFlowInfo other) {
|
||||
// Map<Object, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
|
||||
// builder.keySet().retainAll(other.nullabilityInfo.keySet());
|
||||
// for (Map.Entry<Object, NullabilityFlags> entry : builder.entrySet()) {
|
||||
// Object key = entry.getKey();
|
||||
// NullabilityFlags thisFlags = entry.getValue();
|
||||
// NullabilityFlags otherFlags = other.nullabilityInfo.get(key);
|
||||
// assert (otherFlags != null);
|
||||
// builder.put(key, thisFlags.or(otherFlags));
|
||||
// }
|
||||
//
|
||||
// ListMultimap<Object, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<Object, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
|
||||
//
|
||||
// Set<Object> keys = newTypeInfo.keySet();
|
||||
// keys.retainAll(other.typeInfo.keySet());
|
||||
//
|
||||
// for (Object key : keys) {
|
||||
// Collection<JetType> thisTypes = typeInfo.get(key);
|
||||
// Collection<JetType> otherTypes = other.typeInfo.get(key);
|
||||
//
|
||||
// Collection<JetType> newTypes = Sets.newHashSet(thisTypes);
|
||||
// newTypes.retainAll(otherTypes);
|
||||
//
|
||||
// newTypeInfo.putAll(key, newTypes);
|
||||
// }
|
||||
//
|
||||
// return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo);
|
||||
// }
|
||||
//
|
||||
// public DataFlowInfo nullabilityOnly() {
|
||||
// return new DataFlowInfo(nullabilityInfo, EMPTY.copyTypeInfo());
|
||||
// }
|
||||
//
|
||||
// private static class NullabilityFlags {
|
||||
// private final boolean canBeNull;
|
||||
// private final boolean canBeNonNull;
|
||||
//
|
||||
// private NullabilityFlags(boolean canBeNull, boolean canBeNonNull) {
|
||||
// this.canBeNull = canBeNull;
|
||||
// this.canBeNonNull = canBeNonNull;
|
||||
// }
|
||||
//
|
||||
// public boolean canBeNull() {
|
||||
// return canBeNull;
|
||||
// }
|
||||
//
|
||||
// public boolean canBeNonNull() {
|
||||
// return canBeNonNull;
|
||||
// }
|
||||
//
|
||||
// public NullabilityFlags and(NullabilityFlags other) {
|
||||
// return new NullabilityFlags(this.canBeNull && other.canBeNull, this.canBeNonNull && other.canBeNonNull);
|
||||
// }
|
||||
//
|
||||
// public NullabilityFlags or(NullabilityFlags other) {
|
||||
// return new NullabilityFlags(this.canBeNull || other.canBeNull, this.canBeNonNull || other.canBeNonNull);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class DataFlowValue {
|
||||
|
||||
public static final DataFlowValue NULL = new DataFlowValue(new Object(), JetStandardClasses.getNullableNothingType(), false, Nullability.NULL);
|
||||
public static final DataFlowValue NULLABLE = new DataFlowValue(new Object(), JetStandardClasses.getNullableAnyType(), false, Nullability.UNKNOWN);
|
||||
|
||||
private final boolean stableIdentifier;
|
||||
private final JetType type;
|
||||
private final Object id;
|
||||
private final Nullability immanentNullability;
|
||||
|
||||
// Use DataFlowValueFactory
|
||||
/*package*/ DataFlowValue(Object id, JetType type, boolean stableIdentifier, Nullability immanentNullability) {
|
||||
this.stableIdentifier = stableIdentifier;
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.immanentNullability = immanentNullability;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Nullability getImmanentNullability() {
|
||||
return immanentNullability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stable identifier is a non-literal value that is statically known to be immutable
|
||||
* @return
|
||||
*/
|
||||
public boolean isStableIdentifier() {
|
||||
return stableIdentifier;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JetType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
DataFlowValue that = (DataFlowValue) o;
|
||||
|
||||
if (stableIdentifier != that.stableIdentifier) 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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (stableIdentifier ? "stable " : "unstable ") + (id == null ? null : id.toString()) + " " + immanentNullability;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (stableIdentifier ? 1 : 0);
|
||||
result = 31 * result + (type != null ? type.hashCode() : 0);
|
||||
result = 31 * result + (id != null ? id.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.JetNodeTypes;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.JetModuleUtil;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class DataFlowValueFactory {
|
||||
public static final DataFlowValueFactory INSTANCE = new DataFlowValueFactory();
|
||||
|
||||
private DataFlowValueFactory() {}
|
||||
|
||||
@NotNull
|
||||
public DataFlowValue createDataFlowValue(@NotNull JetExpression expression, @NotNull JetType type, @NotNull BindingContext bindingContext) {
|
||||
if (expression instanceof JetConstantExpression) {
|
||||
JetConstantExpression constantExpression = (JetConstantExpression) expression;
|
||||
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
|
||||
}
|
||||
Pair<Object, Boolean> result = getIdForStableIdentifier(expression, bindingContext, false);
|
||||
return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataFlowValue createDataFlowValue(@NotNull ThisReceiverDescriptor receiver) {
|
||||
JetType type = receiver.getType();
|
||||
return new DataFlowValue(receiver.getDeclarationDescriptor(), type, true, getImmanentNullability(type));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataFlowValue createDataFlowValue(@NotNull VariableDescriptor variableDescriptor) {
|
||||
JetType type = variableDescriptor.getOutType();
|
||||
return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor), getImmanentNullability(type));
|
||||
}
|
||||
|
||||
// private Object getId(@NotNull JetExpression expression, @NotNull BindingContext bindingContext) {
|
||||
// if (expression instanceof JetThisExpression) {
|
||||
// JetThisExpression thisExpression = (JetThisExpression) expression;
|
||||
// DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getThisReference());
|
||||
// if (declarationDescriptor instanceof CallableDescriptor) {
|
||||
// return ((CallableDescriptor) declarationDescriptor).getReceiverParameter();
|
||||
// }
|
||||
// if (declarationDescriptor instanceof ClassDescriptor) {
|
||||
// return ((ClassDescriptor) declarationDescriptor).getImplicitReceiver();
|
||||
// }
|
||||
//// throw new AssertionError("No resolution data for expression " + expression.getText());
|
||||
// }
|
||||
// else if (expression instanceof JetReferenceExpression) {
|
||||
// JetReferenceExpression referenceExpression = (JetReferenceExpression) expression;
|
||||
// DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, referenceExpression);
|
||||
// if (declarationDescriptor != null) {
|
||||
// return declarationDescriptor;
|
||||
// }
|
||||
//// throw new AssertionError("No resolution data for expression " + expression.getText() + DiagnosticUtils.atLocation(expression));
|
||||
// }
|
||||
// return expression;
|
||||
// }
|
||||
|
||||
private Nullability getImmanentNullability(JetType type) {
|
||||
return type.isNullable() ? Nullability.UNKNOWN : Nullability.NOT_NULL;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Pair<Object, Boolean> getIdForStableIdentifier(@NotNull JetExpression expression, @NotNull BindingContext bindingContext, boolean allowNamespaces) {
|
||||
if (expression instanceof JetParenthesizedExpression) {
|
||||
JetParenthesizedExpression parenthesizedExpression = (JetParenthesizedExpression) expression;
|
||||
JetExpression innerExpression = parenthesizedExpression.getExpression();
|
||||
if (innerExpression == null) {
|
||||
return Pair.create(null, false);
|
||||
}
|
||||
return getIdForStableIdentifier(innerExpression, bindingContext, allowNamespaces);
|
||||
}
|
||||
else if (expression instanceof JetQualifiedExpression) {
|
||||
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
|
||||
JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
|
||||
if (selectorExpression == null) {
|
||||
return Pair.create(null, false);
|
||||
}
|
||||
Pair<Object, Boolean> receiverId = getIdForStableIdentifier(qualifiedExpression.getReceiverExpression(), bindingContext, true);
|
||||
Pair<Object, Boolean> selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, allowNamespaces);
|
||||
return receiverId.second ? selectorId : Pair.create(receiverId.first, false);
|
||||
}
|
||||
if (expression instanceof JetSimpleNameExpression) {
|
||||
JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) expression;
|
||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
|
||||
if (declarationDescriptor instanceof VariableDescriptor) {
|
||||
return Pair.create((Object) declarationDescriptor, isStableVariable((VariableDescriptor) declarationDescriptor));
|
||||
}
|
||||
if (declarationDescriptor instanceof NamespaceDescriptor) {
|
||||
return Pair.create((Object) declarationDescriptor, allowNamespaces);
|
||||
}
|
||||
if (declarationDescriptor instanceof ClassDescriptor) {
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
|
||||
return Pair.create((Object) classDescriptor, classDescriptor.isClassObjectAValue());
|
||||
}
|
||||
}
|
||||
else if (expression instanceof JetThisExpression) {
|
||||
JetThisExpression thisExpression = (JetThisExpression) expression;
|
||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getThisReference());
|
||||
if (declarationDescriptor instanceof CallableDescriptor) {
|
||||
return Pair.create((Object) ((CallableDescriptor) declarationDescriptor).getReceiverParameter(), true);
|
||||
}
|
||||
if (declarationDescriptor instanceof ClassDescriptor) {
|
||||
return Pair.create((Object) ((ClassDescriptor) declarationDescriptor).getImplicitReceiver(), true);
|
||||
}
|
||||
return Pair.create(null, true);
|
||||
}
|
||||
else if (expression instanceof JetRootNamespaceExpression) {
|
||||
return Pair.create((Object) JetModuleUtil.getRootNamespaceType(expression), allowNamespaces);
|
||||
}
|
||||
return Pair.create(null, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VariableDescriptor getVariableDescriptorFromSimpleName(@NotNull BindingContext bindingContext, @NotNull JetExpression expression) {
|
||||
JetExpression receiver = JetPsiUtil.deparenthesize(expression);
|
||||
VariableDescriptor variableDescriptor = null;
|
||||
if (receiver instanceof JetSimpleNameExpression) {
|
||||
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiver;
|
||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, nameExpression);
|
||||
if (declarationDescriptor instanceof VariableDescriptor) {
|
||||
variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
||||
}
|
||||
}
|
||||
return variableDescriptor;
|
||||
}
|
||||
|
||||
public static boolean isStableVariable(@NotNull VariableDescriptor variableDescriptor) {
|
||||
if (variableDescriptor.isVar()) return false;
|
||||
if (variableDescriptor instanceof PropertyDescriptor) {
|
||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
|
||||
DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
|
||||
if (containingDeclaration instanceof ClassDescriptor) {
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
|
||||
if (classDescriptor.getModality().isOverridable() && propertyDescriptor.getModality().isOverridable()) return false;
|
||||
}
|
||||
else {
|
||||
assert !propertyDescriptor.getModality().isOverridable() : "Property outside a class must not be overridable";
|
||||
}
|
||||
// TODO: check that it's internal
|
||||
PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
|
||||
if (getter != null && !getter.isDefault()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public enum Nullability {
|
||||
NULL(true, false),
|
||||
NOT_NULL(false, true),
|
||||
UNKNOWN(true, true),
|
||||
IMPOSSIBLE(false, false);
|
||||
|
||||
@NotNull
|
||||
public static Nullability fromFlags(boolean canBeNull, boolean canBeNonNull) {
|
||||
if (!canBeNull && !canBeNonNull) return IMPOSSIBLE;
|
||||
if (!canBeNull && canBeNonNull) return NOT_NULL;
|
||||
if (canBeNull && !canBeNonNull) return NULL;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
private final boolean canBeNull;
|
||||
private final boolean canBeNonNull;
|
||||
|
||||
Nullability(boolean canBeNull, boolean canBeNonNull) {
|
||||
this.canBeNull = canBeNull;
|
||||
this.canBeNonNull = canBeNonNull;
|
||||
}
|
||||
|
||||
public boolean canBeNull() {
|
||||
return canBeNull;
|
||||
}
|
||||
|
||||
public boolean canBeNonNull() {
|
||||
return canBeNonNull;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Nullability refine(@NotNull Nullability other) {
|
||||
switch (this) {
|
||||
case UNKNOWN:
|
||||
return other;
|
||||
case IMPOSSIBLE:
|
||||
return other;
|
||||
case NULL:
|
||||
switch (other) {
|
||||
case NOT_NULL: return NOT_NULL;
|
||||
default: return NULL;
|
||||
}
|
||||
case NOT_NULL:
|
||||
switch (other) {
|
||||
case NULL: return NOT_NULL;
|
||||
default: return NOT_NULL;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Nullability invert() {
|
||||
switch (this) {
|
||||
case NULL:
|
||||
return NOT_NULL;
|
||||
case NOT_NULL:
|
||||
return UNKNOWN;
|
||||
case UNKNOWN:
|
||||
return UNKNOWN;
|
||||
case IMPOSSIBLE:
|
||||
return UNKNOWN;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Nullability and(@NotNull Nullability other) {
|
||||
return fromFlags(this.canBeNull && other.canBeNull, this.canBeNonNull && other.canBeNonNull);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Nullability or(@NotNull Nullability other) {
|
||||
return fromFlags(this.canBeNull || other.canBeNull, this.canBeNonNull || other.canBeNonNull);
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -2,12 +2,13 @@ package org.jetbrains.jet.lang.resolve.scopes.receivers;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class ClassReceiver implements ReceiverDescriptor {
|
||||
public class ClassReceiver implements ThisReceiverDescriptor {
|
||||
|
||||
private final ClassDescriptor classDescriptor;
|
||||
|
||||
@@ -26,6 +27,12 @@ public class ClassReceiver implements ReceiverDescriptor {
|
||||
return classDescriptor.getDefaultType();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DeclarationDescriptor getDeclarationDescriptor() {
|
||||
return classDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, D> R accept(@NotNull ReceiverDescriptorVisitor<R, D> visitor, D data) {
|
||||
return visitor.visitClassReceiver(this, data);
|
||||
|
||||
+12
-2
@@ -2,15 +2,25 @@ package org.jetbrains.jet.lang.resolve.scopes.receivers;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class ExtensionReceiver extends ImplicitReceiverDescriptor {
|
||||
public class ExtensionReceiver extends AbstractReceiverDescriptor implements ThisReceiverDescriptor {
|
||||
|
||||
private final CallableDescriptor descriptor;
|
||||
|
||||
public ExtensionReceiver(@NotNull CallableDescriptor callableDescriptor, @NotNull JetType receiverType) {
|
||||
super(callableDescriptor, receiverType);
|
||||
super(receiverType);
|
||||
this.descriptor = callableDescriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DeclarationDescriptor getDeclarationDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package org.jetbrains.jet.lang.resolve.scopes.receivers;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
/**
|
||||
* Describes a "this" receiver
|
||||
*
|
||||
* @author abreslav
|
||||
*/
|
||||
public abstract class ImplicitReceiverDescriptor extends AbstractReceiverDescriptor {
|
||||
private final DeclarationDescriptor declarationDescriptor;
|
||||
|
||||
protected ImplicitReceiverDescriptor(@NotNull DeclarationDescriptor declarationDescriptor, @NotNull JetType receiverType) {
|
||||
super(receiverType);
|
||||
this.declarationDescriptor = declarationDescriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DeclarationDescriptor getDeclarationDescriptor() {
|
||||
return declarationDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.jetbrains.jet.lang.resolve.scopes.receivers;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
|
||||
/**
|
||||
* Describes a "this" receiver
|
||||
*
|
||||
* @author abreslav
|
||||
*/
|
||||
public interface ThisReceiverDescriptor extends ReceiverDescriptor {
|
||||
@NotNull
|
||||
DeclarationDescriptor getDeclarationDescriptor();
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallMaker;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
|
||||
import org.jetbrains.jet.lang.resolve.constants.*;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver;
|
||||
import org.jetbrains.jet.lang.resolve.TypeResolver;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallMaker;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallResolver;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
|
||||
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantResolver;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.psi.JetElement;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
|
||||
+6
-6
@@ -14,7 +14,7 @@ import org.jetbrains.jet.lang.diagnostics.Diagnostic;
|
||||
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
|
||||
@@ -56,7 +56,7 @@ public class ExpressionTypingServices {
|
||||
ExpressionTypingContext context = ExpressionTypingContext.newContext(
|
||||
semanticServices,
|
||||
new HashMap<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), new LabelResolver(),
|
||||
trace, scope, DataFlowInfo.getEmpty(), expectedType, FORBIDDEN, false
|
||||
trace, scope, DataFlowInfo.EMPTY, expectedType, FORBIDDEN, false
|
||||
);
|
||||
return expressionTypingFacade.getType(expression, context);
|
||||
}
|
||||
@@ -65,14 +65,14 @@ public class ExpressionTypingServices {
|
||||
ExpressionTypingContext context = ExpressionTypingContext.newContext(
|
||||
semanticServices,
|
||||
new HashMap<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), new LabelResolver(),
|
||||
trace, scope, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, FORBIDDEN,
|
||||
trace, scope, DataFlowInfo.EMPTY, NO_EXPECTED_TYPE, FORBIDDEN,
|
||||
true);
|
||||
return expressionTypingFacade.getType(expression, context);
|
||||
// return ((ExpressionTypingContext) ExpressionTyperVisitorWithNamespaces).INSTANCE.getType(expression, ExpressionTypingContext.newRootContext(semanticServices, trace, scope, DataFlowInfo.getEmpty(), TypeUtils.NO_EXPECTED_TYPE, TypeUtils.NO_EXPECTED_TYPE));
|
||||
}
|
||||
|
||||
public void checkFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor) {
|
||||
checkFunctionReturnType(outerScope, function, functionDescriptor, DataFlowInfo.getEmpty());
|
||||
checkFunctionReturnType(outerScope, function, functionDescriptor, DataFlowInfo.EMPTY);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -87,7 +87,7 @@ public class ExpressionTypingServices {
|
||||
public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType) {
|
||||
checkFunctionReturnType(function, ExpressionTypingContext.newContext(
|
||||
semanticServices, new HashMap<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), new LabelResolver(),
|
||||
trace, functionInnerScope, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, expectedReturnType, false
|
||||
trace, functionInnerScope, DataFlowInfo.EMPTY, NO_EXPECTED_TYPE, expectedReturnType, false
|
||||
));
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ public class ExpressionTypingServices {
|
||||
assert bodyExpression != null;
|
||||
JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace);
|
||||
expressionTypingFacade.getType(bodyExpression, ExpressionTypingContext.newContext(semanticServices, new HashMap<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), new LabelResolver(),
|
||||
trace, functionInnerScope, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, FORBIDDEN, false));
|
||||
trace, functionInnerScope, DataFlowInfo.EMPTY, NO_EXPECTED_TYPE, FORBIDDEN, false));
|
||||
//todo function literals
|
||||
final Collection<JetExpression> returnedExpressions = Lists.newArrayList();
|
||||
if (function.hasBlockBody()) {
|
||||
|
||||
+102
-63
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.jet.lang.types.expressions;
|
||||
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -12,20 +13,22 @@ import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler;
|
||||
import org.jetbrains.jet.lang.resolve.calls.AutoCastUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
||||
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.JetTypeChecker;
|
||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.RESULT_TYPE_MISMATCH;
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.TYPE_MISMATCH;
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.MUST_BE_WRAPPED_IN_A_REF;
|
||||
|
||||
/**
|
||||
@@ -100,13 +103,13 @@ public class ExpressionTypingUtils {
|
||||
@NotNull
|
||||
public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, @Nullable final WritableScope scopeToExtend, final ExpressionTypingContext context) {
|
||||
if (condition == null) return context.dataFlowInfo;
|
||||
final DataFlowInfo[] result = new DataFlowInfo[] {context.dataFlowInfo};
|
||||
final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(context.dataFlowInfo);
|
||||
condition.accept(new JetVisitorVoid() {
|
||||
@Override
|
||||
public void visitIsExpression(JetIsExpression expression) {
|
||||
if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) {
|
||||
JetPattern pattern = expression.getPattern();
|
||||
result[0] = context.patternsToDataFlowInfo.get(pattern);
|
||||
result.set(context.patternsToDataFlowInfo.get(pattern));
|
||||
if (scopeToExtend != null) {
|
||||
List<VariableDescriptor> descriptors = context.patternsToBoundVariableLists.get(pattern);
|
||||
if (descriptors != null) {
|
||||
@@ -143,68 +146,94 @@ public class ExpressionTypingUtils {
|
||||
}
|
||||
dataFlowInfo = operator.compose(dataFlowInfo, rightInfo);
|
||||
}
|
||||
result[0] = dataFlowInfo;
|
||||
result.set(dataFlowInfo);
|
||||
}
|
||||
else if (operationToken == JetTokens.EQEQ
|
||||
|| operationToken == JetTokens.EXCLEQ
|
||||
|| operationToken == JetTokens.EQEQEQ
|
||||
|| operationToken == JetTokens.EXCLEQEQEQ) {
|
||||
else {
|
||||
JetExpression left = expression.getLeft();
|
||||
JetExpression right = expression.getRight();
|
||||
if (right == null) return;
|
||||
|
||||
if (!(left instanceof JetSimpleNameExpression)) {
|
||||
JetExpression tmp = left;
|
||||
left = right;
|
||||
right = tmp;
|
||||
|
||||
if (!(left instanceof JetSimpleNameExpression)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VariableDescriptor variableDescriptor = AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), left);
|
||||
if (variableDescriptor == null) return;
|
||||
|
||||
// TODO : validate that DF makes sense for this variable: local, val, internal w/backing field, etc
|
||||
|
||||
// Comparison to a non-null expression
|
||||
JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
|
||||
if (lhsType == null) return;
|
||||
JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
|
||||
if (rhsType != null && !rhsType.isNullable()) {
|
||||
extendDataFlowWithNullComparison(operationToken, variableDescriptor, !conditionValue);
|
||||
return;
|
||||
}
|
||||
if (rhsType == null) return;
|
||||
|
||||
VariableDescriptor rightVariable = AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), right);
|
||||
if (rightVariable != null) {
|
||||
JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
|
||||
if (lhsType != null && !lhsType.isNullable()) {
|
||||
extendDataFlowWithNullComparison(operationToken, rightVariable, !conditionValue);
|
||||
return;
|
||||
BindingContext bindingContext = context.trace.getBindingContext();
|
||||
DataFlowValue leftValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, lhsType, bindingContext);
|
||||
DataFlowValue rightValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(right, rhsType, bindingContext);
|
||||
|
||||
Boolean equals = null;
|
||||
if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
||||
equals = true;
|
||||
}
|
||||
else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
|
||||
equals = false;
|
||||
}
|
||||
if (equals != null) {
|
||||
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
|
||||
result.set(context.dataFlowInfo.equate(leftValue, rightValue));
|
||||
}
|
||||
else {
|
||||
result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
|
||||
}
|
||||
}
|
||||
|
||||
// Comparison to 'null'
|
||||
if (!(right instanceof JetConstantExpression)) {
|
||||
return;
|
||||
}
|
||||
JetConstantExpression constantExpression = (JetConstantExpression) right;
|
||||
if (constantExpression.getNode().getElementType() != JetNodeTypes.NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
extendDataFlowWithNullComparison(operationToken, variableDescriptor, conditionValue);
|
||||
// if (!(left instanceof JetSimpleNameExpression)) {
|
||||
// JetExpression tmp = left;
|
||||
// left = right;
|
||||
// right = tmp;
|
||||
//
|
||||
// if (!(left instanceof JetSimpleNameExpression)) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), left);
|
||||
// if (variableDescriptor == null) return;
|
||||
//
|
||||
// // TODO : validate that DF makes sense for this variable: local, val, internal w/backing field, etc
|
||||
//
|
||||
// // Comparison to a non-null expression
|
||||
// JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
|
||||
// if (rhsType != null && !rhsType.isNullable()) {
|
||||
// extendDataFlowWithNullComparison(operationToken, variableDescriptor, !conditionValue);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// VariableDescriptor rightVariable = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), right);
|
||||
// if (rightVariable != null) {
|
||||
// JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left);
|
||||
// if (lhsType != null && !lhsType.isNullable()) {
|
||||
// extendDataFlowWithNullComparison(operationToken, rightVariable, !conditionValue);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Comparison to 'null'
|
||||
// if (!(right instanceof JetConstantExpression)) {
|
||||
// return;
|
||||
// }
|
||||
// JetConstantExpression constantExpression = (JetConstantExpression) right;
|
||||
// if (constantExpression.getNode().getElementType() != JetNodeTypes.NULL) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// extendDataFlowWithNullComparison(operationToken, variableDescriptor, conditionValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void extendDataFlowWithNullComparison(IElementType operationToken, @NotNull VariableDescriptor variableDescriptor, boolean equalsToNull) {
|
||||
if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
||||
result[0] = context.dataFlowInfo.equalsToNull(variableDescriptor, !equalsToNull);
|
||||
}
|
||||
else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
|
||||
result[0] = context.dataFlowInfo.equalsToNull(variableDescriptor, equalsToNull);
|
||||
}
|
||||
}
|
||||
// private void extendDataFlowWithNullComparison(IElementType operationToken, @NotNull VariableDescriptor variableDescriptor, boolean equalsToNull) {
|
||||
// boolean equality;
|
||||
// if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
||||
//// result[0] = context.dataFlowInfo.establishEqualityToNull(variableDescriptor, !equalsToNull);
|
||||
// equality = true;
|
||||
// }
|
||||
// else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) {
|
||||
//// result[0] = context.dataFlowInfo.establishEqualityToNull(variableDescriptor, equalsToNull);
|
||||
// equality = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void visitUnaryExpression(JetUnaryExpression expression) {
|
||||
@@ -212,7 +241,7 @@ public class ExpressionTypingUtils {
|
||||
if (operationTokenType == JetTokens.EXCL) {
|
||||
JetExpression baseExpression = expression.getBaseExpression();
|
||||
if (baseExpression != null) {
|
||||
result[0] = extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend, context);
|
||||
result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,10 +254,10 @@ public class ExpressionTypingUtils {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (result[0] == null) {
|
||||
if (result.get() == null) {
|
||||
return context.dataFlowInfo;
|
||||
}
|
||||
return result[0];
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public static boolean isTypeFlexible(@Nullable JetExpression expression) {
|
||||
@@ -246,17 +275,27 @@ public class ExpressionTypingUtils {
|
||||
context.semanticServices.getTypeChecker().isSubtypeOf(expressionType, context.expectedType)) {
|
||||
return expressionType;
|
||||
}
|
||||
if (AutoCastUtils.castExpression(expression, context.expectedType, context.dataFlowInfo, context.trace) == null) {
|
||||
context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType));
|
||||
return expressionType;
|
||||
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, expressionType, context.trace.getBindingContext());
|
||||
for (JetType possibleType : context.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
||||
if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, context.expectedType)) {
|
||||
if (dataFlowValue.isStableIdentifier()) {
|
||||
context.trace.record(AUTOCAST, expression, possibleType);
|
||||
}
|
||||
else {
|
||||
context.trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText()));
|
||||
}
|
||||
return possibleType;
|
||||
}
|
||||
}
|
||||
return context.expectedType;
|
||||
context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType));
|
||||
return expressionType;
|
||||
}
|
||||
|
||||
public static void checkWrappingInRef(JetExpression expression, ExpressionTypingContext context) {
|
||||
if (!(expression instanceof JetSimpleNameExpression)) return;
|
||||
JetSimpleNameExpression simpleName = (JetSimpleNameExpression) expression;
|
||||
VariableDescriptor variable = AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), simpleName);
|
||||
VariableDescriptor variable = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), simpleName);
|
||||
if (variable != null) {
|
||||
DeclarationDescriptor containingDeclaration = variable.getContainingDeclaration();
|
||||
if (context.scope.getContainingDeclaration() != containingDeclaration && containingDeclaration instanceof CallableDescriptor) {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.DeferredType;
|
||||
|
||||
+20
-17
@@ -2,12 +2,14 @@ package org.jetbrains.jet.lang.types.expressions;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.AutoCastUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
||||
@@ -19,8 +21,6 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.INCOMPATIBLE_TYPES;
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.UNSUPPORTED;
|
||||
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.ensureBooleanResultWithCustomSubject;
|
||||
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.newWritableScopeImpl;
|
||||
|
||||
@@ -35,11 +35,12 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
@Override
|
||||
public JetType visitIsExpression(JetIsExpression expression, ExpressionTypingContext contextWithExpectedType) {
|
||||
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE);
|
||||
JetType knownType = facade.safeGetType(expression.getLeftHandSide(), context.replaceScope(context.scope));
|
||||
JetExpression leftHandSide = expression.getLeftHandSide();
|
||||
JetType knownType = facade.safeGetType(leftHandSide, context.replaceScope(context.scope));
|
||||
JetPattern pattern = expression.getPattern();
|
||||
if (pattern != null) {
|
||||
WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in 'is'");
|
||||
DataFlowInfo newDataFlowInfo = checkPatternType(pattern, knownType, scopeToExtend, context, AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), expression.getLeftHandSide()));
|
||||
DataFlowInfo newDataFlowInfo = checkPatternType(pattern, knownType, scopeToExtend, context, DataFlowValueFactory.INSTANCE.createDataFlowValue(leftHandSide, knownType, context.trace.getBindingContext()));
|
||||
context.patternsToDataFlowInfo.put(pattern, newDataFlowInfo);
|
||||
context.patternsToBoundVariableLists.put(pattern, scopeToExtend.getDeclaredVariables());
|
||||
}
|
||||
@@ -53,7 +54,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
final JetExpression subjectExpression = expression.getSubjectExpression();
|
||||
|
||||
final JetType subjectType = subjectExpression != null ? context.getServices().safeGetType(context.scope, subjectExpression, TypeUtils.NO_EXPECTED_TYPE) : ErrorUtils.createErrorType("Unknown type");
|
||||
final VariableDescriptor variableDescriptor = subjectExpression != null ? AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), subjectExpression) : null;
|
||||
final DataFlowValue variableDescriptor = subjectExpression != null ? DataFlowValueFactory.INSTANCE.createDataFlowValue(subjectExpression, subjectType, context.trace.getBindingContext()) : null;
|
||||
|
||||
// TODO : exhaustive patterns
|
||||
|
||||
@@ -108,7 +109,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private DataFlowInfo checkWhenCondition(@Nullable final JetExpression subjectExpression, final JetType subjectType, JetWhenCondition condition, final WritableScope scopeToExtend, final ExpressionTypingContext context, final VariableDescriptor... subjectVariables) {
|
||||
private DataFlowInfo checkWhenCondition(@Nullable final JetExpression subjectExpression, final JetType subjectType, JetWhenCondition condition, final WritableScope scopeToExtend, final ExpressionTypingContext context, final DataFlowValue... subjectVariables) {
|
||||
final DataFlowInfo[] newDataFlowInfo = new DataFlowInfo[]{context.dataFlowInfo};
|
||||
condition.accept(new JetVisitorVoid() {
|
||||
|
||||
@@ -151,8 +152,8 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
return newDataFlowInfo[0];
|
||||
}
|
||||
|
||||
private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, @NotNull final WritableScope scopeToExtend, final ExpressionTypingContext context, @NotNull final VariableDescriptor... subjectVariables) {
|
||||
final DataFlowInfo[] result = new DataFlowInfo[] {context.dataFlowInfo};
|
||||
private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, @NotNull final WritableScope scopeToExtend, final ExpressionTypingContext context, @NotNull final DataFlowValue... subjectVariables) {
|
||||
final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(context.dataFlowInfo);
|
||||
pattern.accept(new JetVisitorVoid() {
|
||||
@Override
|
||||
public void visitTypePattern(JetTypePattern typePattern) {
|
||||
@@ -160,7 +161,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
if (typeReference != null) {
|
||||
JetType type = context.getTypeResolver().resolveType(context.scope, typeReference);
|
||||
checkTypeCompatibility(type, subjectType, typePattern);
|
||||
result[0] = context.dataFlowInfo.isInstanceOf(subjectVariables, type);
|
||||
result.set(context.dataFlowInfo.establishSubtyping(subjectVariables, type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +188,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
|
||||
JetPattern entryPattern = entry.getPattern();
|
||||
if (entryPattern != null) {
|
||||
result[0] = result[0].and(checkPatternType(entryPattern, type, scopeToExtend, context));
|
||||
result.set(result.get().and(checkPatternType(entryPattern, type, scopeToExtend, context)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,7 +201,9 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
ReceiverDescriptor receiver = new TransientReceiver(subjectType);
|
||||
JetType selectorReturnType = facade.getSelectorReturnType(receiver, null, decomposerExpression, context);
|
||||
|
||||
result[0] = checkPatternType(pattern.getArgumentList(), selectorReturnType == null ? ErrorUtils.createErrorType("No type") : selectorReturnType, scopeToExtend, context);
|
||||
result.set(checkPatternType(pattern.getArgumentList(), selectorReturnType == null
|
||||
? ErrorUtils.createErrorType("No type")
|
||||
: selectorReturnType, scopeToExtend, context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,10 +238,10 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
JetWhenCondition condition = pattern.getCondition();
|
||||
if (condition != null) {
|
||||
int oldLength = subjectVariables.length;
|
||||
VariableDescriptor[] newSubjectVariables = new VariableDescriptor[oldLength + 1];
|
||||
DataFlowValue[] newSubjectVariables = new DataFlowValue[oldLength + 1];
|
||||
System.arraycopy(subjectVariables, 0, newSubjectVariables, 0, oldLength);
|
||||
newSubjectVariables[oldLength] = variableDescriptor;
|
||||
result[0] = checkWhenCondition(null, subjectType, condition, scopeToExtend, context, newSubjectVariables);
|
||||
newSubjectVariables[oldLength] = DataFlowValueFactory.INSTANCE.createDataFlowValue(variableDescriptor);
|
||||
result.set(checkWhenCondition(null, subjectType, condition, scopeToExtend, context, newSubjectVariables));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +262,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName()));
|
||||
}
|
||||
});
|
||||
return result[0];
|
||||
return result.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -189,11 +189,11 @@ fun tuples(a: Any?) {
|
||||
val s: (Any, String) = (<info descr="Automatically cast to Any">a</info>, <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but String was expected">a</error>)
|
||||
}
|
||||
if (a is String) {
|
||||
val s: (Any, String) = (<info descr="Automatically cast to String">a</info>, <info descr="Automatically cast to String">a</info>)
|
||||
val s: (Any, String) = (<info descr="Automatically cast to Any">a</info>, <info descr="Automatically cast to String">a</info>)
|
||||
}
|
||||
fun illegalTupleReturnType(): (Any, String) = (<error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but Any was expected">a</error>, <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but String was expected">a</error>)
|
||||
if (a is String) {
|
||||
fun legalTupleReturnType(): (Any, String) = (<info descr="Automatically cast to String">a</info>, <info descr="Automatically cast to String">a</info>)
|
||||
fun legalTupleReturnType(): (Any, String) = (<info descr="Automatically cast to Any">a</info>, <info descr="Automatically cast to String">a</info>)
|
||||
}
|
||||
val illegalFunctionLiteral: Function0<Int> = <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Function0<Any?> but Function0<Int> was expected">{ <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but Int was expected">a</error> }</error>
|
||||
val illegalReturnValueInFunctionLiteral: Function0<Int> = { (): Int => <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but Int was expected">a</error> }
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
namespace example;
|
||||
|
||||
namespace ns {
|
||||
val y : Any? = 2
|
||||
}
|
||||
|
||||
object Obj {
|
||||
val y : Any? = 2
|
||||
}
|
||||
|
||||
class AClass() {
|
||||
class object {
|
||||
val y : Any? = 2
|
||||
}
|
||||
}
|
||||
|
||||
val x : Any? = 1
|
||||
|
||||
fun Any?.vars(a: Any?) : Int {
|
||||
var b: Int = 0
|
||||
if (ns.y is Int) {
|
||||
b = ns.y
|
||||
}
|
||||
if (ns.y is Int) {
|
||||
b = example.ns.y
|
||||
}
|
||||
if (example.ns.y is Int) {
|
||||
b = ns.y
|
||||
}
|
||||
if (example.ns.y is Int) {
|
||||
b = example.ns.y
|
||||
}
|
||||
// if (namespace.bottles.ns.y is Int) {
|
||||
// b = ns.y
|
||||
// }
|
||||
if (Obj.y is Int) {
|
||||
b = Obj.y
|
||||
}
|
||||
if (example.Obj.y is Int) {
|
||||
b = Obj.y
|
||||
}
|
||||
if (AClass.y is Int) {
|
||||
b = AClass.y
|
||||
}
|
||||
if (example.AClass.y is Int) {
|
||||
b = AClass.y
|
||||
}
|
||||
if (x is Int) {
|
||||
b = x
|
||||
}
|
||||
if (example.x is Int) {
|
||||
b = x
|
||||
}
|
||||
if (example.x is Int) {
|
||||
b = example.x
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
fun Any?.foo() : Int {
|
||||
if (this is Int)
|
||||
return this
|
||||
if (this@foo is Int)
|
||||
return this
|
||||
if (this@foo is Int)
|
||||
return this@foo
|
||||
if (this is Int)
|
||||
return this@foo
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
trait T {}
|
||||
|
||||
open class C {
|
||||
fun foo() {
|
||||
var t : T? = null
|
||||
if (this is T) {
|
||||
t = this
|
||||
}
|
||||
if (this is T) {
|
||||
t = this@C
|
||||
}
|
||||
if (this@C is T) {
|
||||
t = this
|
||||
}
|
||||
if (this@C is T) {
|
||||
t = this@C
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallResolver;
|
||||
import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
@@ -111,7 +111,7 @@ public class JetResolveTest extends ExtensibleResolveTestCase {
|
||||
List<JetType> parameterTypeList = Arrays.asList(parameterType);
|
||||
// JetTypeInferrer.Services typeInferrerServices = JetSemanticServices.createSemanticServices(getProject()).getTypeInferrerServices(new BindingTraceContext());
|
||||
|
||||
CallResolver callResolver = new CallResolver(JetSemanticServices.createSemanticServices(getProject()), DataFlowInfo.getEmpty());
|
||||
CallResolver callResolver = new CallResolver(JetSemanticServices.createSemanticServices(getProject()), DataFlowInfo.EMPTY);
|
||||
OverloadResolutionResults<FunctionDescriptor> functions = callResolver.resolveExactSignature(
|
||||
classDescriptor.getMemberScope(typeArguments), ReceiverDescriptor.NO_RECEIVER, name, parameterTypeList);
|
||||
for (ResolvedCall<FunctionDescriptor> resolvedCall : functions.getResults()) {
|
||||
|
||||
Reference in New Issue
Block a user