From 2feba3345edebd953aac1d8717d9ccce527291d6 Mon Sep 17 00:00:00 2001 From: svtk Date: Fri, 4 Nov 2011 22:54:24 +0400 Subject: [PATCH] KT-437 Support assignability checks --- .../jet/lang/resolve/BindingContextUtils.java | 5 +- .../BasicExpressionTypingVisitor.java | 19 +++++ .../ExpressionTypingVisitorForStatements.java | 12 ++-- .../quick/LValueAssignment.jet | 71 +++++++++++++++++++ 4 files changed, 100 insertions(+), 7 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java index af0491dc005..c617571bba0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java @@ -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); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index f00ecc3cce7..eab844d2e17 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -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); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java index f9010c9689b..a320b6d6da4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java @@ -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; diff --git a/compiler/testData/checkerWithErrorTypes/quick/LValueAssignment.jet b/compiler/testData/checkerWithErrorTypes/quick/LValueAssignment.jet index a5e989ec001..ae5cfbef6b9 100644 --- a/compiler/testData/checkerWithErrorTypes/quick/LValueAssignment.jet +++ b/compiler/testData/checkerWithErrorTypes/quick/LValueAssignment.jet @@ -56,3 +56,74 @@ fun canBe(var i: Int, val j: Int) { class A() { var a: Int = 3 } + +class Test() { + fun testIllegalValues() { + 1 += 23 + (1 : Int) += 43 + (@l 1) += 23 + + getInt() += 343 + (@f getInt()) += 343 + (getInt() : Int) += 343 + + 1++ + (@r 1)++ + (1 : Int)++ + + getInt()++ + (@m getInt())++ + (getInt() : Int)++ + + this++ + + 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 + + b += 34 + (@l b) += 34 + (b : Int) += 34 + (b) += 3 + + a++ + (@ a)++ + (a : Int)++ + (a)++ + } + + fun testArrays(a: Array, ab: Ab) { + a[3] = 4 + a[4]++ + a[6] += 43 + + ab.getArray()[54] = 23 + ab.getArray()[54]++ + + (@f a)[3] = 4 + (a : Array)[4]++ + (ab.getArray() : Array)[54] += 43 + + this[54] = 34 + } +} + +fun Array.checkThis() { + this[45] = 34 + this[352]++ + this[35] += 234 +} + +abstract class Ab { + abstract fun getArray() : Array +} \ No newline at end of file