From 75fbecceda2179254f0448e0a99eedef1fbac7a0 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Tue, 18 Mar 2014 15:02:53 +0400 Subject: [PATCH] Improved error reporting for invoke on expression --- .../jet/lang/diagnostics/Errors.java | 3 +- .../rendering/DefaultErrorMessages.java | 5 +-- .../jet/lang/resolve/calls/CallResolver.java | 8 +--- .../lang/resolve/calls/CallResolverUtil.java | 5 +++ .../lang/resolve/calls/CallTransformer.java | 3 +- .../lang/resolve/calls/CandidateResolver.java | 41 +++++++++++-------- .../calls/tasks/TracingStrategyForInvoke.java | 10 ++++- .../tests/FunctionCalleeExpressions.kt | 8 ++-- .../errors/receiverPresenceErrorForInvoke.kt | 11 +++++ .../resolve/invoke/errors/unresolvedInvoke.kt | 2 +- .../wrongReceiverForInvokeOnExpression.kt | 15 +++++++ .../errors/wrongReceiverTypeForInvoke.kt | 2 +- .../checkers/JetDiagnosticsTestGenerated.java | 10 +++++ 13 files changed, 86 insertions(+), 37 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/errors/receiverPresenceErrorForInvoke.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverForInvokeOnExpression.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index d2ec0a7c61e..cea0652b147 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -339,8 +339,7 @@ public interface Errors { DiagnosticFactory1 ILLEGAL_SELECTOR = DiagnosticFactory1.create(ERROR); - DiagnosticFactory1 CALLEE_NOT_A_FUNCTION = DiagnosticFactory1.create(ERROR); - DiagnosticFactory2 FUNCTION_EXPECTED = DiagnosticFactory2.create(ERROR); + DiagnosticFactory2 FUNCTION_EXPECTED = DiagnosticFactory2.create(ERROR); DiagnosticFactory2 FUNCTION_CALL_EXPECTED = DiagnosticFactory2.create(ERROR, CALL_EXPRESSION); DiagnosticFactory0 NON_TAIL_RECURSIVE_CALL = DiagnosticFactory0.create(WARNING, CALL_EXPRESSION); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java index f1fbde96f95..a98f2dd008d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java @@ -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() { + MAP.put(FUNCTION_EXPECTED, "Expression ''{0}''{1} cannot be invoked as a function. The function 'invoke()' is not found", ELEMENT_TEXT, new Renderer() { @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() { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index d1aed5c2534..2a07534a769 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -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 invokeResults = + TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, call, calleeType); + return (OverloadResolutionResultsImpl) 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) invokeResults; //todo } else { // checkTypesWithNoCallee(trace, scope, call); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolverUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolverUtil.java index 6d79a52df7c..c6ee3681759 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolverUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolverUtil.java @@ -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(); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java index 45256146beb..85792dcb351 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java @@ -207,7 +207,8 @@ public class CallTransformer { functionCall, context.checkArguments, context.dataFlowInfoForArguments); // 'invoke' call resolve - TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, functionCall); + TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke( + calleeExpression, functionCall, variableReceiver.getType()); OverloadResolutionResults results = callResolver.resolveCallForInvoke( basicCallResolutionContext, tracingForInvoke); Collection> calls = ((OverloadResolutionResultsImpl)results).getResultingCalls(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java index 16119e0ca6c..5e1007f5021 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java @@ -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 ResolutionStatus checkReceiverTypeError( + @NotNull CallCandidateResolutionContext 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); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyForInvoke.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyForInvoke.java index c641fd6ca59..63225d0914a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyForInvoke.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyForInvoke.java @@ -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 void unresolvedReferenceWrongReceiver( @NotNull BindingTrace trace, @NotNull Collection> candidates ) { + trace.report(FUNCTION_EXPECTED.on(reference, reference, calleeType)); } } diff --git a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.kt b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.kt index 4f07578fc2c..9de1ae3232c 100644 --- a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.kt +++ b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.kt @@ -44,9 +44,9 @@ fun main(args : Array) { b : Int fooT2()(1) // : Any? - 1() - 1{} - 1(){} + 1() + 1{} + 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."sdf"() + 1."sdf"() 1."sdf" 1.{} diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/receiverPresenceErrorForInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/receiverPresenceErrorForInvoke.kt new file mode 100644 index 00000000000..d44d5d7801f --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/receiverPresenceErrorForInvoke.kt @@ -0,0 +1,11 @@ +fun test1(f: String.() -> Unit) { + (f)() + + f() +} + +fun test2(f: (Int) -> Int) { + 1.f(2) + + 2.(f)(2) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/unresolvedInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/unresolvedInvoke.kt index 707ad370263..2432933fe05 100644 --- a/compiler/testData/diagnostics/tests/resolve/invoke/errors/unresolvedInvoke.kt +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/unresolvedInvoke.kt @@ -1,4 +1,4 @@ fun foo(i: Int) { i() - 1() + 1() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverForInvokeOnExpression.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverForInvokeOnExpression.kt new file mode 100644 index 00000000000..36953d2cfb8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverForInvokeOnExpression.kt @@ -0,0 +1,15 @@ +fun test1() { + 1. { String.(i: Int) -> i }(1) + 1.(@label{ String.(i: Int) -> i })(1) +} + +fun test2(f: String.(Int) -> Unit) { + 11.(f)(1) + 11.(f)() +} + +fun test3() { + fun foo(): String.(Int) -> Unit = {} + + 1.(foo())(1) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt index 31f3f009421..3ddec244424 100644 --- a/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt @@ -5,5 +5,5 @@ fun String.invoke(i: Int) {} fun foo(i: Int) { i(1) - 1(1) + 1(1) } \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 63cf3de56c3..ac37a39e7f4 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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");