org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMapper Maven / Gradle / Ivy
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.calls;
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.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.ReservedCheckingKt;
import org.jetbrains.kotlin.resolve.OverrideResolver;
import org.jetbrains.kotlin.resolve.calls.util.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.components.ArgumentsUtilsKt;
import org.jetbrains.kotlin.resolve.calls.model.*;
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
import java.util.*;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.*;
import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
import static org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMapper.Status.*;
public class ValueArgumentsToParametersMapper {
public enum Status {
ERROR(false),
WEAK_ERROR(false),
OK(true);
private final boolean success;
Status(boolean success) {
this.success = success;
}
public boolean isSuccess() {
return success;
}
public Status compose(Status other) {
if (this == ERROR || other == ERROR) return ERROR;
if (this == WEAK_ERROR || other == WEAK_ERROR) return WEAK_ERROR;
return this;
}
}
public static Status mapValueArgumentsToParameters(
@NotNull Call call,
@NotNull TracingStrategy tracing,
@NotNull MutableResolvedCall candidateCall,
@NotNull LanguageVersionSettings languageVersionSettings
) {
//return new ValueArgumentsToParametersMapper().process(call, tracing, candidateCall, unmappedArguments);
Processor processor = new Processor<>(call, candidateCall, tracing, languageVersionSettings);
processor.process();
return processor.status;
}
private static class Processor {
private final Call call;
private final TracingStrategy tracing;
private final MutableResolvedCall candidateCall;
private final LanguageVersionSettings languageVersionSettings;
private final List parameters;
private final Map parameterByName;
private Map parameterByNameInOverriddenMethods;
private final Map varargs = new HashMap<>();
private final Set usedParameters = new HashSet<>();
private Status status = OK;
private Processor(
@NotNull Call call,
@NotNull MutableResolvedCall candidateCall,
@NotNull TracingStrategy tracing,
@NotNull LanguageVersionSettings languageVersionSettings
) {
this.call = call;
this.tracing = tracing;
this.candidateCall = candidateCall;
this.parameters = candidateCall.getCandidateDescriptor().getValueParameters();
this.languageVersionSettings = languageVersionSettings;
this.parameterByName = new HashMap<>();
for (ValueParameterDescriptor valueParameter : parameters) {
parameterByName.put(valueParameter.getName(), valueParameter);
}
}
@Nullable
private ValueParameterDescriptor getParameterByNameInOverriddenMethods(Name name) {
if (parameterByNameInOverriddenMethods == null) {
parameterByNameInOverriddenMethods = new HashMap<>();
for (ValueParameterDescriptor valueParameter : parameters) {
for (ValueParameterDescriptor parameterDescriptor : valueParameter.getOverriddenDescriptors()) {
parameterByNameInOverriddenMethods.put(parameterDescriptor.getName(), valueParameter);
}
}
}
return parameterByNameInOverriddenMethods.get(name);
}
// We saw only positioned arguments so far
private final ProcessorState positionedOnly = new ProcessorState() {
private int currentParameter = 0;
private int numberOfParametersForPositionedArguments() {
return call.getCallType() == Call.CallType.ARRAY_SET_METHOD ? parameters.size() - 1 : parameters.size();
}
@Nullable
public ValueParameterDescriptor nextValueParameter() {
if (currentParameter >= numberOfParametersForPositionedArguments()) return null;
ValueParameterDescriptor head = parameters.get(currentParameter);
// If we found a vararg parameter, we are stuck with it forever
if (head.getVarargElementType() == null) {
currentParameter++;
}
return head;
}
@Override
public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
return positionedThenNamed.processNamedArgument(argument);
}
@Override
public ProcessorState processPositionedArgument(@NotNull ValueArgument argument) {
processArgument(argument, nextValueParameter());
return positionedOnly;
}
@Override
public ProcessorState processArraySetRHS(@NotNull ValueArgument argument) {
processArgument(argument, CollectionsKt.lastOrNull(parameters));
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()));
setStatus(WEAK_ERROR);
}
}
};
// We saw zero or more positioned arguments and then a named one
private final ProcessorState positionedThenNamed = new ProcessorState() {
@Override
public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
assert argument.isNamed();
D candidate = candidateCall.getCandidateDescriptor();
ValueArgumentName argumentName = argument.getArgumentName();
assert argumentName != null;
ValueParameterDescriptor valueParameterDescriptor = parameterByName.get(argumentName.getAsName());
KtSimpleNameExpression nameReference = argumentName.getReferenceExpression();
if (!languageVersionSettings.supportsFeature(LanguageFeature.YieldIsNoMoreReserved)) {
ReservedCheckingKt.checkReservedYield(nameReference, candidateCall.getTrace());
}
if (nameReference != null) {
if (candidate instanceof MemberDescriptor && ((MemberDescriptor) candidate).isExpect() &&
candidate.getContainingDeclaration() instanceof ClassDescriptor) {
// We do not allow named arguments for members of expected classes until we're able to use both
// expected and actual definitions when compiling platform code
report(NAMED_ARGUMENTS_NOT_ALLOWED.on(nameReference, EXPECTED_CLASS_MEMBER));
}
else if (!candidate.hasStableParameterNames()) {
BadNamedArgumentsTarget badNamedArgumentsTarget;
if (candidate instanceof FunctionInvokeDescriptor) {
badNamedArgumentsTarget = INVOKE_ON_FUNCTION_TYPE;
} else if (candidate instanceof DeserializedCallableMemberDescriptor) {
badNamedArgumentsTarget = INTEROP_FUNCTION;
} else {
badNamedArgumentsTarget = NON_KOTLIN_FUNCTION;
}
report(NAMED_ARGUMENTS_NOT_ALLOWED.on(nameReference, badNamedArgumentsTarget));
}
}
if (candidate.hasStableParameterNames() && nameReference != null &&
candidate instanceof CallableMemberDescriptor && ((CallableMemberDescriptor)candidate).getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
if (valueParameterDescriptor == null) {
valueParameterDescriptor = getParameterByNameInOverriddenMethods(argumentName.getAsName());
}
if (valueParameterDescriptor != null) {
for (ValueParameterDescriptor parameterFromSuperclass : valueParameterDescriptor.getOverriddenDescriptors()) {
if (OverrideResolver.Companion.shouldReportParameterNameOverrideWarning(valueParameterDescriptor, parameterFromSuperclass)) {
report(NAME_FOR_AMBIGUOUS_PARAMETER.on(nameReference));
}
}
}
}
if (valueParameterDescriptor == null) {
if (nameReference != null) {
report(NAMED_PARAMETER_NOT_FOUND.on(nameReference, nameReference));
}
setStatus(WEAK_ERROR);
}
else {
if (nameReference != null) {
candidateCall.getTrace().record(REFERENCE_TARGET, nameReference, valueParameterDescriptor);
}
if (!usedParameters.add(valueParameterDescriptor)) {
if (nameReference != null) {
report(ARGUMENT_PASSED_TWICE.on(nameReference));
}
setStatus(WEAK_ERROR);
}
else {
putVararg(valueParameterDescriptor, argument);
}
}
return positionedThenNamed;
}
@Override
public ProcessorState processPositionedArgument(@NotNull ValueArgument argument) {
report(MIXING_NAMED_AND_POSITIONED_ARGUMENTS.on(argument.asElement()));
setStatus(WEAK_ERROR);
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 (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);
}
}
for (Map.Entry entry : varargs.entrySet()) {
candidateCall.recordValueArgument(entry.getKey(), entry.getValue());
}
processFunctionLiteralArguments();
reportUnmappedParameters();
}
private void processFunctionLiteralArguments() {
List extends LambdaArgument> functionLiteralArguments = call.getFunctionLiteralArguments();
if (functionLiteralArguments.isEmpty()) return;
LambdaArgument lambdaArgument = functionLiteralArguments.get(0);
KtExpression possiblyLabeledFunctionLiteral = lambdaArgument.getArgumentExpression();
if (parameters.isEmpty()) {
CallUtilKt.reportTrailingLambdaErrorOr(
candidateCall.getTrace(), possiblyLabeledFunctionLiteral,
expression -> TOO_MANY_ARGUMENTS.on(expression, candidateCall.getCandidateDescriptor())
);
setStatus(ERROR);
}
else {
ValueParameterDescriptor lastParameter = CollectionsKt.last(parameters);
if (lastParameter.getVarargElementType() != null) {
CallUtilKt.reportTrailingLambdaErrorOr(
candidateCall.getTrace(), possiblyLabeledFunctionLiteral,
expression -> VARARG_OUTSIDE_PARENTHESES.on(expression)
);
setStatus(ERROR);
}
else if (!usedParameters.add(lastParameter)) {
CallUtilKt.reportTrailingLambdaErrorOr(
candidateCall.getTrace(), possiblyLabeledFunctionLiteral,
expr -> TOO_MANY_ARGUMENTS.on(expr, candidateCall.getCandidateDescriptor())
);
setStatus(WEAK_ERROR);
}
else {
putVararg(lastParameter, lambdaArgument);
}
}
for (int i = 1; i < functionLiteralArguments.size(); i++) {
KtExpression argument = functionLiteralArguments.get(i).getArgumentExpression();
if (argument instanceof KtLambdaExpression) {
report(MANY_LAMBDA_EXPRESSION_ARGUMENTS.on(argument));
if (CallUtilKt.isTrailingLambdaOnNewLIne((KtLambdaExpression) argument)) {
report(UNEXPECTED_TRAILING_LAMBDA_ON_A_NEW_LINE.on((KtLambdaExpression) argument));
}
}
setStatus(WEAK_ERROR);
}
}
private void reportUnmappedParameters() {
for (ValueParameterDescriptor valueParameter : parameters) {
if (!usedParameters.contains(valueParameter)) {
if (ArgumentsUtilsKt.hasDefaultValue(valueParameter)) {
candidateCall.recordValueArgument(valueParameter, DefaultValueArgument.DEFAULT);
}
else if (valueParameter.getVarargElementType() != null) {
candidateCall.recordValueArgument(valueParameter, new VarargValueArgument());
}
else {
tracing.noValueForParameter(candidateCall.getTrace(), valueParameter);
setStatus(ERROR);
}
}
}
}
private void putVararg(ValueParameterDescriptor valueParameterDescriptor, ValueArgument valueArgument) {
if (valueParameterDescriptor.getVarargElementType() != null) {
VarargValueArgument vararg = varargs.computeIfAbsent(valueParameterDescriptor, k -> new VarargValueArgument());
vararg.addArgument(valueArgument);
}
else {
LeafPsiElement spread = valueArgument.getSpreadElement();
if (spread != null) {
candidateCall.getTrace().report(NON_VARARG_SPREAD.onError(spread));
setStatus(WEAK_ERROR);
}
ResolvedValueArgument argument = new ExpressionValueArgument(valueArgument);
candidateCall.recordValueArgument(valueParameterDescriptor, argument);
}
}
private void setStatus(@NotNull Status newStatus) {
status = status.compose(newStatus);
}
private void report(Diagnostic diagnostic) {
candidateCall.getTrace().report(diagnostic);
}
private interface ProcessorState {
ProcessorState processNamedArgument(@NotNull ValueArgument argument);
ProcessorState processPositionedArgument(@NotNull ValueArgument argument);
ProcessorState processArraySetRHS(@NotNull ValueArgument argument);
}
}
private ValueArgumentsToParametersMapper() {}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy