Fix "No error on increment or augmented assignment when 'get' is an operator but 'set' is not" #KT-11300
This commit is contained in:
+18
-3
@@ -736,7 +736,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression);
|
context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression);
|
||||||
KtExpression stubExpression = ExpressionTypingUtils.createFakeExpressionOfType(
|
KtExpression stubExpression = ExpressionTypingUtils.createFakeExpressionOfType(
|
||||||
baseExpression.getProject(), context.trace, "e", type);
|
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
|
// x++ type is x type, but ++x type is x.inc() type
|
||||||
DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(
|
DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(
|
||||||
@@ -854,9 +854,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
@NotNull BindingTrace trace,
|
@NotNull BindingTrace trace,
|
||||||
@NotNull ExpressionTypingContext context,
|
@NotNull ExpressionTypingContext context,
|
||||||
@NotNull KtExpression expression,
|
@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(
|
private boolean checkLValue(
|
||||||
@@ -864,6 +865,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
@NotNull ExpressionTypingContext context,
|
@NotNull ExpressionTypingContext context,
|
||||||
@NotNull KtExpression expressionWithParenthesis,
|
@NotNull KtExpression expressionWithParenthesis,
|
||||||
@Nullable KtExpression rightHandSide,
|
@Nullable KtExpression rightHandSide,
|
||||||
|
@NotNull KtOperationExpression operationExpression,
|
||||||
boolean canBeThis
|
boolean canBeThis
|
||||||
) {
|
) {
|
||||||
KtExpression expression = KtPsiUtil.deparenthesize(expressionWithParenthesis);
|
KtExpression expression = KtPsiUtil.deparenthesize(expressionWithParenthesis);
|
||||||
@@ -875,6 +877,19 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
TemporaryBindingTrace ignoreReportsTrace = TemporaryBindingTrace.create(trace, "Trace for checking set function");
|
TemporaryBindingTrace ignoreReportsTrace = TemporaryBindingTrace.create(trace, "Trace for checking set function");
|
||||||
ExpressionTypingContext findSetterContext = context.replaceBindingTrace(ignoreReportsTrace);
|
ExpressionTypingContext findSetterContext = context.replaceBindingTrace(ignoreReportsTrace);
|
||||||
KotlinTypeInfo info = resolveArrayAccessSetMethod(arrayAccessExpression, rightHandSide, findSetterContext, 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;
|
return info.getType() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -219,7 +219,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
TemporaryTraceAndCache temporaryForBinaryOperation = TemporaryTraceAndCache.create(
|
TemporaryTraceAndCache temporaryForBinaryOperation = TemporaryTraceAndCache.create(
|
||||||
context, "trace to check binary operation like '+' for", expression);
|
context, "trace to check binary operation like '+' for", expression);
|
||||||
TemporaryBindingTrace ignoreReportsTrace = TemporaryBindingTrace.create(context.trace, "Trace for checking assignability");
|
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) {
|
if (assignmentOperationType == null || lhsAssignable) {
|
||||||
// Check for '+'
|
// Check for '+'
|
||||||
Name counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType));
|
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),
|
context.replaceTraceAndCache(temporaryForBinaryOperation).replaceScope(scope),
|
||||||
receiver, expression, counterpartName
|
receiver, expression, counterpartName
|
||||||
);
|
);
|
||||||
|
|
||||||
binaryOperationType = OverloadResolutionResultsUtil.getResultingType(binaryOperationDescriptors, context.contextDependency);
|
binaryOperationType = OverloadResolutionResultsUtil.getResultingType(binaryOperationDescriptors, context.contextDependency);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -269,7 +270,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
|
|
||||||
components.dataFlowAnalyzer.checkType(binaryOperationType, expression, context.replaceExpectedType(expectedType)
|
components.dataFlowAnalyzer.checkType(binaryOperationType, expression, context.replaceExpectedType(expectedType)
|
||||||
.replaceDataFlowInfo(rightInfo.getDataFlowInfo()).replaceCallPosition(new CallPosition.PropertyAssignment(left)));
|
.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();
|
temporary.commit();
|
||||||
return rightInfo.replaceType(checkAssignmentType(type, expression, contextWithExpectedType));
|
return rightInfo.replaceType(checkAssignmentType(type, expression, contextWithExpectedType));
|
||||||
@@ -308,7 +309,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
KtArrayAccessExpression arrayAccessExpression = (KtArrayAccessExpression) left;
|
KtArrayAccessExpression arrayAccessExpression = (KtArrayAccessExpression) left;
|
||||||
if (right == null) return TypeInfoFactoryKt.noTypeInfo(context);
|
if (right == null) return TypeInfoFactoryKt.noTypeInfo(context);
|
||||||
KotlinTypeInfo typeInfo = basic.resolveArrayAccessSetMethod(arrayAccessExpression, right, context, context.trace);
|
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));
|
return typeInfo.replaceType(checkAssignmentType(typeInfo.getType(), expression, contextWithExpectedType));
|
||||||
}
|
}
|
||||||
KotlinTypeInfo leftInfo = ExpressionTypingUtils.getTypeInfoOrNullType(left, context, facade);
|
KotlinTypeInfo leftInfo = ExpressionTypingUtils.getTypeInfoOrNullType(left, context, facade);
|
||||||
@@ -333,7 +334,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
resultInfo = leftInfo;
|
resultInfo = leftInfo;
|
||||||
}
|
}
|
||||||
if (expectedType != null && leftOperand != null) { //if expectedType == null, some other error has been generated
|
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));
|
return resultInfo.replaceType(components.dataFlowAnalyzer.checkStatementType(expression, contextWithExpectedType));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class A {
|
||||||
|
operator fun get(x: Int): Int = x
|
||||||
|
fun set(<!UNUSED_PARAMETER!>x<!>: Int, <!UNUSED_PARAMETER!>y<!>: Int) {} // no `operator` modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val a = A()
|
||||||
|
<!OPERATOR_MODIFIER_REQUIRED!>a[1]<!>++
|
||||||
|
<!OPERATOR_MODIFIER_REQUIRED!>a[1]<!> += 3
|
||||||
|
<!OPERATOR_MODIFIER_REQUIRED!>a[1]<!> = a[1] + 3
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun main(/*0*/ args: kotlin.Array<kotlin.String>): 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
|
||||||
|
}
|
||||||
@@ -11856,6 +11856,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("kt3450.kt")
|
||||||
public void testKt3450() throws Exception {
|
public void testKt3450() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/operatorsOverloading/kt3450.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/operatorsOverloading/kt3450.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user