Logic refactored to a finite automaton

This commit is contained in:
Andrey Breslav
2013-01-30 20:59:19 +04:00
parent fdde9b9c49
commit 8fd59feb21
@@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor; import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace; import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace;
import org.jetbrains.jet.lang.resolve.calls.model.*; import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy; import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy;
@@ -66,246 +67,295 @@ import static org.jetbrains.jet.lang.resolve.calls.ValueArgumentsToParametersMap
return this; return this;
} }
} }
public static <D extends CallableDescriptor> Status mapValueArgumentsToParameters( public static <D extends CallableDescriptor> Status mapValueArgumentsToParameters(
@NotNull Call call, @NotNull Call call,
@NotNull TracingStrategy tracing, @NotNull TracingStrategy tracing,
@NotNull ResolvedCallImpl<D> candidateCall, @NotNull ResolvedCallImpl<D> candidateCall,
@NotNull Set<ValueArgument> unmappedArguments @NotNull Set<ValueArgument> unmappedArguments
) { ) {
Map<ValueParameterDescriptor, VarargValueArgument> varargs = Maps.newHashMap(); //return new ValueArgumentsToParametersMapper().process(call, tracing, candidateCall, unmappedArguments);
Set<ValueParameterDescriptor> usedParameters = Sets.newHashSet(); Processor<D> processor = new Processor<D>(call, candidateCall, tracing);
processor.process();
Status status = processArgumentsInParens(call, candidateCall, unmappedArguments, varargs, usedParameters); unmappedArguments.addAll(processor.unmappedArguments);
return processor.status;
status = processFunctionLiteralArguments(call, candidateCall, varargs, usedParameters, status);
status = reportUnusedParameters(tracing, candidateCall, usedParameters, status);
status = checkReceiverArgument(call, tracing, candidateCall, status);
assert (candidateCall.getThisObject().exists() == (candidateCall.getResultingDescriptor().getExpectedThisObject() != null))
: "Shouldn't happen because of TaskPrioritizer: " + candidateCall.getCandidateDescriptor();
return status;
} }
private static <D extends CallableDescriptor> Status processArgumentsInParens( private static class Processor<D extends CallableDescriptor> {
Call call, private final Call call;
ResolvedCallImpl<D> candidateCall, private final TracingStrategy tracing;
Set<ValueArgument> unmappedArguments, private final ResolvedCallImpl<D> candidateCall;
Map<ValueParameterDescriptor, VarargValueArgument> varargs,
Set<ValueParameterDescriptor> usedParameters
) {
DelegatingBindingTrace traceForCall = candidateCall.getTrace();
D candidate = candidateCall.getCandidateDescriptor();
List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters();
Map<Name, ValueParameterDescriptor> parameterByName = Maps.newHashMap(); private final Map<Name,ValueParameterDescriptor> parameterByName;
for (ValueParameterDescriptor valueParameter : valueParameters) {
parameterByName.put(valueParameter.getName(), valueParameter); private final Set<ValueArgument> unmappedArguments = Sets.newHashSet();
private final Map<ValueParameterDescriptor, VarargValueArgument> varargs = Maps.newHashMap();
private final Set<ValueParameterDescriptor> usedParameters = Sets.newHashSet();
private Status status = OK;
private Processor(@NotNull Call call, @NotNull ResolvedCallImpl<D> candidateCall, @NotNull TracingStrategy tracing) {
this.call = call;
this.tracing = tracing;
this.candidateCall = candidateCall;
this.parameterByName = Maps.newHashMap();
for (ValueParameterDescriptor valueParameter : candidateCall.getCandidateDescriptor().getValueParameters()) {
parameterByName.put(valueParameter.getName(), valueParameter);
}
} }
List<? extends ValueArgument> valueArguments = call.getValueArguments(); private final ProcessorState initial = new ProcessorState() {
Status status = OK; @Override
boolean someNamed = false; public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
boolean somePositioned = false; return namedOnly.processNamedArgument(argument);
for (int i = 0; i < valueArguments.size(); i++) { }
ValueArgument valueArgument = valueArguments.get(i);
if (valueArgument.isNamed()) { @Override
someNamed = true; public ProcessorState processPositionedArgument(
JetSimpleNameExpression nameReference = valueArgument.getArgumentName().getReferenceExpression(); @NotNull ValueArgument argument, int index
) {
return positionedOnly.processPositionedArgument(argument, index);
}
};
private final ProcessorState positionedOnly = new ProcessorState() {
@Override
public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
return error.processNamedArgument(argument);
}
@Override
public ProcessorState processPositionedArgument(@NotNull ValueArgument argument, int index) {
BindingTrace traceForCall = candidateCall.getTrace();
D candidate = candidateCall.getCandidateDescriptor();
List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters();
int parameterCount = valueParameters.size();
if (index < parameterCount) {
ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(index);
usedParameters.add(valueParameterDescriptor);
putVararg(valueParameterDescriptor, argument);
}
else if (!valueParameters.isEmpty()) {
ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(valueParameters.size() - 1);
if (valueParameterDescriptor.getVarargElementType() != null) {
putVararg(valueParameterDescriptor, argument);
usedParameters.add(valueParameterDescriptor);
}
else {
traceForCall.report(TOO_MANY_ARGUMENTS.on(argument.asElement(), candidate));
unmappedArguments.add(argument);
setStatus(WEAK_ERROR);
}
}
else {
traceForCall.report(TOO_MANY_ARGUMENTS.on(argument.asElement(), candidate));
unmappedArguments.add(argument);
setStatus(ERROR);
}
return positionedOnly;
}
};
private final ProcessorState namedOnly = new ProcessorState() {
@Override
public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
assert argument.isNamed();
DelegatingBindingTrace traceForCall = candidateCall.getTrace();
JetSimpleNameExpression nameReference = argument.getArgumentName().getReferenceExpression();
ValueParameterDescriptor valueParameterDescriptor = parameterByName.get(nameReference.getReferencedNameAsName()); ValueParameterDescriptor valueParameterDescriptor = parameterByName.get(nameReference.getReferencedNameAsName());
if (valueParameterDescriptor == null) { if (valueParameterDescriptor == null) {
traceForCall.report(NAMED_PARAMETER_NOT_FOUND.on(nameReference)); traceForCall.report(NAMED_PARAMETER_NOT_FOUND.on(nameReference));
unmappedArguments.add(valueArgument); unmappedArguments.add(argument);
status = WEAK_ERROR; setStatus(WEAK_ERROR);
} }
else { else {
traceForCall.record(REFERENCE_TARGET, nameReference, valueParameterDescriptor); traceForCall.record(REFERENCE_TARGET, nameReference, valueParameterDescriptor);
if (!usedParameters.add(valueParameterDescriptor)) { if (!usedParameters.add(valueParameterDescriptor)) {
traceForCall.report(ARGUMENT_PASSED_TWICE.on(nameReference)); traceForCall.report(ARGUMENT_PASSED_TWICE.on(nameReference));
unmappedArguments.add(valueArgument); unmappedArguments.add(argument);
status = WEAK_ERROR; setStatus(WEAK_ERROR);
} }
else { else {
status = status.compose(put(candidateCall, valueParameterDescriptor, valueArgument, varargs)); putVararg(valueParameterDescriptor, argument);
} }
} }
if (somePositioned) {
traceForCall.report(MIXING_NAMED_AND_POSITIONED_ARGUMENTS.on(nameReference)); return namedOnly;
status = WEAK_ERROR;
}
} }
else {
somePositioned = true; @Override
if (someNamed) { public ProcessorState processPositionedArgument(
traceForCall.report(MIXING_NAMED_AND_POSITIONED_ARGUMENTS.on(valueArgument.asElement())); @NotNull ValueArgument argument, int index
status = WEAK_ERROR; ) {
return error.processPositionedArgument(argument, index);
}
};
private final ProcessorState error = new ProcessorState() {
@Override
public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
namedOnly.processNamedArgument(argument);
candidateCall.getTrace().report(MIXING_NAMED_AND_POSITIONED_ARGUMENTS.on(argument.getArgumentName()));
setStatus(WEAK_ERROR);
return error;
}
@Override
public ProcessorState processPositionedArgument(
@NotNull ValueArgument argument, int index
) {
candidateCall.getTrace().report(MIXING_NAMED_AND_POSITIONED_ARGUMENTS.on(argument.asElement()));
setStatus(WEAK_ERROR);
return error;
}
};
public void process() {
ProcessorState state = initial;
List<? extends ValueArgument> arguments = call.getValueArguments();
for (int i = 0; i < arguments.size(); i++) {
ValueArgument valueArgument = arguments.get(i);
if (valueArgument.isNamed()) {
state = state.processNamedArgument(valueArgument);
} }
else { else {
int parameterCount = valueParameters.size(); state = state.processPositionedArgument(valueArgument, i);
if (i < parameterCount) { }
ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(i); }
usedParameters.add(valueParameterDescriptor);
status = status.compose(put(candidateCall, valueParameterDescriptor, valueArgument, varargs)); processFunctionLiteralArguments();
reportUnmappedParameters();
checkReceiverArgument();
assert (candidateCall.getThisObject().exists() == (candidateCall.getResultingDescriptor().getExpectedThisObject() != null))
: "Shouldn't happen because of TaskPrioritizer: " + candidateCall.getCandidateDescriptor();
}
private void processFunctionLiteralArguments() {
D candidate = candidateCall.getCandidateDescriptor();
DelegatingBindingTrace traceForCall = candidateCall.getTrace();
List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters();
List<JetExpression> functionLiteralArguments = call.getFunctionLiteralArguments();
if (!functionLiteralArguments.isEmpty()) {
JetExpression possiblyLabeledFunctionLiteral = functionLiteralArguments.get(0);
if (valueParameters.isEmpty()) {
traceForCall.report(TOO_MANY_ARGUMENTS.on(possiblyLabeledFunctionLiteral, candidate));
setStatus(ERROR);
}
else {
JetFunctionLiteralExpression functionLiteral;
if (possiblyLabeledFunctionLiteral instanceof JetLabelQualifiedExpression) {
JetLabelQualifiedExpression labeledFunctionLiteral = (JetLabelQualifiedExpression) possiblyLabeledFunctionLiteral;
functionLiteral = (JetFunctionLiteralExpression) labeledFunctionLiteral.getLabeledExpression();
} }
else if (!valueParameters.isEmpty()) { else {
ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(valueParameters.size() - 1); functionLiteral = (JetFunctionLiteralExpression) possiblyLabeledFunctionLiteral;
if (valueParameterDescriptor.getVarargElementType() != null) { }
status = status.compose(put(candidateCall, valueParameterDescriptor, valueArgument, varargs));
usedParameters.add(valueParameterDescriptor); ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(valueParameters.size() - 1);
if (valueParameterDescriptor.getVarargElementType() != null) {
traceForCall.report(VARARG_OUTSIDE_PARENTHESES.on(possiblyLabeledFunctionLiteral));
setStatus(ERROR);
}
else {
if (!usedParameters.add(valueParameterDescriptor)) {
traceForCall.report(TOO_MANY_ARGUMENTS.on(possiblyLabeledFunctionLiteral, candidate));
setStatus(WEAK_ERROR);
} }
else { else {
traceForCall.report(TOO_MANY_ARGUMENTS.on(valueArgument.asElement(), candidate)); putVararg(valueParameterDescriptor, CallMaker.makeValueArgument(functionLiteral));
unmappedArguments.add(valueArgument);
status = WEAK_ERROR;
} }
} }
else { }
traceForCall.report(TOO_MANY_ARGUMENTS.on(valueArgument.asElement(), candidate));
unmappedArguments.add(valueArgument); for (int i = 1; i < functionLiteralArguments.size(); i++) {
status = ERROR; JetExpression argument = functionLiteralArguments.get(i);
} traceForCall.report(MANY_FUNCTION_LITERAL_ARGUMENTS.on(argument));
setStatus(WEAK_ERROR);
} }
} }
} }
return status;
}
private static <D extends CallableDescriptor> Status processFunctionLiteralArguments( private void reportUnmappedParameters() {
Call call, DelegatingBindingTrace traceForCall = candidateCall.getTrace();
ResolvedCallImpl<D> candidateCall,
Map<ValueParameterDescriptor, VarargValueArgument> varargs,
Set<ValueParameterDescriptor> usedParameters,
Status status
) {
D candidate = candidateCall.getCandidateDescriptor();
DelegatingBindingTrace traceForCall = candidateCall.getTrace();
List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters();
List<JetExpression> functionLiteralArguments = call.getFunctionLiteralArguments(); List<ValueParameterDescriptor> valueParameters = candidateCall.getCandidateDescriptor().getValueParameters();
if (!functionLiteralArguments.isEmpty()) { for (ValueParameterDescriptor valueParameter : valueParameters) {
JetExpression possiblyLabeledFunctionLiteral = functionLiteralArguments.get(0); if (!usedParameters.contains(valueParameter)) {
if (valueParameter.hasDefaultValue()) {
if (valueParameters.isEmpty()) { candidateCall.recordValueArgument(valueParameter, DefaultValueArgument.DEFAULT);
traceForCall.report(TOO_MANY_ARGUMENTS.on(possiblyLabeledFunctionLiteral, candidate)); }
status = ERROR; else if (valueParameter.getVarargElementType() != null) {
} candidateCall.recordValueArgument(valueParameter, new VarargValueArgument());
else {
JetFunctionLiteralExpression functionLiteral;
if (possiblyLabeledFunctionLiteral instanceof JetLabelQualifiedExpression) {
JetLabelQualifiedExpression labeledFunctionLiteral = (JetLabelQualifiedExpression) possiblyLabeledFunctionLiteral;
functionLiteral = (JetFunctionLiteralExpression) labeledFunctionLiteral.getLabeledExpression();
}
else {
functionLiteral = (JetFunctionLiteralExpression) possiblyLabeledFunctionLiteral;
}
ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(valueParameters.size() - 1);
if (valueParameterDescriptor.getVarargElementType() != null) {
traceForCall.report(VARARG_OUTSIDE_PARENTHESES.on(possiblyLabeledFunctionLiteral));
status = ERROR;
}
else {
if (!usedParameters.add(valueParameterDescriptor)) {
traceForCall.report(TOO_MANY_ARGUMENTS.on(possiblyLabeledFunctionLiteral, candidate));
status = WEAK_ERROR;
} }
else { else {
status = status.compose(put(candidateCall, valueParameterDescriptor, CallMaker.makeValueArgument(functionLiteral), varargs)); tracing.noValueForParameter(traceForCall, valueParameter);
setStatus(ERROR);
} }
} }
} }
for (int i = 1; i < functionLiteralArguments.size(); i++) {
JetExpression argument = functionLiteralArguments.get(i);
traceForCall.report(MANY_FUNCTION_LITERAL_ARGUMENTS.on(argument));
status = WEAK_ERROR;
}
} }
return status;
}
private static <D extends CallableDescriptor> Status reportUnusedParameters( private void checkReceiverArgument() {
TracingStrategy tracing, DelegatingBindingTrace traceForCall = candidateCall.getTrace();
ResolvedCallImpl<D> candidateCall, D candidate = candidateCall.getCandidateDescriptor();
Set<ValueParameterDescriptor> usedParameters,
Status status
) {
DelegatingBindingTrace traceForCall = candidateCall.getTrace();
List<ValueParameterDescriptor> valueParameters = candidateCall.getCandidateDescriptor().getValueParameters(); ReceiverParameterDescriptor receiverParameter = candidate.getReceiverParameter();
for (ValueParameterDescriptor valueParameter : valueParameters) { ReceiverValue receiverArgument = candidateCall.getReceiverArgument();
if (!usedParameters.contains(valueParameter)) { if (receiverParameter != null &&!receiverArgument.exists()) {
if (valueParameter.hasDefaultValue()) { tracing.missingReceiver(traceForCall, receiverParameter);
candidateCall.recordValueArgument(valueParameter, DefaultValueArgument.DEFAULT); setStatus(ERROR);
} }
else if (valueParameter.getVarargElementType() != null) { if (receiverParameter == null && receiverArgument.exists()) {
candidateCall.recordValueArgument(valueParameter, new VarargValueArgument()); tracing.noReceiverAllowed(traceForCall);
if (call.getCalleeExpression() instanceof JetSimpleNameExpression) {
setStatus(STRONG_ERROR);
} }
else { else {
tracing.noValueForParameter(traceForCall, valueParameter); setStatus(ERROR);
status = ERROR;
} }
} }
} }
return status;
}
private static <D extends CallableDescriptor> Status checkReceiverArgument( private void putVararg(
Call call, ValueParameterDescriptor valueParameterDescriptor,
TracingStrategy tracing, ValueArgument valueArgument
ResolvedCallImpl<D> candidateCall, ) {
Status status if (valueParameterDescriptor.getVarargElementType() != null) {
) { VarargValueArgument vararg = varargs.get(valueParameterDescriptor);
DelegatingBindingTrace traceForCall = candidateCall.getTrace(); if (vararg == null) {
D candidate = candidateCall.getCandidateDescriptor(); vararg = new VarargValueArgument();
varargs.put(valueParameterDescriptor, vararg);
ReceiverParameterDescriptor receiverParameter = candidate.getReceiverParameter(); candidateCall.recordValueArgument(valueParameterDescriptor, vararg);
ReceiverValue receiverArgument = candidateCall.getReceiverArgument(); }
if (receiverParameter != null &&!receiverArgument.exists()) { vararg.addArgument(valueArgument);
tracing.missingReceiver(traceForCall, receiverParameter);
status = ERROR;
}
if (receiverParameter == null && receiverArgument.exists()) {
tracing.noReceiverAllowed(traceForCall);
if (call.getCalleeExpression() instanceof JetSimpleNameExpression) {
status = STRONG_ERROR;
} }
else { else {
status = ERROR; LeafPsiElement spread = valueArgument.getSpreadElement();
if (spread != null) {
candidateCall.getTrace().report(NON_VARARG_SPREAD.on(spread));
setStatus(WEAK_ERROR);
}
ResolvedValueArgument argument = new ExpressionValueArgument(valueArgument);
candidateCall.recordValueArgument(valueParameterDescriptor, argument);
} }
} }
return status;
}
private static <D extends CallableDescriptor> Status put( private void setStatus(@NotNull Status newStatus) {
ResolvedCallImpl<D> candidateCall, status = status.compose(newStatus);
ValueParameterDescriptor valueParameterDescriptor,
ValueArgument valueArgument,
Map<ValueParameterDescriptor, VarargValueArgument> varargs
) {
Status error = OK;
if (valueParameterDescriptor.getVarargElementType() != null) {
VarargValueArgument vararg = varargs.get(valueParameterDescriptor);
if (vararg == null) {
vararg = new VarargValueArgument();
varargs.put(valueParameterDescriptor, vararg);
candidateCall.recordValueArgument(valueParameterDescriptor, vararg);
}
vararg.addArgument(valueArgument);
} }
else {
LeafPsiElement spread = valueArgument.getSpreadElement(); private interface ProcessorState {
if (spread != null) { ProcessorState processNamedArgument(@NotNull ValueArgument argument);
candidateCall.getTrace().report(NON_VARARG_SPREAD.on(spread)); ProcessorState processPositionedArgument(@NotNull ValueArgument argument, int index);
error = WEAK_ERROR;
}
ResolvedValueArgument argument = new ExpressionValueArgument(valueArgument);
candidateCall.recordValueArgument(valueParameterDescriptor, argument);
} }
return error;
} }
} }