Improved error reporting for invoke on expression

This commit is contained in:
Svetlana Isakova
2014-03-18 15:02:53 +04:00
parent db253ae7e1
commit 75fbecceda
13 changed files with 86 additions and 37 deletions
@@ -339,8 +339,7 @@ public interface Errors {
DiagnosticFactory1<JetExpression, String> ILLEGAL_SELECTOR = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<JetExpression, JetType> CALLEE_NOT_A_FUNCTION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<JetReferenceExpression, JetExpression, JetType> FUNCTION_EXPECTED = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<JetExpression, JetExpression, JetType> FUNCTION_EXPECTED = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<JetExpression, JetExpression, Boolean> FUNCTION_CALL_EXPECTED = DiagnosticFactory2.create(ERROR, CALL_EXPRESSION);
DiagnosticFactory0<PsiElement> NON_TAIL_RECURSIVE_CALL = DiagnosticFactory0.create(WARNING, CALL_EXPRESSION);
@@ -260,7 +260,6 @@ public class DefaultErrorMessages {
TO_STRING, RENDER_TYPE, RENDER_TYPE);
MAP.put(COMPARE_TO_TYPE_MISMATCH, "''compareTo()'' must return kotlin.Int, but returns {0}", RENDER_TYPE);
MAP.put(CALLEE_NOT_A_FUNCTION, "Expecting a function type, but found {0}", RENDER_TYPE);
MAP.put(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY,
"Returns are not allowed for functions with expression body. Use block body in '{...}'");
@@ -400,12 +399,12 @@ public class DefaultErrorMessages {
MAP.put(CONFLICTING_OVERLOADS, "{1} is already defined in ''{0}''", DescriptorRenderer.TEXT, TO_STRING);
MAP.put(FUNCTION_EXPECTED, "Expression ''{0}''{1} cannot be invoked as a function", ELEMENT_TEXT, new Renderer<JetType>() {
MAP.put(FUNCTION_EXPECTED, "Expression ''{0}''{1} cannot be invoked as a function. The function 'invoke()' is not found", ELEMENT_TEXT, new Renderer<JetType>() {
@NotNull
@Override
public String render(@NotNull JetType type) {
if (type.isError()) return "";
return " of type '" + type.toString() + "'";
return " of type '" + RENDER_TYPE.render(type) + "'";
}
});
MAP.put(FUNCTION_CALL_EXPECTED, "Function invocation ''{0}({1})'' expected", ELEMENT_TEXT,new Renderer<Boolean>() {
@@ -287,13 +287,9 @@ public class CallResolver {
Call call = new CallTransformer.CallForImplicitInvoke(
context.call.getExplicitReceiver(), expressionReceiver, context.call);
TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, call);
OverloadResolutionResults<FunctionDescriptor> invokeResults =
TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, call, calleeType);
return (OverloadResolutionResultsImpl<FunctionDescriptor>)
resolveCallForInvoke(context.replaceCall(call), tracingForInvoke);
if (invokeResults.isNothing() || invokeResults.getResultCode() == CANDIDATES_WITH_WRONG_RECEIVER) {
context.trace.report(CALLEE_NOT_A_FUNCTION.on(calleeExpression, calleeType));
}
return (OverloadResolutionResultsImpl<FunctionDescriptor>) invokeResults; //todo
}
else {
// checkTypesWithNoCallee(trace, scope, call);
@@ -154,4 +154,9 @@ public class CallResolverUtil {
JetExpression expression = ((ExpressionReceiver) thisObject).getExpression();
return expression instanceof JetSimpleNameExpression;
}
public static boolean isInvokeCallOnExpressionWithBothReceivers(@NotNull Call call) {
if (call.getCallType() != Call.CallType.INVOKE || isInvokeCallOnVariable(call)) return false;
return call.getExplicitReceiver().exists() && call.getThisObject().exists();
}
}
@@ -207,7 +207,8 @@ public class CallTransformer<D extends CallableDescriptor, F extends D> {
functionCall, context.checkArguments, context.dataFlowInfoForArguments);
// 'invoke' call resolve
TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, functionCall);
TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(
calleeExpression, functionCall, variableReceiver.getType());
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveCallForInvoke(
basicCallResolutionContext, tracingForInvoke);
Collection<ResolvedCallWithTrace<FunctionDescriptor>> calls = ((OverloadResolutionResultsImpl<FunctionDescriptor>)results).getResultingCalls();
@@ -107,17 +107,17 @@ public class CandidateResolver {
argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(context.call, context.tracing,
candidateCall, unmappedArguments);
if (!argumentMappingStatus.isSuccess()) {
if (argumentMappingStatus == ValueArgumentsToParametersMapper.Status.STRONG_ERROR) {
candidateCall.setUnmappedArguments(unmappedArguments);
//For the expressions like '42.(f)()' where f: () -> Unit we'd like to generate an error 'no receiver admitted',
//not to throw away the candidate.
if (argumentMappingStatus == ValueArgumentsToParametersMapper.Status.STRONG_ERROR
&& !CallResolverUtil.isInvokeCallOnExpressionWithBothReceivers(context.call)) {
candidateCall.addStatus(RECEIVER_PRESENCE_ERROR);
return;
}
else {
candidateCall.addStatus(OTHER_ERROR);
}
candidateCall.setUnmappedArguments(unmappedArguments);
if ((argumentMappingStatus == ValueArgumentsToParametersMapper.Status.ERROR && candidate.getTypeParameters().isEmpty()) ||
argumentMappingStatus == ValueArgumentsToParametersMapper.Status.STRONG_ERROR) {
return;
}
}
}
@@ -845,19 +845,24 @@ public class CandidateResolver {
ReceiverParameterDescriptor receiverDescriptor = candidateDescriptor.getReceiverParameter();
ReceiverParameterDescriptor expectedThisObjectDescriptor = candidateDescriptor.getExpectedThisObject();
ReceiverParameterDescriptor receiverParameterDescriptor;
ReceiverValue receiverArgument;
if (receiverDescriptor != null && candidateCall.getReceiverArgument().exists()) {
receiverParameterDescriptor = receiverDescriptor;
receiverArgument = candidateCall.getReceiverArgument();
}
else if (expectedThisObjectDescriptor != null && candidateCall.getThisObject().exists()) {
receiverParameterDescriptor = expectedThisObjectDescriptor;
receiverArgument = candidateCall.getThisObject();
}
else {
return SUCCESS;
ResolutionStatus status = SUCCESS;
// For the expressions like '42.(f)()' where f: String.() -> Unit we'd like to generate a type mismatch error on '1',
// not to throw away the candidate, so the following check is skipped.
if (!CallResolverUtil.isInvokeCallOnExpressionWithBothReceivers(context.call)) {
status = status.combine(checkReceiverTypeError(context, receiverDescriptor, candidateCall.getReceiverArgument()));
}
status = status.combine(checkReceiverTypeError(context, expectedThisObjectDescriptor, candidateCall.getThisObject()));
return status;
}
private static <D extends CallableDescriptor> ResolutionStatus checkReceiverTypeError(
@NotNull CallCandidateResolutionContext<D> context,
@Nullable ReceiverParameterDescriptor receiverParameterDescriptor,
@NotNull ReceiverValue receiverArgument
) {
if (receiverParameterDescriptor == null || !receiverArgument.exists()) return SUCCESS;
D candidateDescriptor = context.candidateCall.getCandidateDescriptor();
JetType erasedReceiverType = CallResolverUtil.getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor);
@@ -22,18 +22,24 @@ import org.jetbrains.jet.lang.psi.Call;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
import org.jetbrains.jet.lang.types.JetType;
import java.util.Collection;
import static org.jetbrains.jet.lang.diagnostics.Errors.FUNCTION_EXPECTED;
import static org.jetbrains.jet.lang.resolve.BindingContext.CALL;
import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
public class TracingStrategyForInvoke extends AbstractTracingStrategy {
private final JetType calleeType;
public TracingStrategyForInvoke(
@NotNull JetExpression reference,
@NotNull Call call
@NotNull Call call,
@NotNull JetType calleeType
) {
super(reference, call);
this.calleeType = calleeType;
}
@Override
@@ -52,11 +58,13 @@ public class TracingStrategyForInvoke extends AbstractTracingStrategy {
@Override
public void unresolvedReference(@NotNull BindingTrace trace) {
trace.report(FUNCTION_EXPECTED.on(reference, reference, calleeType));
}
@Override
public <D extends CallableDescriptor> void unresolvedReferenceWrongReceiver(
@NotNull BindingTrace trace, @NotNull Collection<ResolvedCallWithTrace<D>> candidates
) {
trace.report(FUNCTION_EXPECTED.on(reference, reference, calleeType));
}
}
@@ -44,9 +44,9 @@ fun main(args : Array<String>) {
b : Int
<!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>fooT2<!>()(1) // : Any?
<!CALLEE_NOT_A_FUNCTION!>1<!>()
<!CALLEE_NOT_A_FUNCTION!>1<!>{}
<!CALLEE_NOT_A_FUNCTION!>1<!>(){}
<!FUNCTION_EXPECTED!>1<!>()
<!FUNCTION_EXPECTED!>1<!>{}
<!FUNCTION_EXPECTED!>1<!>(){}
}
fun f() : Int.() -> Unit = {}
@@ -63,7 +63,7 @@ fun main1() {
1.if(true){Int.() -> 1}else{f()}()
1.if(true){Int.() -> 1}else{Int.() -> 1}()
1.<!CALLEE_NOT_A_FUNCTION!>"sdf"<!>()
1.<!FUNCTION_EXPECTED!>"sdf"<!>()
1.<!ILLEGAL_SELECTOR!>"sdf"<!>
1.<!ILLEGAL_SELECTOR!>{}<!>
@@ -0,0 +1,11 @@
fun test1(f: String.() -> Unit) {
<!MISSING_RECEIVER!>(f)<!>()
<!MISSING_RECEIVER!>f<!>()
}
fun test2(f: (Int) -> Int) {
1.<!UNRESOLVED_REFERENCE!>f<!>(2)
2.<!NO_RECEIVER_ADMITTED!>(f)<!>(2)
}
@@ -1,4 +1,4 @@
fun foo(i: Int) {
<!FUNCTION_EXPECTED!>i<!>()
<!CALLEE_NOT_A_FUNCTION!>1<!>()
<!FUNCTION_EXPECTED!>1<!>()
}
@@ -0,0 +1,15 @@
fun test1() {
<!TYPE_MISMATCH!>1<!>. { String.(i: Int) -> i }(1)
<!TYPE_MISMATCH!>1<!>.(@label{ String.(i: Int) -> i })(1)
}
fun test2(f: String.(Int) -> Unit) {
<!TYPE_MISMATCH!>11<!>.(f)(1)
<!TYPE_MISMATCH!>11<!>.(f)(<!NO_VALUE_FOR_PARAMETER!>)<!>
}
fun test3() {
fun foo(): String.(Int) -> Unit = {}
<!TYPE_MISMATCH!>1<!>.(foo())(1)
}
@@ -5,5 +5,5 @@ fun String.invoke(i: Int) {}
fun foo(i: Int) {
<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>i<!>(1)
<!CALLEE_NOT_A_FUNCTION!>1<!>(1)
<!FUNCTION_EXPECTED!>1<!>(1)
}
@@ -6113,6 +6113,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/invisibleInvoke.kt");
}
@TestMetadata("receiverPresenceErrorForInvoke.kt")
public void testReceiverPresenceErrorForInvoke() throws Exception {
doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/receiverPresenceErrorForInvoke.kt");
}
@TestMetadata("typeInferenceErrorForInvoke.kt")
public void testTypeInferenceErrorForInvoke() throws Exception {
doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/typeInferenceErrorForInvoke.kt");
@@ -6128,6 +6133,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/unsafeCallWithInvoke.kt");
}
@TestMetadata("wrongReceiverForInvokeOnExpression.kt")
public void testWrongReceiverForInvokeOnExpression() throws Exception {
doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverForInvokeOnExpression.kt");
}
@TestMetadata("wrongReceiverTypeForInvoke.kt")
public void testWrongReceiverTypeForInvoke() throws Exception {
doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt");