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; package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor; import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
@@ -14,7 +15,7 @@ public class BindingContextUtils {
} }
@Nullable @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); DeclarationDescriptor declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, referenceExpression);
if (declarationDescriptor == null) { if (declarationDescriptor == null) {
return bindingContext.get(BindingContext.LABEL_TARGET, referenceExpression); return bindingContext.get(BindingContext.LABEL_TARGET, referenceExpression);
@@ -23,7 +24,7 @@ public class BindingContextUtils {
} }
@Nullable @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; DeclarationDescriptor descriptor = null;
if (!onlyReference && (element instanceof JetProperty || element instanceof JetParameter)) { if (!onlyReference && (element instanceof JetProperty || element instanceof JetParameter)) {
descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element); descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
@@ -579,6 +579,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
else { else {
context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression); context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression);
ExpressionTypingUtils.checkWrappingInRef(baseExpression, context); ExpressionTypingUtils.checkWrappingInRef(baseExpression, context);
checkLValue(context.trace, baseExpression);
} }
// TODO : Maybe returnType? // TODO : Maybe returnType?
result = receiverType; result = receiverType;
@@ -591,6 +593,23 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return DataFlowUtils.checkType(result, expression, context); 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 @Override
public JetType visitBinaryExpression(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) { public JetType visitBinaryExpression(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE);
@@ -117,11 +117,11 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace); TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace);
JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression); 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 there isn't, we call plus (or like) and then assign
if (assignmentOperationType == null) { if (assignmentOperationType == null) {
String counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType)); String counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType));
JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
if (left instanceof JetArrayAccessExpression) { if (left instanceof JetArrayAccessExpression) {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
resolveArrayAccessToLValue(arrayAccessExpression, expression.getRight(), operationSign, context); resolveArrayAccessToLValue(arrayAccessExpression, expression.getRight(), operationSign, context);
@@ -135,6 +135,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
else { else {
temporaryBindingTrace.commit(); temporaryBindingTrace.commit();
} }
checkLValue(context.trace, expression.getLeft());
return checkExpectedType(expression, context); return checkExpectedType(expression, context);
} }
@@ -144,15 +145,16 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV
JetExpression right = expression.getRight(); JetExpression right = expression.getRight();
if (left instanceof JetArrayAccessExpression) { if (left instanceof JetArrayAccessExpression) {
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; 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)); JetType leftType = facade.getType(expression.getLeft(), context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(scope));
if (right != null) { if (right != null) {
JetType rightType = facade.getType(right, context.replaceExpectedType(leftType).replaceScope(scope)); JetType rightType = facade.getType(right, context.replaceExpectedType(leftType).replaceScope(scope));
} }
VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(context.trace.getBindingContext(), left, true); if (leftType != null) { //if leftType == null, some another error has been generated
if (variable == null && leftType != null) { //if leftType == null, some another error has been generated checkLValue(context.trace, expression.getLeft());
context.trace.report(VARIABLE_EXPECTED.on(left != null ? left : expression.getLeft()));
} }
if (left instanceof JetSimpleNameExpression) { if (left instanceof JetSimpleNameExpression) {
JetSimpleNameExpression simpleName = (JetSimpleNameExpression) left; JetSimpleNameExpression simpleName = (JetSimpleNameExpression) left;
@@ -56,3 +56,74 @@ fun canBe(var i: Int, val j: Int) {
class A() { class A() {
var a: Int = 3 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>
}