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");
|
||||
|
||||
+22
-12
@@ -313,14 +313,19 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
|
||||
return SignatureInfo(overload, { null }, null, isGrey = false)
|
||||
}
|
||||
|
||||
assert(call.valueArguments.size >= currentArgumentIndex)
|
||||
val isArraySetMethod = call.callType == Call.CallType.ARRAY_SET_METHOD
|
||||
|
||||
val argumentsBeforeCurrent = call.valueArguments.subList(0, currentArgumentIndex)
|
||||
val arguments = call.valueArguments.let { args ->
|
||||
// For array set method call, we're only interested in the arguments in brackets which are all except the last one
|
||||
if (isArraySetMethod) args.dropLast(1) else args
|
||||
}
|
||||
|
||||
assert(arguments.size >= currentArgumentIndex)
|
||||
|
||||
val callToUse: Call
|
||||
val currentArgument: ValueArgument
|
||||
if (call.valueArguments.size > currentArgumentIndex) {
|
||||
currentArgument = call.valueArguments[currentArgumentIndex]
|
||||
if (arguments.size > currentArgumentIndex) {
|
||||
currentArgument = arguments[currentArgumentIndex]
|
||||
callToUse = call
|
||||
}
|
||||
else {
|
||||
@@ -334,9 +339,12 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
|
||||
override fun isExternal() = false
|
||||
}
|
||||
callToUse = object : DelegatingCall(call) {
|
||||
val arguments = call.valueArguments + currentArgument
|
||||
val argumentsWithCurrent =
|
||||
arguments + currentArgument +
|
||||
// For array set method call, also add the argument in the right-hand side
|
||||
(if (isArraySetMethod) listOf(call.valueArguments.last()) else listOf())
|
||||
|
||||
override fun getValueArguments() = arguments
|
||||
override fun getValueArguments() = argumentsWithCurrent
|
||||
override fun getFunctionLiteralArguments() = emptyList<LambdaArgument>()
|
||||
override fun getValueArgumentList() = null
|
||||
}
|
||||
@@ -347,20 +355,22 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
|
||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||
|
||||
fun argumentToParameter(argument: ValueArgument): ValueParameterDescriptor? {
|
||||
val parameter = (resolvedCall.getArgumentMapping(argument) as? ArgumentMatch)?.valueParameter ?: return null
|
||||
if (call.callType == Call.CallType.ARRAY_SET_METHOD && parameter.index == resultingDescriptor.valueParameters.lastIndex) return null
|
||||
return parameter
|
||||
return (resolvedCall.getArgumentMapping(argument) as? ArgumentMatch)?.valueParameter
|
||||
}
|
||||
|
||||
val highlightParameterIndex = argumentToParameter(currentArgument)?.index
|
||||
|
||||
if (!(argumentsBeforeCurrent + currentArgument).all { argumentToParameter(it) != null }) { // some of arguments before the current one (or the current one) are not mapped to any of the parameters
|
||||
val argumentsBeforeCurrent = arguments.subList(0, currentArgumentIndex)
|
||||
if ((argumentsBeforeCurrent + currentArgument).any { argumentToParameter(it) == null }) {
|
||||
// some of arguments before the current one (or the current one) are not mapped to any of the parameters
|
||||
return SignatureInfo(resultingDescriptor, ::argumentToParameter, highlightParameterIndex, isGrey = true)
|
||||
}
|
||||
|
||||
// grey out if not all arguments before the current are matched
|
||||
val isGrey = argumentsBeforeCurrent
|
||||
.any { argument -> resolvedCall.getArgumentMapping(argument).isError() && !argument.hasError(bindingContext) /* ignore arguments that has error type */ }
|
||||
val isGrey = argumentsBeforeCurrent.any { argument ->
|
||||
resolvedCall.getArgumentMapping(argument).isError() &&
|
||||
!argument.hasError(bindingContext) /* ignore arguments that have error type */
|
||||
}
|
||||
return SignatureInfo(resultingDescriptor, ::argumentToParameter, highlightParameterIndex, isGrey)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user