Resolve array access RHS always as the last argument of the call
Also do not attempt to match any of the arguments in the brackets with the last parameter of the 'set' method #KT-10633 Fixed
This commit is contained in:
+43
-20
@@ -19,10 +19,13 @@ package org.jetbrains.kotlin.resolve.calls;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
@@ -32,6 +35,7 @@ import org.jetbrains.kotlin.resolve.calls.model.*;
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -118,15 +122,18 @@ public class ValueArgumentsToParametersMapper {
|
||||
|
||||
// We saw only positioned arguments so far
|
||||
private final ProcessorState positionedOnly = new ProcessorState() {
|
||||
|
||||
private int currentParameter = 0;
|
||||
|
||||
private int numberOfParametersForPositionedArguments() {
|
||||
int size = candidateCall.getCandidateDescriptor().getValueParameters().size();
|
||||
return call.getCallType() == Call.CallType.ARRAY_SET_METHOD ? size - 1 : size;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ValueParameterDescriptor nextValueParameter() {
|
||||
List<ValueParameterDescriptor> parameters = candidateCall.getCandidateDescriptor().getValueParameters();
|
||||
if (currentParameter >= parameters.size()) return null;
|
||||
if (currentParameter >= numberOfParametersForPositionedArguments()) return null;
|
||||
|
||||
ValueParameterDescriptor head = parameters.get(currentParameter);
|
||||
ValueParameterDescriptor head = candidateCall.getCandidateDescriptor().getValueParameters().get(currentParameter);
|
||||
|
||||
// If we found a vararg parameter, we are stuck with it forever
|
||||
if (head.getVarargElementType() == null) {
|
||||
@@ -142,20 +149,27 @@ public class ValueArgumentsToParametersMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorState processPositionedArgument(@NotNull ValueArgument argument, int index) {
|
||||
ValueParameterDescriptor valueParameterDescriptor = nextValueParameter();
|
||||
public ProcessorState processPositionedArgument(@NotNull ValueArgument argument) {
|
||||
processArgument(argument, nextValueParameter());
|
||||
return positionedOnly;
|
||||
}
|
||||
|
||||
if (valueParameterDescriptor != null) {
|
||||
usedParameters.add(valueParameterDescriptor);
|
||||
putVararg(valueParameterDescriptor, argument);
|
||||
@Override
|
||||
public ProcessorState processArraySetRHS(@NotNull ValueArgument argument) {
|
||||
processArgument(argument, CollectionsKt.lastOrNull(candidateCall.getCandidateDescriptor().getValueParameters()));
|
||||
return positionedOnly;
|
||||
}
|
||||
|
||||
private void processArgument(@NotNull ValueArgument argument, @Nullable ValueParameterDescriptor parameter) {
|
||||
if (parameter != null) {
|
||||
usedParameters.add(parameter);
|
||||
putVararg(parameter, argument);
|
||||
}
|
||||
else {
|
||||
report(TOO_MANY_ARGUMENTS.on(argument.asElement(), candidateCall.getCandidateDescriptor()));
|
||||
unmappedArguments.add(argument);
|
||||
setStatus(WEAK_ERROR);
|
||||
}
|
||||
|
||||
return positionedOnly;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -220,27 +234,34 @@ public class ValueArgumentsToParametersMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorState processPositionedArgument(
|
||||
@NotNull ValueArgument argument, int index
|
||||
) {
|
||||
public ProcessorState processPositionedArgument(@NotNull ValueArgument argument) {
|
||||
report(MIXING_NAMED_AND_POSITIONED_ARGUMENTS.on(argument.asElement()));
|
||||
setStatus(WEAK_ERROR);
|
||||
unmappedArguments.add(argument);
|
||||
|
||||
return positionedThenNamed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorState processArraySetRHS(@NotNull ValueArgument argument) {
|
||||
throw new IllegalStateException("Array set RHS cannot appear after a named argument syntactically: " + argument);
|
||||
}
|
||||
};
|
||||
|
||||
public void process() {
|
||||
ProcessorState state = positionedOnly;
|
||||
boolean isArraySetMethod = call.getCallType() == Call.CallType.ARRAY_SET_METHOD;
|
||||
List<? extends ValueArgument> argumentsInParentheses = CallUtilKt.getValueArgumentsInParentheses(call);
|
||||
for (int i = 0; i < argumentsInParentheses.size(); i++) {
|
||||
ValueArgument valueArgument = argumentsInParentheses.get(i);
|
||||
for (Iterator<? extends ValueArgument> iterator = argumentsInParentheses.iterator(); iterator.hasNext(); ) {
|
||||
ValueArgument valueArgument = iterator.next();
|
||||
if (valueArgument.isNamed()) {
|
||||
state = state.processNamedArgument(valueArgument);
|
||||
}
|
||||
else if (isArraySetMethod && !iterator.hasNext()) {
|
||||
state = state.processArraySetRHS(valueArgument);
|
||||
}
|
||||
else {
|
||||
state = state.processPositionedArgument(valueArgument, i);
|
||||
state = state.processPositionedArgument(valueArgument);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,9 +362,11 @@ public class ValueArgumentsToParametersMapper {
|
||||
|
||||
private interface ProcessorState {
|
||||
ProcessorState processNamedArgument(@NotNull ValueArgument argument);
|
||||
ProcessorState processPositionedArgument(@NotNull ValueArgument argument, int index);
|
||||
}
|
||||
|
||||
ProcessorState processPositionedArgument(@NotNull ValueArgument argument);
|
||||
|
||||
ProcessorState processArraySetRHS(@NotNull ValueArgument argument);
|
||||
}
|
||||
}
|
||||
|
||||
private ValueArgumentsToParametersMapper() {}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
object A {
|
||||
operator fun set(x: Int, y: String = "y", z: Double) {
|
||||
}
|
||||
}
|
||||
|
||||
object B {
|
||||
operator fun set(x: Int, y: String = "y", z: Double = 3.14, w: Char = 'w', v: Boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
object D {
|
||||
operator fun set(x: Int, vararg y: String, z: Double) {
|
||||
}
|
||||
}
|
||||
|
||||
object Z {
|
||||
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun set() {
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A[0] = <!TYPE_MISMATCH!>""<!>
|
||||
A[0] = 2.72
|
||||
|
||||
B[0] = <!TYPE_MISMATCH!>""<!>
|
||||
B[0] = <!CONSTANT_EXPECTED_TYPE_MISMATCH!>2.72<!>
|
||||
B[0] = true
|
||||
|
||||
D[0] = <!TYPE_MISMATCH!>""<!>
|
||||
D[0] = 2.72
|
||||
|
||||
Z[<!TOO_MANY_ARGUMENTS!>0<!>] = <!TOO_MANY_ARGUMENTS!>""<!>
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package
|
||||
|
||||
public fun test(): kotlin.Unit
|
||||
|
||||
public object A {
|
||||
private constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final operator fun set(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.String = ..., /*2*/ z: kotlin.Double): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public object B {
|
||||
private constructor B()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final operator fun set(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.String = ..., /*2*/ z: kotlin.Double = ..., /*3*/ w: kotlin.Char = ..., /*4*/ v: kotlin.Boolean): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public object D {
|
||||
private constructor D()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final operator fun set(/*0*/ x: kotlin.Int, /*1*/ vararg y: kotlin.String /*kotlin.Array<out kotlin.String>*/, /*2*/ z: kotlin.Double): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public object Z {
|
||||
private constructor Z()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final operator fun set(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
class A {
|
||||
operator fun get(x: Int) {}
|
||||
operator fun set(x: String, value: Int) {}
|
||||
|
||||
fun d(x: Int) {
|
||||
this["", <!TOO_MANY_ARGUMENTS!>1<!>] = 1
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
public final class A {
|
||||
public constructor A()
|
||||
public final fun d(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final operator fun get(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final operator fun set(/*0*/ x: kotlin.String, /*1*/ value: kotlin.Int): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
var count = 0
|
||||
|
||||
operator fun Int.get(s: Int): Int {
|
||||
count++
|
||||
return this + s
|
||||
}
|
||||
|
||||
operator fun Int.set(s: Int, x: String = "", z: Int) {
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
1[2] = 1
|
||||
1.set(2, z = 1)
|
||||
1[2] += 1
|
||||
|
||||
1.set(2, <!CONSTANT_EXPECTED_TYPE_MISMATCH!>1<!><!NO_VALUE_FOR_PARAMETER!>)<!>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package
|
||||
|
||||
public var count: kotlin.Int
|
||||
public fun main(/*0*/ args: kotlin.Array<kotlin.String>): kotlin.Unit
|
||||
public operator fun kotlin.Int.get(/*0*/ s: kotlin.Int): kotlin.Int
|
||||
public operator fun kotlin.Int.set(/*0*/ s: kotlin.Int, /*1*/ x: kotlin.String = ..., /*2*/ z: kotlin.Int): kotlin.Unit
|
||||
@@ -2604,6 +2604,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/checkArguments"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayAccessSet.kt")
|
||||
public void testArrayAccessSet() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/checkArguments/arrayAccessSet.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayAccessSetTooManyArgs.kt")
|
||||
public void testArrayAccessSetTooManyArgs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/checkArguments/arrayAccessSetTooManyArgs.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("booleanExpressions.kt")
|
||||
public void testBooleanExpressions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/checkArguments/booleanExpressions.kt");
|
||||
@@ -13077,6 +13089,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt10633.kt")
|
||||
public void testKt10633() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/regressions/kt10633.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt127.kt")
|
||||
public void testKt127() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/regressions/kt127.kt");
|
||||
|
||||
Reference in New Issue
Block a user