diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 886dc03614f..9127b0bf9cb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -736,7 +736,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression); KtExpression stubExpression = ExpressionTypingUtils.createFakeExpressionOfType( baseExpression.getProject(), context.trace, "e", type); - checkLValue(context.trace, context, baseExpression, stubExpression); + checkLValue(context.trace, context, baseExpression, stubExpression, expression); } // x++ type is x type, but ++x type is x.inc() type DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue( @@ -854,9 +854,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @NotNull BindingTrace trace, @NotNull ExpressionTypingContext context, @NotNull KtExpression expression, - @Nullable KtExpression rightHandSide + @Nullable KtExpression rightHandSide, + @NotNull KtOperationExpression operationExpression ) { - return checkLValue(trace, context, expression, rightHandSide, false); + return checkLValue(trace, context, expression, rightHandSide, operationExpression, false); } private boolean checkLValue( @@ -864,6 +865,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @NotNull ExpressionTypingContext context, @NotNull KtExpression expressionWithParenthesis, @Nullable KtExpression rightHandSide, + @NotNull KtOperationExpression operationExpression, boolean canBeThis ) { KtExpression expression = KtPsiUtil.deparenthesize(expressionWithParenthesis); @@ -875,6 +877,19 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { TemporaryBindingTrace ignoreReportsTrace = TemporaryBindingTrace.create(trace, "Trace for checking set function"); ExpressionTypingContext findSetterContext = context.replaceBindingTrace(ignoreReportsTrace); KotlinTypeInfo info = resolveArrayAccessSetMethod(arrayAccessExpression, rightHandSide, findSetterContext, ignoreReportsTrace); + + IElementType operationType = operationExpression.getOperationReference().getReferencedNameElementType(); + if (KtTokens.AUGMENTED_ASSIGNMENTS.contains(operationType) + || operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS) { + ResolvedCall resolvedCall = ignoreReportsTrace.get(INDEXED_LVALUE_SET, expression); + if (resolvedCall != null) { + CallableDescriptor descriptor = resolvedCall.getResultingDescriptor(); + // Call must be validated with the actual, not temporary trace in order to report operator diagnostic + // Only unary assignment expressions (++, --) and +=/... must be checked, normal assignments have the proper trace + components.symbolUsageValidator.validateCall(resolvedCall, descriptor, trace, expression); + } + } + return info.getType() != null; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java index 2f378a27bd5..52d00b67361 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java @@ -219,7 +219,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito TemporaryTraceAndCache temporaryForBinaryOperation = TemporaryTraceAndCache.create( context, "trace to check binary operation like '+' for", expression); TemporaryBindingTrace ignoreReportsTrace = TemporaryBindingTrace.create(context.trace, "Trace for checking assignability"); - boolean lhsAssignable = basic.checkLValue(ignoreReportsTrace, context, left, right); + boolean lhsAssignable = basic.checkLValue(ignoreReportsTrace, context, left, right, expression); if (assignmentOperationType == null || lhsAssignable) { // Check for '+' Name counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType)); @@ -227,6 +227,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito context.replaceTraceAndCache(temporaryForBinaryOperation).replaceScope(scope), receiver, expression, counterpartName ); + binaryOperationType = OverloadResolutionResultsUtil.getResultingType(binaryOperationDescriptors, context.contextDependency); } else { @@ -269,7 +270,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito components.dataFlowAnalyzer.checkType(binaryOperationType, expression, context.replaceExpectedType(expectedType) .replaceDataFlowInfo(rightInfo.getDataFlowInfo()).replaceCallPosition(new CallPosition.PropertyAssignment(left))); - basic.checkLValue(context.trace, context, leftOperand, right); + basic.checkLValue(context.trace, context, leftOperand, right, expression); } temporary.commit(); return rightInfo.replaceType(checkAssignmentType(type, expression, contextWithExpectedType)); @@ -308,7 +309,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito KtArrayAccessExpression arrayAccessExpression = (KtArrayAccessExpression) left; if (right == null) return TypeInfoFactoryKt.noTypeInfo(context); KotlinTypeInfo typeInfo = basic.resolveArrayAccessSetMethod(arrayAccessExpression, right, context, context.trace); - basic.checkLValue(context.trace, context, arrayAccessExpression, right); + basic.checkLValue(context.trace, context, arrayAccessExpression, right, expression); return typeInfo.replaceType(checkAssignmentType(typeInfo.getType(), expression, contextWithExpectedType)); } KotlinTypeInfo leftInfo = ExpressionTypingUtils.getTypeInfoOrNullType(left, context, facade); @@ -333,7 +334,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito resultInfo = leftInfo; } if (expectedType != null && leftOperand != null) { //if expectedType == null, some other error has been generated - basic.checkLValue(context.trace, context, leftOperand, right); + basic.checkLValue(context.trace, context, leftOperand, right, expression); } return resultInfo.replaceType(components.dataFlowAnalyzer.checkStatementType(expression, contextWithExpectedType)); } diff --git a/compiler/testData/diagnostics/tests/operatorsOverloading/kt11300.kt b/compiler/testData/diagnostics/tests/operatorsOverloading/kt11300.kt new file mode 100644 index 00000000000..01c21dfca63 --- /dev/null +++ b/compiler/testData/diagnostics/tests/operatorsOverloading/kt11300.kt @@ -0,0 +1,11 @@ +class A { + operator fun get(x: Int): Int = x + fun set(x: Int, y: Int) {} // no `operator` modifier +} + +fun main(args: Array) { + val a = A() + a[1]++ + a[1] += 3 + a[1] = a[1] + 3 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/operatorsOverloading/kt11300.txt b/compiler/testData/diagnostics/tests/operatorsOverloading/kt11300.txt new file mode 100644 index 00000000000..071043791c7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/operatorsOverloading/kt11300.txt @@ -0,0 +1,12 @@ +package + +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun get(/*0*/ x: kotlin.Int): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun set(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index d966294dfca..b2e644f977e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -11856,6 +11856,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("kt11300.kt") + public void testKt11300() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/operatorsOverloading/kt11300.kt"); + doTest(fileName); + } + @TestMetadata("kt3450.kt") public void testKt3450() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/operatorsOverloading/kt3450.kt");