Merge pull request #81 from udalov/kt2164

KT-2164 & KT-2216
This commit is contained in:
Svetlana Isakova
2012-06-19 06:02:21 -07:00
13 changed files with 197 additions and 69 deletions
@@ -68,6 +68,7 @@ public interface BindingContext {
WritableSlice<JetExpression, CompileTimeConstant<?>> COMPILE_TIME_VALUE = Slices.createSimpleSlice(); WritableSlice<JetExpression, CompileTimeConstant<?>> COMPILE_TIME_VALUE = Slices.createSimpleSlice();
WritableSlice<JetTypeReference, JetType> TYPE = Slices.createSimpleSlice(); WritableSlice<JetTypeReference, JetType> TYPE = Slices.createSimpleSlice();
WritableSlice<JetExpression, JetType> EXPRESSION_TYPE = new BasicWritableSlice<JetExpression, JetType>(DO_NOTHING); WritableSlice<JetExpression, JetType> EXPRESSION_TYPE = new BasicWritableSlice<JetExpression, JetType>(DO_NOTHING);
WritableSlice<JetExpression, DataFlowInfo> EXPRESSION_DATA_FLOW_INFO = new BasicWritableSlice<JetExpression, DataFlowInfo>(DO_NOTHING);
WritableSlice<JetReferenceExpression, DeclarationDescriptor> REFERENCE_TARGET = new BasicWritableSlice<JetReferenceExpression, DeclarationDescriptor>(DO_NOTHING); WritableSlice<JetReferenceExpression, DeclarationDescriptor> REFERENCE_TARGET = new BasicWritableSlice<JetReferenceExpression, DeclarationDescriptor>(DO_NOTHING);
WritableSlice<JetElement, ResolvedCall<? extends CallableDescriptor>> RESOLVED_CALL = new BasicWritableSlice<JetElement, ResolvedCall<? extends CallableDescriptor>>(DO_NOTHING); WritableSlice<JetElement, ResolvedCall<? extends CallableDescriptor>> RESOLVED_CALL = new BasicWritableSlice<JetElement, ResolvedCall<? extends CallableDescriptor>>(DO_NOTHING);
@@ -767,6 +767,7 @@ public class CallResolver {
private <D extends CallableDescriptor, F extends D> ResolutionStatus checkValueArgumentTypes(CallResolutionContext<D, F> context) { private <D extends CallableDescriptor, F extends D> ResolutionStatus checkValueArgumentTypes(CallResolutionContext<D, F> context) {
ResolutionStatus result = SUCCESS; ResolutionStatus result = SUCCESS;
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : context.candidateCall.getValueArguments().entrySet()) { for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : context.candidateCall.getValueArguments().entrySet()) {
ValueParameterDescriptor parameterDescriptor = entry.getKey(); ValueParameterDescriptor parameterDescriptor = entry.getKey();
ResolvedValueArgument resolvedArgument = entry.getValue(); ResolvedValueArgument resolvedArgument = entry.getValue();
@@ -777,7 +778,9 @@ public class CallResolver {
if (expression == null) continue; if (expression == null) continue;
JetType expectedType = getEffectiveExpectedType(parameterDescriptor, argument); JetType expectedType = getEffectiveExpectedType(parameterDescriptor, argument);
JetType type = expressionTypingServices.getType(context.scope, expression, expectedType, context.dataFlowInfo, context.candidateCall.getTrace()); JetTypeInfo typeInfo = expressionTypingServices.getTypeInfo(context.scope, expression, expectedType, dataFlowInfo, context.candidateCall.getTrace());
JetType type = typeInfo.getType();
dataFlowInfo = dataFlowInfo.and(typeInfo.getDataFlowInfo());
if (type == null || ErrorUtils.isErrorType(type)) { if (type == null || ErrorUtils.isErrorType(type)) {
context.candidateCall.argumentHasNoType(); context.candidateCall.argumentHasNoType();
} }
@@ -65,7 +65,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements ResolvedC
private final Map<TypeParameterDescriptor, JetType> typeArguments = Maps.newLinkedHashMap(); private final Map<TypeParameterDescriptor, JetType> typeArguments = Maps.newLinkedHashMap();
private final Map<ValueParameterDescriptor, JetType> autoCasts = Maps.newHashMap(); private final Map<ValueParameterDescriptor, JetType> autoCasts = Maps.newHashMap();
private final Map<ValueParameterDescriptor, ResolvedValueArgument> valueArguments = Maps.newHashMap(); private final Map<ValueParameterDescriptor, ResolvedValueArgument> valueArguments = Maps.newLinkedHashMap();
private boolean someArgumentHasNoType = false; private boolean someArgumentHasNoType = false;
private TemporaryBindingTrace trace; private TemporaryBindingTrace trace;
private ResolutionStatus status = UNKNOWN_STATUS; private ResolutionStatus status = UNKNOWN_STATUS;
@@ -33,6 +33,7 @@ import org.jetbrains.jet.lang.resolve.calls.CallMaker;
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults; import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResultsUtil; import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResultsUtil;
import org.jetbrains.jet.lang.resolve.calls.autocasts.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.calls.autocasts.DataFlowValueFactory;
import org.jetbrains.jet.lang.resolve.constants.*; import org.jetbrains.jet.lang.resolve.constants.*;
import org.jetbrains.jet.lang.resolve.constants.StringValue; import org.jetbrains.jet.lang.resolve.constants.StringValue;
@@ -72,9 +73,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
public JetTypeInfo visitSimpleNameExpression(JetSimpleNameExpression expression, ExpressionTypingContext context) { public JetTypeInfo visitSimpleNameExpression(JetSimpleNameExpression expression, ExpressionTypingContext context) {
// TODO : other members // TODO : other members
// TODO : type substitutions??? // TODO : type substitutions???
JetType type = DataFlowUtils.checkType(getSelectorReturnType(NO_RECEIVER, null, expression, context), expression, context); JetTypeInfo typeInfo = getSelectorReturnTypeInfo(NO_RECEIVER, null, expression, context);
JetType type = DataFlowUtils.checkType(typeInfo.getType(), expression, context);
ExpressionTypingUtils.checkWrappingInRef(expression, context); ExpressionTypingUtils.checkWrappingInRef(expression, context);
return JetTypeInfo.create(type, context.dataFlowInfo); // TODO : Extensions to this return JetTypeInfo.create(type, typeInfo.getDataFlowInfo()); // TODO : Extensions to this
} }
@Nullable @Nullable
@@ -192,37 +194,41 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
public JetTypeInfo visitBinaryWithTypeRHSExpression(JetBinaryExpressionWithTypeRHS expression, ExpressionTypingContext context) { public JetTypeInfo visitBinaryWithTypeRHSExpression(JetBinaryExpressionWithTypeRHS expression, ExpressionTypingContext context) {
JetTypeReference right = expression.getRight(); JetTypeReference right = expression.getRight();
JetType result = null; JetType result = null;
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
if (right != null) { if (right != null) {
JetType targetType = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, right, context.trace, true); JetType targetType = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, right, context.trace, true);
boolean tryWithNoExpectedType = true;
if (isTypeFlexible(expression.getLeft()) || expression.getOperationSign().getReferencedNameElementType() == JetTokens.COLON) { if (isTypeFlexible(expression.getLeft()) || expression.getOperationSign().getReferencedNameElementType() == JetTokens.COLON) {
TemporaryBindingTrace temporaryTraceWithExpectedType = TemporaryBindingTrace.create(context.trace); TemporaryBindingTrace temporaryTraceWithExpectedType = TemporaryBindingTrace.create(context.trace);
boolean success = checkBinaryWithTypeRHS(expression, context.replaceBindingTrace(temporaryTraceWithExpectedType).replaceExpectedType(targetType), targetType); ExpressionTypingContext contextWithTemporaryTrace = context.replaceBindingTrace(temporaryTraceWithExpectedType).replaceExpectedType(targetType);
if (success) { JetTypeInfo typeInfo = facade.getTypeInfo(expression.getLeft(), contextWithTemporaryTrace);
if (typeInfo.getType() != null && checkBinaryWithTypeRHS(expression, contextWithTemporaryTrace, targetType, typeInfo.getType())) {
temporaryTraceWithExpectedType.commit(); temporaryTraceWithExpectedType.commit();
} dataFlowInfo = typeInfo.getDataFlowInfo();
else { tryWithNoExpectedType = false;
checkBinaryWithTypeRHS(expression, context.replaceExpectedType(NO_EXPECTED_TYPE), targetType);
} }
} }
else {
checkBinaryWithTypeRHS(expression, context.replaceExpectedType(NO_EXPECTED_TYPE), targetType); if (tryWithNoExpectedType) {
ExpressionTypingContext contextWithNoExpectedType = context.replaceExpectedType(NO_EXPECTED_TYPE);
JetTypeInfo typeInfo = facade.getTypeInfo(expression.getLeft(), contextWithNoExpectedType);
if (typeInfo.getType() != null) {
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, typeInfo.getType());
dataFlowInfo = typeInfo.getDataFlowInfo();
}
} }
IElementType operationType = expression.getOperationSign().getReferencedNameElementType(); IElementType operationType = expression.getOperationSign().getReferencedNameElementType();
result = operationType == JetTokens.AS_SAFE ? TypeUtils.makeNullable(targetType) : targetType; result = operationType == JetTokens.AS_SAFE ? TypeUtils.makeNullable(targetType) : targetType;
} }
else { else {
facade.getTypeInfo(expression.getLeft(), context.replaceExpectedType(NO_EXPECTED_TYPE)); dataFlowInfo = facade.getTypeInfo(expression.getLeft(), context.replaceExpectedType(NO_EXPECTED_TYPE)).getDataFlowInfo();
} }
return DataFlowUtils.checkType(result, expression, context, context.dataFlowInfo); return DataFlowUtils.checkType(result, expression, context, dataFlowInfo);
} }
private boolean checkBinaryWithTypeRHS(JetBinaryExpressionWithTypeRHS expression, ExpressionTypingContext context, @NotNull JetType targetType) { private boolean checkBinaryWithTypeRHS(JetBinaryExpressionWithTypeRHS expression, ExpressionTypingContext context, @NotNull JetType targetType, JetType actualType) {
JetType actualType = facade.getTypeInfo(expression.getLeft(), context).getType();
if (actualType == null) return false;
JetSimpleNameExpression operationSign = expression.getOperationSign(); JetSimpleNameExpression operationSign = expression.getOperationSign();
IElementType operationType = operationSign.getReferencedNameElementType(); IElementType operationType = operationSign.getReferencedNameElementType();
if (operationType == JetTokens.COLON) { if (operationType == JetTokens.COLON) {
@@ -537,11 +543,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
if (selectorExpression == null) return JetTypeInfo.create(null, context.dataFlowInfo); if (selectorExpression == null) return JetTypeInfo.create(null, context.dataFlowInfo);
if (receiverType == null) receiverType = ErrorUtils.createErrorType("Type for " + expression.getText()); if (receiverType == null) receiverType = ErrorUtils.createErrorType("Type for " + expression.getText());
context = context.replaceDataFlowInfo(receiverTypeInfo.getDataFlowInfo());
if (selectorExpression instanceof JetSimpleNameExpression) { if (selectorExpression instanceof JetSimpleNameExpression) {
propagateConstantValues(expression, context, (JetSimpleNameExpression) selectorExpression); propagateConstantValues(expression, context, (JetSimpleNameExpression) selectorExpression);
} }
JetType selectorReturnType = getSelectorReturnType(new ExpressionReceiver(receiverExpression, receiverType), expression.getOperationTokenNode(), selectorExpression, context); JetTypeInfo selectorReturnTypeInfo = getSelectorReturnTypeInfo(new ExpressionReceiver(receiverExpression, receiverType), expression.getOperationTokenNode(), selectorExpression, context);
JetType selectorReturnType = selectorReturnTypeInfo.getType();
//TODO move further //TODO move further
if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) { if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
@@ -552,12 +561,11 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
} }
} }
JetType result = selectorReturnType;
// TODO : this is suspicious: remove this code? // TODO : this is suspicious: remove this code?
if (result != null) { if (selectorReturnType != null) {
context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, result); context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, selectorReturnType);
} }
return DataFlowUtils.checkType(result, expression, context, receiverTypeInfo.getDataFlowInfo()); return DataFlowUtils.checkType(selectorReturnType, expression, context, selectorReturnTypeInfo.getDataFlowInfo());
} }
private void propagateConstantValues(JetQualifiedExpression expression, ExpressionTypingContext context, JetSimpleNameExpression selectorExpression) { private void propagateConstantValues(JetQualifiedExpression expression, ExpressionTypingContext context, JetSimpleNameExpression selectorExpression) {
@@ -639,34 +647,33 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return null; return null;
} }
@Nullable @NotNull
public JetType getSelectorReturnType(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull ExpressionTypingContext context) { public JetTypeInfo getSelectorReturnTypeInfo(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull ExpressionTypingContext context) {
if (selectorExpression instanceof JetCallExpression) { if (selectorExpression instanceof JetCallExpression) {
return getCallExpressionTypeInfo((JetCallExpression) selectorExpression, receiver, callOperationNode, context);
return getCallExpressionType((JetCallExpression) selectorExpression, receiver, callOperationNode, context);
} }
else if (selectorExpression instanceof JetSimpleNameExpression) { else if (selectorExpression instanceof JetSimpleNameExpression) {
return getSimpleNameExpressionTypeInfo((JetSimpleNameExpression) selectorExpression, receiver, callOperationNode, context);
return getSimpleNameExpressionType((JetSimpleNameExpression) selectorExpression, receiver, callOperationNode, context);
} }
else if (selectorExpression instanceof JetQualifiedExpression) { else if (selectorExpression instanceof JetQualifiedExpression) {
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression; JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression;
JetExpression newReceiverExpression = qualifiedExpression.getReceiverExpression(); JetExpression newReceiverExpression = qualifiedExpression.getReceiverExpression();
JetType newReceiverType = getSelectorReturnType(receiver, callOperationNode, newReceiverExpression, context.replaceExpectedType(NO_EXPECTED_TYPE)); JetTypeInfo newReceiverTypeInfo = getSelectorReturnTypeInfo(receiver, callOperationNode, newReceiverExpression, context.replaceExpectedType(NO_EXPECTED_TYPE));
JetType newReceiverType = newReceiverTypeInfo.getType();
DataFlowInfo newReceiverDataFlowInfo = newReceiverTypeInfo.getDataFlowInfo();
JetExpression newSelectorExpression = qualifiedExpression.getSelectorExpression(); JetExpression newSelectorExpression = qualifiedExpression.getSelectorExpression();
if (newReceiverType != null && newSelectorExpression != null) { if (newReceiverType != null && newSelectorExpression != null) {
return getSelectorReturnType(new ExpressionReceiver(newReceiverExpression, newReceiverType), qualifiedExpression.getOperationTokenNode(), newSelectorExpression, context); return getSelectorReturnTypeInfo(new ExpressionReceiver(newReceiverExpression, newReceiverType), qualifiedExpression.getOperationTokenNode(), newSelectorExpression, context.replaceDataFlowInfo(newReceiverDataFlowInfo));
} }
} }
else { else {
context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression, selectorExpression.getText())); context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression, selectorExpression.getText()));
} }
return null; return JetTypeInfo.create(null, context.dataFlowInfo);
} }
@Nullable @NotNull
private JetType getSimpleNameExpressionType(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverDescriptor receiver, private JetTypeInfo getSimpleNameExpressionTypeInfo(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverDescriptor receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context) { @Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context) {
boolean[] result = new boolean[1]; boolean[] result = new boolean[1];
@@ -675,7 +682,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
JetType type = getVariableType(nameExpression, receiver, callOperationNode, context.replaceBindingTrace(traceForVariable), result); JetType type = getVariableType(nameExpression, receiver, callOperationNode, context.replaceBindingTrace(traceForVariable), result);
if (result[0]) { if (result[0]) {
traceForVariable.commit(); traceForVariable.commit();
return type; return JetTypeInfo.create(type, context.dataFlowInfo);
} }
Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList()); Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList());
@@ -685,15 +692,16 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
traceForFunction.commit(); traceForFunction.commit();
boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0; boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0;
context.trace.report(FUNCTION_CALL_EXPECTED.on(nameExpression, nameExpression, hasValueParameters)); context.trace.report(FUNCTION_CALL_EXPECTED.on(nameExpression, nameExpression, hasValueParameters));
return functionDescriptor != null ? functionDescriptor.getReturnType() : null; type = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
return JetTypeInfo.create(type, context.dataFlowInfo);
} }
traceForVariable.commit(); traceForVariable.commit();
return null; return JetTypeInfo.create(null, context.dataFlowInfo);
} }
@Nullable @NotNull
private JetType getCallExpressionType(@NotNull JetCallExpression callExpression, @NotNull ReceiverDescriptor receiver, private JetTypeInfo getCallExpressionTypeInfo(@NotNull JetCallExpression callExpression, @NotNull ReceiverDescriptor receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context) { @Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context) {
boolean[] result = new boolean[1]; boolean[] result = new boolean[1];
@@ -709,7 +717,22 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0; boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0;
context.trace.report(FUNCTION_CALL_EXPECTED.on(callExpression, callExpression, hasValueParameters)); context.trace.report(FUNCTION_CALL_EXPECTED.on(callExpression, callExpression, hasValueParameters));
} }
return functionDescriptor != null ? functionDescriptor.getReturnType() : null; if (functionDescriptor == null) {
return JetTypeInfo.create(null, context.dataFlowInfo);
}
JetType type = functionDescriptor.getReturnType();
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
JetValueArgumentList argumentList = callExpression.getValueArgumentList();
if (argumentList != null) {
for (JetValueArgument argument : argumentList.getArguments()) {
JetExpression expression = argument.getArgumentExpression();
if (expression != null) {
dataFlowInfo = dataFlowInfo.and(facade.getTypeInfo(expression, context.replaceDataFlowInfo(dataFlowInfo)).getDataFlowInfo());
}
}
}
return JetTypeInfo.create(type, dataFlowInfo);
} }
JetExpression calleeExpression = callExpression.getCalleeExpression(); JetExpression calleeExpression = callExpression.getCalleeExpression();
@@ -721,11 +744,11 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
traceForVariable.commit(); traceForVariable.commit();
context.trace.report(FUNCTION_EXPECTED.on((JetReferenceExpression) calleeExpression, calleeExpression, context.trace.report(FUNCTION_EXPECTED.on((JetReferenceExpression) calleeExpression, calleeExpression,
type != null ? type : ErrorUtils.createErrorType(""))); type != null ? type : ErrorUtils.createErrorType("")));
return null; return JetTypeInfo.create(null, context.dataFlowInfo);
} }
} }
traceForFunction.commit(); traceForFunction.commit();
return null; return JetTypeInfo.create(null, context.dataFlowInfo);
} }
private static void checkSuper(@NotNull ReceiverDescriptor receiverDescriptor, @NotNull OverloadResolutionResults<? extends CallableDescriptor> results, private static void checkSuper(@NotNull ReceiverDescriptor receiverDescriptor, @NotNull OverloadResolutionResults<? extends CallableDescriptor> results,
@@ -743,8 +766,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
@Override @Override
public JetTypeInfo visitCallExpression(JetCallExpression expression, ExpressionTypingContext context) { public JetTypeInfo visitCallExpression(JetCallExpression expression, ExpressionTypingContext context) {
JetType expressionType = getCallExpressionType(expression, NO_RECEIVER, null, context); JetTypeInfo expressionTypeInfo = getCallExpressionTypeInfo(expression, NO_RECEIVER, null, context);
return DataFlowUtils.checkType(expressionType, expression, context, context.dataFlowInfo); return DataFlowUtils.checkType(expressionTypeInfo.getType(), expression, context, expressionTypeInfo.getDataFlowInfo());
} }
@Override @Override
@@ -784,6 +807,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
if (isKnownToBeNotNull(baseExpression, context)) { if (isKnownToBeNotNull(baseExpression, context)) {
context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, type)); context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, type));
} }
else {
DataFlowValue value = DataFlowValueFactory.INSTANCE.createDataFlowValue(baseExpression, type, context.trace.getBindingContext());
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
}
return DataFlowUtils.checkType(TypeUtils.makeNotNullable(type), expression, context, dataFlowInfo); return DataFlowUtils.checkType(TypeUtils.makeNotNullable(type), expression, context, dataFlowInfo);
} }
@@ -22,17 +22,16 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetTypeInfo;
/** /**
* @author abreslav * @author abreslav
*/ */
/*package*/ interface ExpressionTypingInternals extends ExpressionTypingFacade { /*package*/ interface ExpressionTypingInternals extends ExpressionTypingFacade {
@Nullable @NotNull
JetType getSelectorReturnType(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull ExpressionTypingContext context); JetTypeInfo getSelectorReturnTypeInfo(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull ExpressionTypingContext context);
boolean checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @Nullable JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context); boolean checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @Nullable JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context);
@@ -115,12 +115,17 @@ public class ExpressionTypingServices {
return ErrorUtils.createErrorType("Type for " + expression.getText()); return ErrorUtils.createErrorType("Type for " + expression.getText());
} }
@Nullable @NotNull
public JetType getType(@NotNull final JetScope scope, @NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) { public JetTypeInfo getTypeInfo(@NotNull final JetScope scope, @NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) {
ExpressionTypingContext context = ExpressionTypingContext.newContext( ExpressionTypingContext context = ExpressionTypingContext.newContext(
this, trace, scope, dataFlowInfo, expectedType, false this, trace, scope, dataFlowInfo, expectedType, false
); );
return expressionTypingFacade.getTypeInfo(expression, context).getType(); return expressionTypingFacade.getTypeInfo(expression, context);
}
@Nullable
public JetType getType(@NotNull final JetScope scope, @NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) {
return getTypeInfo(scope, expression, expectedType, dataFlowInfo, trace).getType();
} }
public JetType getTypeWithNamespaces(@NotNull final JetScope scope, @NotNull JetExpression expression, @NotNull BindingTrace trace) { public JetType getTypeWithNamespaces(@NotNull final JetScope scope, @NotNull JetExpression expression, @NotNull BindingTrace trace) {
@@ -69,8 +69,8 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetTypeInfo, E
} }
@Override @Override
public JetType getSelectorReturnType(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull ExpressionTypingContext context) { public JetTypeInfo getSelectorReturnTypeInfo(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull ExpressionTypingContext context) {
return basic.getSelectorReturnType(receiver, callOperationNode, selectorExpression, context); return basic.getSelectorReturnTypeInfo(receiver, callOperationNode, selectorExpression, context);
} }
@Override @Override
@@ -115,8 +115,11 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetTypeInfo, E
@NotNull @NotNull
private JetTypeInfo getTypeInfo(@NotNull JetExpression expression, ExpressionTypingContext context, JetVisitor<JetTypeInfo, ExpressionTypingContext> visitor) { private JetTypeInfo getTypeInfo(@NotNull JetExpression expression, ExpressionTypingContext context, JetVisitor<JetTypeInfo, ExpressionTypingContext> visitor) {
if (context.trace.get(BindingContext.PROCESSED, expression)) { if (context.trace.get(BindingContext.PROCESSED, expression)) {
return JetTypeInfo.create(context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression), DataFlowInfo dataFlowInfo = context.trace.get(BindingContext.EXPRESSION_DATA_FLOW_INFO, expression);
context.dataFlowInfo); if (dataFlowInfo == null) {
dataFlowInfo = context.dataFlowInfo;
}
return JetTypeInfo.create(context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression), dataFlowInfo);
} }
JetTypeInfo result; JetTypeInfo result;
try { try {
@@ -128,7 +131,7 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetTypeInfo, E
} }
if (result.getType() instanceof DeferredType) { if (result.getType() instanceof DeferredType) {
result = JetTypeInfo.create(((DeferredType) result.getType()).getActualType(), context.dataFlowInfo); result = JetTypeInfo.create(((DeferredType) result.getType()).getActualType(), result.getDataFlowInfo());
} }
if (result.getType() != null) { if (result.getType() != null) {
context.trace.record(BindingContext.EXPRESSION_TYPE, expression, result.getType()); context.trace.record(BindingContext.EXPRESSION_TYPE, expression, result.getType());
@@ -144,7 +147,10 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetTypeInfo, E
context.trace.record(BindingContext.RESOLUTION_SCOPE, expression, context.scope); context.trace.record(BindingContext.RESOLUTION_SCOPE, expression, context.scope);
} }
context.trace.record(BindingContext.PROCESSED, expression); context.trace.record(BindingContext.PROCESSED, expression);
return result; if (result.getDataFlowInfo() != context.dataFlowInfo) {
context.trace.record(BindingContext.EXPRESSION_DATA_FLOW_INFO, expression, result.getDataFlowInfo());
}
return result;
} }
////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////
@@ -239,7 +239,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
JetExpression decomposerExpression = pattern.getDecomposerExpression(); JetExpression decomposerExpression = pattern.getDecomposerExpression();
if (decomposerExpression != null) { if (decomposerExpression != null) {
ReceiverDescriptor receiver = new TransientReceiver(subjectType); ReceiverDescriptor receiver = new TransientReceiver(subjectType);
JetType selectorReturnType = facade.getSelectorReturnType(receiver, null, decomposerExpression, context); JetType selectorReturnType = facade.getSelectorReturnTypeInfo(receiver, null, decomposerExpression, context).getType();
if (pattern.getArgumentList() != null) { if (pattern.getArgumentList() != null) {
result.set(checkPatternType(pattern.getArgumentList(), selectorReturnType == null result.set(checkPatternType(pattern.getArgumentList(), selectorReturnType == null
@@ -6,8 +6,8 @@ fun foo() {
bar(<!TYPE_MISMATCH!>x<!>) bar(<!TYPE_MISMATCH!>x<!>)
if (x != null) bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) if (x != null) bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
if (x == null) bar(x!!) if (x == null) bar(x!!)
if (x != null) else bar(x!!) if (x != null) else bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
if (x == null) else bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) if (x == null) else bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
if (x != null) bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) else bar(x!!) if (x != null) bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) else bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
if (x == null) bar(x!!) else bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) if (x == null) bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) else bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
} }
@@ -2,11 +2,11 @@ fun main(args : Array<String>) {
val a : Int? = null val a : Int? = null
val b : Int? = null val b : Int? = null
a!! : Int a!! : Int
a!! + 2 a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!> + 2
a!!.plus(2) a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.plus(2)
a!!.plus(b!!) a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.plus(b!!)
2.plus(b!!) 2.plus(b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
2 + b!! 2 + b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
val c = 1 val c = 1
c<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!> c<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
@@ -30,6 +30,6 @@ fun main(args : Array<String>) {
} }
} }
val <!UNUSED_VARIABLE!>f<!> : String = <!TYPE_MISMATCH!>a!!<!> val <!UNUSED_VARIABLE!>f<!> : String = <!TYPE_MISMATCH!>a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!><!>
<!TYPE_MISMATCH!>a!!<!> : String <!TYPE_MISMATCH!>a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!><!> : String
} }
@@ -0,0 +1,29 @@
class Foo {
fun foo(a: Foo): Foo = a
}
fun main(args : Array<String>) {
val x: Foo? = null
val y: Foo? = null
x<!UNSAFE_CALL!>.<!>foo(<!TYPE_MISMATCH!>y<!>)
x!!.foo(<!TYPE_MISMATCH!>y<!>)
x.foo(y!!)
x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo(y<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
val a: Foo? = null
val b: Foo? = null
val c: Foo? = null
a<!UNSAFE_CALL!>.<!>foo(b<!UNSAFE_CALL!>.<!>foo(<!TYPE_MISMATCH!>c<!>))
a!!.foo(b<!UNSAFE_CALL!>.<!>foo(<!TYPE_MISMATCH!>c<!>))
a.foo(b!!.foo(<!TYPE_MISMATCH!>c<!>))
a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo(b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo(<!TYPE_MISMATCH!>c<!>))
a.foo(b.foo(c!!))
a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo(b.foo(c<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>))
a.foo(b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo(c<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>))
a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo(b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo(c<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>))
val z: Foo? = null
z!!.foo(z<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
}
@@ -0,0 +1,38 @@
//KT-2164 !! does not propagate nullability information
package kt2164
fun foo(x: Int): Int = x + 1
fun main(args : Array<String>) {
val x: Int? = null
foo(<!TYPE_MISMATCH!>x<!>)
if (x != null) {
foo(x)
foo(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
foo(x)
}
foo(<!TYPE_MISMATCH!>x<!>)
if (x != null) {
foo(x)
foo(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
foo(x)
} else {
foo(<!TYPE_MISMATCH!>x<!>)
foo(x!!)
foo(x)
}
foo(x)
foo(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
foo(x)
val y: Int? = null
y!!
y<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
foo(y)
foo(y<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
}
@@ -0,0 +1,20 @@
//KT-2216 Nullability of a value determined in function parameter computation doesn't pass to code following
package kt2216
fun bar(y: Int, z: Int) = y + z
fun baz(a: Int, b: Int, c: Int, d: Int) = a + b + c + d
fun foo() {
val x: Int? = 0
bar(if (x != null) x else return, x)
x + 2
bar(x, x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
val y: Int? = 0
val z: Int? = 0
bar(if (y != null) y else <!TYPE_MISMATCH!>z<!>, <!TYPE_MISMATCH!>y<!>)
y <!UNSAFE_INFIX_CALL!>+<!> 2
baz(<!TYPE_MISMATCH!>y<!>, <!TYPE_MISMATCH!>y<!>, if (y == null) return else y, y)
baz(y, z!!, z, y)
}