KT-437 Support assignability checks
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
+19
@@ -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);
|
||||||
|
|||||||
+7
-5
@@ -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>
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user