KT-437 Support assignability checks

This commit is contained in:
svtk
2011-11-04 22:54:24 +04:00
parent bd1898cff2
commit 2feba3345e
4 changed files with 100 additions and 7 deletions
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
@@ -14,7 +15,7 @@ public class BindingContextUtils {
}
@Nullable
public static PsiElement resolveToDeclarationPsiElement(BindingContext bindingContext, JetReferenceExpression referenceExpression) {
public static PsiElement resolveToDeclarationPsiElement(@NotNull BindingContext bindingContext, @Nullable JetReferenceExpression referenceExpression) {
DeclarationDescriptor declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, referenceExpression);
if (declarationDescriptor == null) {
return bindingContext.get(BindingContext.LABEL_TARGET, referenceExpression);
@@ -23,7 +24,7 @@ public class BindingContextUtils {
}
@Nullable
public static VariableDescriptor extractVariableDescriptorIfAny(BindingContext bindingContext, JetElement element, boolean onlyReference) {
public static VariableDescriptor extractVariableDescriptorIfAny(@NotNull BindingContext bindingContext, @Nullable JetElement element, boolean onlyReference) {
DeclarationDescriptor descriptor = null;
if (!onlyReference && (element instanceof JetProperty || element instanceof JetParameter)) {
descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
@@ -579,6 +579,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
else {
context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression);
ExpressionTypingUtils.checkWrappingInRef(baseExpression, context);
checkLValue(context.trace, baseExpression);
}
// TODO : Maybe returnType?
result = receiverType;
@@ -591,6 +593,23 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return DataFlowUtils.checkType(result, expression, context);
}
protected void checkLValue(BindingTrace trace, JetExpression expression) {
checkLValue(trace, expression, false);
}
private void checkLValue(BindingTrace trace, JetExpression expressionWithParenthesis, boolean canBeThis) {
JetExpression expression = JetPsiUtil.deparenthesize(expressionWithParenthesis);
if (expression instanceof JetArrayAccessExpression) {
checkLValue(trace, ((JetArrayAccessExpression) expressionWithParenthesis).getArrayExpression(), true);
return;
}
if (canBeThis && expression instanceof JetThisExpression) return;
VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
if (variable == null) {
trace.report(VARIABLE_EXPECTED.on(expression != null ? expression : expressionWithParenthesis));
}
}
@Override
public JetType visitBinaryExpression(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE);
@@ -117,11 +117,11 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace);
JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression);
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
// If there isn't, we call plus (or like) and then assign
if (assignmentOperationType == null) {
String counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType));
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
if (left instanceof JetArrayAccessExpression) {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
resolveArrayAccessToLValue(arrayAccessExpression, expression.getRight(), operationSign, context);
@@ -135,6 +135,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
else {
temporaryBindingTrace.commit();
}
checkLValue(context.trace, expression.getLeft());
return checkExpectedType(expression, context);
}
@@ -144,15 +145,16 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
JetExpression right = expression.getRight();
if (left instanceof JetArrayAccessExpression) {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
return resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context);
JetType resultType = resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context);
checkLValue(context.trace, arrayAccessExpression);
return resultType;
}
JetType leftType = facade.getType(expression.getLeft(), context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(scope));
if (right != null) {
JetType rightType = facade.getType(right, context.replaceExpectedType(leftType).replaceScope(scope));
}
VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(context.trace.getBindingContext(), left, true);
if (variable == null && leftType != null) { //if leftType == null, some another error has been generated
context.trace.report(VARIABLE_EXPECTED.on(left != null ? left : expression.getLeft()));
if (leftType != null) { //if leftType == null, some another error has been generated
checkLValue(context.trace, expression.getLeft());
}
if (left instanceof JetSimpleNameExpression) {
JetSimpleNameExpression simpleName = (JetSimpleNameExpression) left;
@@ -56,3 +56,74 @@ fun canBe(var i: Int, val j: Int) {
class A() {
var a: Int = 3
}
class Test() {
fun testIllegalValues() {
<!VARIABLE_EXPECTED!>1<!> += 23
(<!VARIABLE_EXPECTED!>1<!> : Int) += 43
(@l <!VARIABLE_EXPECTED!>1<!>) += 23
<!VARIABLE_EXPECTED!>getInt()<!> += 343
(@f <!VARIABLE_EXPECTED!>getInt()<!>) += 343
(<!VARIABLE_EXPECTED!>getInt()<!> : Int) += 343
<!VARIABLE_EXPECTED!>1<!>++
(@r <!VARIABLE_EXPECTED!>1<!>)++
(<!VARIABLE_EXPECTED!>1<!> : Int)++
<!VARIABLE_EXPECTED!>getInt()<!>++
(@m <!VARIABLE_EXPECTED!>getInt()<!>)++
(<!VARIABLE_EXPECTED!>getInt()<!> : Int)++
this<!UNRESOLVED_REFERENCE!>++<!>
var s : String = "r"
s += "ss"
s += this
s += (@a 2)
}
fun testVariables() {
var a: Int = 34
val b: Int = 34
a += 34
(@l a) += 34
(a : Int) += 34
<!VAL_REASSIGNMENT!>b<!> += 34
(@l <!VAL_REASSIGNMENT!>b<!>) += 34
(<!VAL_REASSIGNMENT!>b<!> : Int) += 34
(<!VAL_REASSIGNMENT!>b<!>) += 3
a++
(@ a)++
(a : Int)++
(a)++
}
fun testArrays(a: Array<Int>, ab: Ab) {
a[3] = 4
a[4]++
a[6] += 43
<!VARIABLE_EXPECTED!>ab.getArray()<!>[54] = 23
<!VARIABLE_EXPECTED!>ab.getArray()<!>[54]++
(@f a)[3] = 4
(a : Array<Int>)[4]++
(<!VARIABLE_EXPECTED!>ab.getArray()<!> : Array<Int>)[54] += 43
<!UNRESOLVED_REFERENCE!>this[54]<!> = 34
}
}
fun Array<Int>.checkThis() {
this[45] = 34
this[352]++
this[35] += 234
}
abstract class Ab {
abstract fun getArray() : Array<Int>
}