New check rules for inc, plus & plusAssign

KT-1176 arrayList[0]++. Setter is stored for array access expression when it's needed
This commit is contained in:
svtk
2012-02-03 12:07:21 +04:00
parent db07a33d59
commit 6251b413a2
12 changed files with 209 additions and 162 deletions
@@ -556,7 +556,7 @@ public abstract class StackValue {
} }
public int receiverSize() { public int receiverSize() {
if(isStandardStack(resolvedGetCall) && isStandardStack(resolvedSetCall)) { if(isStandardStack(resolvedGetCall, 1) && isStandardStack(resolvedSetCall, 2)) {
return 2; return 2;
} }
else { else {
@@ -566,7 +566,7 @@ public abstract class StackValue {
@Override @Override
public void dupReceiver(InstructionAdapter v) { public void dupReceiver(InstructionAdapter v) {
if(isStandardStack(resolvedGetCall) && isStandardStack(resolvedSetCall)) { if(isStandardStack(resolvedGetCall, 1) && isStandardStack(resolvedSetCall, 2)) {
v.dup2(); // collection and index v.dup2(); // collection and index
} }
else { else {
@@ -702,7 +702,7 @@ public abstract class StackValue {
} }
} }
private boolean isStandardStack(ResolvedCall call) { private boolean isStandardStack(ResolvedCall call, int valueParamsSize) {
if(call == null) if(call == null)
return true; return true;
@@ -710,12 +710,15 @@ public abstract class StackValue {
if(typeParameterDescriptor.isReified()) if(typeParameterDescriptor.isReified())
return false; return false;
} }
if(call.getResultingDescriptor().getValueParameters().size() != 1) List<ValueParameterDescriptor> valueParameters = call.getResultingDescriptor().getValueParameters();
if(valueParameters.size() != valueParamsSize)
return false; return false;
if(codegen.typeMapper.mapType(call.getResultingDescriptor().getValueParameters().get(0).getOutType()).getSize() != 1) for (ValueParameterDescriptor valueParameter : valueParameters) {
return false; if (codegen.typeMapper.mapType(valueParameter.getOutType()).getSize() != 1)
return false;
}
if(call.getThisObject().exists()) { if(call.getThisObject().exists()) {
if(call.getReceiverArgument().exists()) if(call.getReceiverArgument().exists())
@@ -310,6 +310,16 @@ public class JetFlowInformationProvider {
hasReassignMethodReturningUnit = true; hasReassignMethodReturningUnit = true;
} }
} }
if (descriptor == null) {
Collection<? extends DeclarationDescriptor> descriptors = trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, operationReference);
if (descriptors != null) {
for (DeclarationDescriptor referenceDescriptor : descriptors) {
if (JetStandardClasses.isUnit(((FunctionDescriptor) referenceDescriptor).getReturnType())) {
hasReassignMethodReturningUnit = true;
}
}
}
}
} }
if (!hasReassignMethodReturningUnit) { if (!hasReassignMethodReturningUnit) {
trace.report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor)); trace.report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor));
@@ -199,6 +199,15 @@ public interface Errors {
SimpleDiagnosticFactory NO_GET_METHOD = SimpleDiagnosticFactory.create(ERROR, "No get method providing array access"); SimpleDiagnosticFactory NO_GET_METHOD = SimpleDiagnosticFactory.create(ERROR, "No get method providing array access");
SimpleDiagnosticFactory NO_SET_METHOD = SimpleDiagnosticFactory.create(ERROR, "No set method providing array access"); SimpleDiagnosticFactory NO_SET_METHOD = SimpleDiagnosticFactory.create(ERROR, "No set method providing array access");
SimpleDiagnosticFactory INC_DEC_SHOULD_NOT_RETURN_UNIT = SimpleDiagnosticFactory.create(ERROR, "Functions inc(), dec() shouldn't return Unit to be used by operators ++, --");
ParameterizedDiagnosticFactory2<DeclarationDescriptor, JetSimpleNameExpression> ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT = new ParameterizedDiagnosticFactory2<DeclarationDescriptor, JetSimpleNameExpression>(ERROR, "Function ''{0}'' should return Unit to be used by corresponding operator ''{1}''", NAME) {
@Override
protected String makeMessageForB(@NotNull JetSimpleNameExpression expression) {
return expression.getText();
}
};
AmbiguousDescriptorDiagnosticFactory ASSIGN_OPERATOR_AMBIGUITY = AmbiguousDescriptorDiagnosticFactory.create("Assignment operators ambiguity: {0}");
SimpleDiagnosticFactory EQUALS_MISSING = SimpleDiagnosticFactory.create(ERROR, "No method 'equals(Any?) : Boolean' available"); SimpleDiagnosticFactory EQUALS_MISSING = SimpleDiagnosticFactory.create(ERROR, "No method 'equals(Any?) : Boolean' available");
SimpleDiagnosticFactory ASSIGNMENT_IN_EXPRESSION_CONTEXT = SimpleDiagnosticFactory.create(ERROR, "Assignments are not expressions, and only expressions are allowed in this context"); SimpleDiagnosticFactory ASSIGNMENT_IN_EXPRESSION_CONTEXT = SimpleDiagnosticFactory.create(ERROR, "Assignments are not expressions, and only expressions are allowed in this context");
SimpleDiagnosticFactory NAMESPACE_IS_NOT_AN_EXPRESSION = SimpleDiagnosticFactory.create(ERROR, "'namespace' is not an expression, it can only be used on the left-hand side of a dot ('.')"); SimpleDiagnosticFactory NAMESPACE_IS_NOT_AN_EXPRESSION = SimpleDiagnosticFactory.create(ERROR, "'namespace' is not an expression, it can only be used on the left-hand side of a dot ('.')");
@@ -0,0 +1,30 @@
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.CallableDescriptor;
import org.jetbrains.jet.lang.types.JetType;
import java.util.Collection;
/**
* @author svtk
*/
public class OverloadResolutionResultsUtil {
@NotNull
public static <D extends CallableDescriptor> OverloadResolutionResults<D> ambiguity(OverloadResolutionResults<D> results1, OverloadResolutionResults<D> results2) {
Collection<ResolvedCallImpl<D>> resultingCalls = Lists.newArrayList();
resultingCalls.addAll((Collection<ResolvedCallImpl<D>>) results1.getResultingCalls());
resultingCalls.addAll((Collection<ResolvedCallImpl<D>>) results2.getResultingCalls());
return OverloadResolutionResultsImpl.ambiguity(resultingCalls);
}
@Nullable
public static <D extends CallableDescriptor> JetType getResultType(OverloadResolutionResults<D> results) {
if (results.isSuccess()) {
return results.getResultingDescriptor().getReturnType();
}
return null;
}
}
@@ -14,6 +14,7 @@ import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.CallMaker; 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.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
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;
@@ -27,6 +28,7 @@ import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
import javax.naming.ldap.ExtendedResponse;
import java.util.*; import java.util.*;
import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.diagnostics.Errors.*;
@@ -661,24 +663,19 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
context.trace.report(UNSUPPORTED.on(operationSign, "visitUnaryExpression")); context.trace.report(UNSUPPORTED.on(operationSign, "visitUnaryExpression"));
return null; return null;
} }
JetType type = null; TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace);
BindingTrace initialTrace = context.trace;
context = context.replaceBindingTrace(temporaryTrace);
JetType type = facade.getType(baseExpression, context.replaceExpectedType(NO_EXPECTED_TYPE));
if (type == null) {
temporaryTrace.commit();
return null;
}
if ((operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) && baseExpression instanceof JetArrayAccessExpression) { if ((operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) && baseExpression instanceof JetArrayAccessExpression) {
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace); JetExpression stubExpression = ExpressionTypingUtils.createStubExpressionOfNecessaryType(baseExpression.getProject(), type, context.trace);
JetType expressionType = facade.getType(baseExpression, context.replaceExpectedType(NO_EXPECTED_TYPE).replaceBindingTrace(temporaryTrace)); resolveArrayAccessSetMethod((JetArrayAccessExpression) baseExpression, stubExpression, context.replaceExpectedType(NO_EXPECTED_TYPE).replaceBindingTrace(TemporaryBindingTrace.create(initialTrace)), context.trace);
if (expressionType != null) {
JetExpression stubExpression = ExpressionTypingUtils.createStubExpressionOfNecessaryType(baseExpression.getProject(), expressionType, context.trace);
type = resolveArrayAccessToLValue((JetArrayAccessExpression) baseExpression, stubExpression, context, true);
}
else {
temporaryTrace.commit();
context.trace.report(UNRESOLVED_REFERENCE.on(operationSign));
}
} }
else {
type = facade.getType(baseExpression, context.replaceExpectedType(NO_EXPECTED_TYPE));
}
if (type == null) return null;
ExpressionReceiver receiver = new ExpressionReceiver(baseExpression, type); ExpressionReceiver receiver = new ExpressionReceiver(baseExpression, type);
@@ -687,12 +684,16 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
expression.getOperationReference(), expression.getOperationReference(),
name); name);
if (!functionDescriptor.isSuccess()) return null; if (!functionDescriptor.isSuccess()) {
temporaryTrace.commit();
return null;
}
JetType returnType = functionDescriptor.getResultingDescriptor().getReturnType(); JetType returnType = functionDescriptor.getResultingDescriptor().getReturnType();
JetType result; JetType result;
if (operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) { if (operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) {
if (context.semanticServices.getTypeChecker().isSubtypeOf(returnType, JetStandardClasses.getUnitType())) { if (context.semanticServices.getTypeChecker().isSubtypeOf(returnType, JetStandardClasses.getUnitType())) {
result = JetStandardClasses.getUnitType(); result = JetStandardClasses.getUnitType();
context.trace.report(INC_DEC_SHOULD_NOT_RETURN_UNIT.on(operationSign));
} }
else { else {
JetType receiverType = receiver.getType(); JetType receiverType = receiver.getType();
@@ -712,7 +713,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
else { else {
result = returnType; result = returnType;
} }
temporaryTrace.commit();
return DataFlowUtils.checkType(result, expression, context); return DataFlowUtils.checkType(result, expression, context);
} }
@@ -849,13 +850,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
public boolean checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @Nullable JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context) { public boolean checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @Nullable JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context) {
String name = "contains"; String name = "contains";
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, right, context.replaceExpectedType(NO_EXPECTED_TYPE)); ExpressionReceiver receiver = safeGetExpressionReceiver(facade, right, context.replaceExpectedType(NO_EXPECTED_TYPE));
OverloadResolutionResults<FunctionDescriptor> functionDescriptor = context.resolveCallWithGivenNameToDescriptor( OverloadResolutionResults<FunctionDescriptor> resolutionResult = context.resolveCallWithGivenNameToDescriptor(
CallMaker.makeCallWithExpressions(callElement, receiver, null, operationSign, Collections.singletonList(left)), CallMaker.makeCallWithExpressions(callElement, receiver, null, operationSign, Collections.singletonList(left)),
operationSign, operationSign,
name); name);
JetType containsType = functionDescriptor.isSuccess() ? functionDescriptor.getResultingDescriptor().getReturnType() : null; JetType containsType = OverloadResolutionResultsUtil.getResultType(resolutionResult);
ensureBooleanResult(operationSign, name, containsType, context); ensureBooleanResult(operationSign, name, containsType, context);
return functionDescriptor.isSuccess(); return resolutionResult.isSuccess();
} }
private void ensureNonemptyIntersectionOfOperandTypes(JetBinaryExpression expression, ExpressionTypingContext context) { private void ensureNonemptyIntersectionOfOperandTypes(JetBinaryExpression expression, ExpressionTypingContext context) {
@@ -892,38 +893,24 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
} }
@Override @Override
public JetType visitArrayAccessExpression(JetArrayAccessExpression expression, ExpressionTypingContext contextWithExpectedType) { public JetType visitArrayAccessExpression(JetArrayAccessExpression expression, ExpressionTypingContext context) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); return resolveArrayAccessGetMethod(expression, context.replaceExpectedType(NO_EXPECTED_TYPE));
JetExpression arrayExpression = expression.getArrayExpression();
ExpressionReceiver receiver = getExpressionReceiver(facade, arrayExpression, context.replaceScope(context.scope));
if (receiver != null) {
OverloadResolutionResults<FunctionDescriptor> results = context.resolveCallWithGivenName(
CallMaker.makeCallWithExpressions(expression, receiver, null, expression, expression.getIndexExpressions()),
expression,
"get"
);
if (results.isSuccess()) {
context.trace.record(INDEXED_LVALUE_GET, expression, results.getResultingCall());
return DataFlowUtils.checkType(results.getResultingDescriptor().getReturnType(), expression, contextWithExpectedType);
}
}
context.trace.report(NO_GET_METHOD.on(expression.getIndicesNode()));
return null;
} }
@Nullable @Nullable
public JetType getTypeForBinaryCall(JetScope scope, String name, ExpressionTypingContext context, JetBinaryExpression binaryExpression) { public JetType getTypeForBinaryCall(JetScope scope, String name, ExpressionTypingContext context, JetBinaryExpression binaryExpression) {
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope)); ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope));
OverloadResolutionResults<FunctionDescriptor> results = context.replaceScope(scope).resolveCallWithGivenNameToDescriptor( return OverloadResolutionResultsUtil.getResultType(getResolutionResultsForBinaryCall(scope, name, context, binaryExpression, receiver));
}
@NotNull
/*package*/ OverloadResolutionResults<FunctionDescriptor> getResolutionResultsForBinaryCall(JetScope scope, String name, ExpressionTypingContext context, JetBinaryExpression binaryExpression, ExpressionReceiver receiver) {
// ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope));
return context.replaceScope(scope).resolveCallWithGivenNameToDescriptor(
CallMaker.makeCall(receiver, binaryExpression), CallMaker.makeCall(receiver, binaryExpression),
binaryExpression.getOperationReference(), binaryExpression.getOperationReference(),
name name
); );
if (results.isSuccess()) {
return results.getResultingDescriptor().getReturnType();
}
return null;
} }
@Override @Override
@@ -999,45 +986,38 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName())); context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName()));
return null; return null;
} }
/*package*/ JetType resolveArrayAccessToLValue(@NotNull JetArrayAccessExpression arrayAccessExpression, @NotNull JetExpression right, @NotNull ExpressionTypingContext context, final boolean getterNeeded) {
ExpressionReceiver receiver = getExpressionReceiver(facade, arrayAccessExpression.getArrayExpression(), context.replaceExpectedType(NO_EXPECTED_TYPE));
if (receiver == null) return null;
OverloadResolutionResults<FunctionDescriptor> getFunctionResults = null; @Nullable
if (getterNeeded) { /*package*/ JetType resolveArrayAccessSetMethod(@NotNull JetArrayAccessExpression arrayAccessExpression, @NotNull JetExpression rightHandSide, @NotNull ExpressionTypingContext context, @NotNull BindingTrace traceForResolveResult) {
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace); return resolveArrayAccessSpecialMethod(arrayAccessExpression, rightHandSide, context, traceForResolveResult, false);
getFunctionResults = context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceBindingTrace(temporaryTrace).resolveCallWithGivenName( }
CallMaker.makeArrayGetCall(receiver, arrayAccessExpression),
arrayAccessExpression,
"get");
if (!getFunctionResults.isSuccess()) {
temporaryTrace.commit();
context.trace.report(NO_GET_METHOD.on(arrayAccessExpression.getIndicesNode()));
return null;
}
context.trace.record(INDEXED_LVALUE_GET, arrayAccessExpression, getFunctionResults.getResultingCall());
}
Call call = CallMaker.makeArraySetCall(receiver, arrayAccessExpression, right); @Nullable
OverloadResolutionResults<FunctionDescriptor> setFunctionResults = context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).resolveCallWithGivenName( /*package*/ JetType resolveArrayAccessGetMethod(@NotNull JetArrayAccessExpression arrayAccessExpression, @NotNull ExpressionTypingContext context) {
call, return resolveArrayAccessSpecialMethod(arrayAccessExpression, null, context, context.trace, true);
}
@Nullable
private JetType resolveArrayAccessSpecialMethod(@NotNull JetArrayAccessExpression arrayAccessExpression,
@Nullable JetExpression rightHandSide, //only for 'set' method
@NotNull ExpressionTypingContext context,
@NotNull BindingTrace traceForResolveResult,
boolean isGet) {
JetType arrayType = facade.getType(arrayAccessExpression.getArrayExpression(), context);
if (arrayType == null) return null;
ExpressionReceiver receiver = new ExpressionReceiver(arrayAccessExpression.getArrayExpression(), arrayType);
if (!isGet) assert rightHandSide != null;
OverloadResolutionResults<FunctionDescriptor> functionResults = context.resolveCallWithGivenName(
isGet ? CallMaker.makeArrayGetCall(receiver, arrayAccessExpression) : CallMaker.makeArraySetCall(receiver, arrayAccessExpression, rightHandSide),
arrayAccessExpression, arrayAccessExpression,
"set"); isGet ? "get" : "set");
if (!setFunctionResults.isSuccess()) { if (!functionResults.isSuccess()) {
context.trace.report(NO_SET_METHOD.on(arrayAccessExpression.getIndicesNode())); JetContainerNode indicesNode = arrayAccessExpression.getIndicesNode();
if (getterNeeded) { traceForResolveResult.report(isGet ? NO_GET_METHOD.on(indicesNode) : NO_SET_METHOD.on(indicesNode));
return getFunctionResults.getResultingDescriptor().getReturnType();
}
return null; return null;
} }
FunctionDescriptor setFunctionDescriptor = setFunctionResults.getResultingDescriptor(); traceForResolveResult.record(isGet ? INDEXED_LVALUE_GET : INDEXED_LVALUE_SET, arrayAccessExpression, functionResults.getResultingCall());
context.trace.record(INDEXED_LVALUE_SET, arrayAccessExpression, setFunctionResults.getResultingCall()); return functionResults.getResultingDescriptor().getReturnType();
if (getterNeeded) {
return getFunctionResults.getResultingDescriptor().getReturnType();
}
return setFunctionDescriptor.getReturnType();
} }
} }
@@ -1,23 +1,29 @@
package org.jetbrains.jet.lang.types.expressions; package org.jetbrains.jet.lang.types.expressions;
import com.google.common.collect.Sets;
import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace; import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResultsUtil;
import org.jetbrains.jet.lang.resolve.TopDownAnalyzer; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetStandardClasses;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collection;
import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.AMBIGUOUS_REFERENCE_TARGET;
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE_REASSIGNMENT; import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE_REASSIGNMENT;
/** /**
@@ -140,63 +146,74 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
} }
protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) { protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE); //There is a temporary binding trace for an opportunity to resolve set method for array if needed (the initial trace should be used there)
// If there's += (or similar op) defined as such, we just call it. TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(contextWithExpectedType.trace);
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE).replaceBindingTrace(temporaryBindingTrace);
JetSimpleNameExpression operationSign = expression.getOperationReference(); JetSimpleNameExpression operationSign = expression.getOperationReference();
IElementType operationType = operationSign.getReferencedNameElementType(); IElementType operationType = operationSign.getReferencedNameElementType();
String name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType);
TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace);
JetType assignmentOperationType = basic.getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression);
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft()); JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
// If there isn't, we call plus (or like) and then assign if (left == null) return null;
if (assignmentOperationType == null) {
String counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType));
if (left instanceof JetArrayAccessExpression) { JetType leftType = facade.getType(left, context);
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; if (leftType == null) {
basic.resolveArrayAccessToLValue(arrayAccessExpression, expression.getRight(), context.replaceScope(scope), false); facade.getType(expression.getRight(), context);
} context.trace.report(UNRESOLVED_REFERENCE.on(operationSign));
assignmentOperationType = basic.getTypeForBinaryCall(scope, counterpartName, context, expression);
if (assignmentOperationType != null) {
context.trace.record(VARIABLE_REASSIGNMENT, expression);
ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context);
}
}
else {
temporaryBindingTrace.commit(); temporaryBindingTrace.commit();
checkReassignment(expression, context, assignmentOperationType, left); return null;
}
ExpressionReceiver receiver = new ExpressionReceiver(left, leftType);
// We check that defined only one of '+=' and '+' operations, and call it (in the case '+' we then also assign)
// Check for '+='
String name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType);
TemporaryBindingTrace assignmentOperationTrace = TemporaryBindingTrace.create(context.trace);
OverloadResolutionResults<FunctionDescriptor> assignmentOperationDescriptors = basic.getResolutionResultsForBinaryCall(scope, name, context.replaceBindingTrace(assignmentOperationTrace), expression, receiver);
JetType assignmentOperationType = OverloadResolutionResultsUtil.getResultType(assignmentOperationDescriptors);
// Check for '+'
String counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType));
TemporaryBindingTrace binaryOperationTrace = TemporaryBindingTrace.create(context.trace);
OverloadResolutionResults<FunctionDescriptor> binaryOperationDescriptors = basic.getResolutionResultsForBinaryCall(scope, counterpartName, context.replaceBindingTrace(binaryOperationTrace), expression, receiver);
JetType binaryOperationType = OverloadResolutionResultsUtil.getResultType(binaryOperationDescriptors);
JetType type = assignmentOperationType != null ? assignmentOperationType : binaryOperationType;
if (assignmentOperationType != null && binaryOperationType != null) {
OverloadResolutionResults<FunctionDescriptor> ambiguityResolutionResults = OverloadResolutionResultsUtil.ambiguity(assignmentOperationDescriptors, binaryOperationDescriptors);
context.trace.report(ASSIGN_OPERATOR_AMBIGUITY.on(operationSign, ambiguityResolutionResults.getResultingCalls()));
Collection<DeclarationDescriptor> descriptors = Sets.newHashSet();
for (ResolvedCall<? extends FunctionDescriptor> call : ambiguityResolutionResults.getResultingCalls()) {
descriptors.add(call.getResultingDescriptor());
}
facade.getType(expression.getRight(), context);
context.trace.record(AMBIGUOUS_REFERENCE_TARGET, operationSign, descriptors);
}
else if (assignmentOperationType != null) {
assignmentOperationTrace.commit();
if (!JetStandardClasses.isUnit(assignmentOperationType)) {
context.trace.report(ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT.on(operationSign, assignmentOperationDescriptors.getResultingDescriptor(), operationSign));
}
} else {
binaryOperationTrace.commit();
context.trace.record(VARIABLE_REASSIGNMENT, expression);
ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context);
if (left instanceof JetArrayAccessExpression) {
ExpressionTypingContext contextForResolve = context.replaceScope(scope).replaceBindingTrace(TemporaryBindingTrace.create(contextWithExpectedType.trace));
basic.resolveArrayAccessSetMethod((JetArrayAccessExpression) left, expression.getRight(), contextForResolve, context.trace);
}
} }
basic.checkLValue(context.trace, expression.getLeft()); basic.checkLValue(context.trace, expression.getLeft());
return checkAssignmentType(assignmentOperationType, expression, contextWithExpectedType); temporaryBindingTrace.commit();
return checkAssignmentType(type, expression, contextWithExpectedType);
} }
private void checkReassignment(@NotNull JetBinaryExpression expression, @NotNull ExpressionTypingContext context,
@NotNull JetType assignmentOperationType, @Nullable JetExpression left) {
if (left == null) return;
JetType leftType = facade.getType(left, context.replaceScope(scope));
if (leftType == null) return;
boolean isUnit = context.semanticServices.getTypeChecker().
isSubtypeOf(assignmentOperationType, JetStandardClasses.getUnitType());
if (isUnit) return;
if (context.semanticServices.getTypeChecker().isSubtypeOf(assignmentOperationType, leftType)) {
context.trace.record(VARIABLE_REASSIGNMENT, expression);
}
else {
context.trace.report(TYPE_MISMATCH.on(expression, leftType, assignmentOperationType));
}
}
protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) { protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE); ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE);
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft()); JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
JetExpression right = expression.getRight(); JetExpression right = expression.getRight();
if (left instanceof JetArrayAccessExpression) { if (left instanceof JetArrayAccessExpression) {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
JetType assignmentType = basic.resolveArrayAccessToLValue(arrayAccessExpression, right, context.replaceScope(scope), false); //todo JetType assignmentType = basic.resolveArrayAccessSetMethod(arrayAccessExpression, right, context.replaceScope(scope), context.trace);
basic.checkLValue(context.trace, arrayAccessExpression); basic.checkLValue(context.trace, arrayAccessExpression);
return checkAssignmentType(assignmentType, expression, contextWithExpectedType); return checkAssignmentType(assignmentType, expression, contextWithExpectedType);
} }
@@ -7,7 +7,7 @@ class ArrayWrapper<T>() {
contents.add(item) contents.add(item)
} }
fun plusAssign(rhs: ArrayWrapper<T>): ArrayWrapper<T> { fun plus(rhs: ArrayWrapper<T>): ArrayWrapper<T> {
val result = ArrayWrapper<T>() val result = ArrayWrapper<T>()
result.contents.addAll(contents) result.contents.addAll(contents)
result.contents.addAll(rhs.contents) result.contents.addAll(rhs.contents)
@@ -37,7 +37,7 @@ fun test4() : Boolean {
return true return true
} }
/*fun test5() : Boolean { fun test5() : Boolean {
var mnr = Array<MyNumber>(2,{MyNumber(42)}) var mnr = Array<MyNumber>(2,{MyNumber(42)})
mnr[0]++ mnr[0]++
if (mnr[0].i != 43) return false if (mnr[0].i != 43) return false
@@ -69,7 +69,7 @@ fun test8() : Boolean {
if (mnr[1].i != 42) return false if (mnr[1].i != 42) return false
return true return true
} }
*/
fun box() : String { fun box() : String {
var m = MyNumber(42) var m = MyNumber(42)
@@ -78,7 +78,7 @@ fun box() : String {
if (!test2()) return "fail test 2" if (!test2()) return "fail test 2"
if (!test3()) return "fail test 3" if (!test3()) return "fail test 3"
if (!test4()) return "fail test 4" if (!test4()) return "fail test 4"
/*
if (!test5()) return "fail test 5" if (!test5()) return "fail test 5"
if (!test6()) return "fail test 6" if (!test6()) return "fail test 6"
if (!test7()) return "fail test 7" if (!test7()) return "fail test 7"
@@ -86,11 +86,11 @@ fun box() : String {
++m ++m
if (m.i != 44) return "fail 0" if (m.i != 43) return "fail 0"
var m1 = ++m var m1 = ++m
if (m1.i != 46) return "fail 3" if (m1.i != 44) return "fail 3"
if (m.i != 46) return "fail 4" if (m.i != 44) return "fail 4"
*/
return "OK" return "OK"
} }
@@ -35,12 +35,12 @@ class UnitIncDec() {
fun testUnitIncDec() { fun testUnitIncDec() {
var x = UnitIncDec() var x = UnitIncDec()
x++ x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>
++x <!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>x
x-- x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>--<!>
--x <!INC_DEC_SHOULD_NOT_RETURN_UNIT!>--<!>x
x = <!TYPE_MISMATCH, UNUSED_CHANGED_VALUE!>x++<!> x = <!UNUSED_CHANGED_VALUE!>x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!><!>
x = <!TYPE_MISMATCH, UNUSED_CHANGED_VALUE!>x--<!> x = <!UNUSED_CHANGED_VALUE!>x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>--<!><!>
x = <!TYPE_MISMATCH!>++x<!> x = <!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>x
x = <!TYPE_MISMATCH, UNUSED_VALUE!>--x<!> x = <!UNUSED_VALUE!><!INC_DEC_SHOULD_NOT_RETURN_UNIT!>--<!>x<!>
} }
@@ -20,9 +20,9 @@ class B() {
fun main(args : Array<String>) { fun main(args : Array<String>) {
var c = B() var c = B()
<!TYPE_MISMATCH!>c += B()<!> c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>+=<!> B()
<!TYPE_MISMATCH!>c *= B()<!> c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>*=<!> B()
<!TYPE_MISMATCH!>c /= B()<!> c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>/=<!> B()
<!TYPE_MISMATCH!>c -= B()<!> c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>-=<!> B()
<!TYPE_MISMATCH!>c %= B()<!> c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>%=<!> B()
} }
+8 -8
View File
@@ -35,12 +35,12 @@ class UnitIncDec() {
fun testUnitIncDec() { fun testUnitIncDec() {
var x = UnitIncDec() var x = UnitIncDec()
x++ x<error>++</error>
++x <error>++</error>x
x-- x<error>--</error>
--x <error>--</error>x
x = <error>x++</error> x = <warning>x<error>++</error></warning>
x = <error>x--</error> x = <warning>x<error>--</error></warning>
x = <error>++x</error> x = <error>++</error>x
x = <error>--x</error> x = <warning><error>--</error>x</warning>
} }
-2
View File
@@ -2,8 +2,6 @@ package std
import java.io.StringReader import java.io.StringReader
inline fun <T> T?.plus(str: String?) : String { return toString() + str }
inline fun String.lastIndexOf(s: String) = (this as java.lang.String).lastIndexOf(s) inline fun String.lastIndexOf(s: String) = (this as java.lang.String).lastIndexOf(s)
inline fun String.lastIndexOf(s: Char) = (this as java.lang.String).lastIndexOf(s.toString()) inline fun String.lastIndexOf(s: Char) = (this as java.lang.String).lastIndexOf(s.toString())