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, String> ILLEGAL_SELECTOR = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<JetExpression, JetType> CALLEE_NOT_A_FUNCTION = DiagnosticFactory1.create(ERROR); DiagnosticFactory2<JetExpression, JetExpression, JetType> FUNCTION_EXPECTED = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<JetReferenceExpression, JetExpression, JetType> FUNCTION_EXPECTED = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<JetExpression, JetExpression, Boolean> FUNCTION_CALL_EXPECTED = DiagnosticFactory2.create(ERROR, CALL_EXPRESSION); DiagnosticFactory2<JetExpression, JetExpression, Boolean> FUNCTION_CALL_EXPECTED = DiagnosticFactory2.create(ERROR, CALL_EXPRESSION);
DiagnosticFactory0<PsiElement> NON_TAIL_RECURSIVE_CALL = DiagnosticFactory0.create(WARNING, 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); TO_STRING, RENDER_TYPE, RENDER_TYPE);
MAP.put(COMPARE_TO_TYPE_MISMATCH, "''compareTo()'' must return kotlin.Int, but returns {0}", 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, MAP.put(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY,
"Returns are not allowed for functions with expression body. Use block body in '{...}'"); "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(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 @NotNull
@Override @Override
public String render(@NotNull JetType type) { public String render(@NotNull JetType type) {
if (type.isError()) return ""; 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>() { 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( Call call = new CallTransformer.CallForImplicitInvoke(
context.call.getExplicitReceiver(), expressionReceiver, context.call); context.call.getExplicitReceiver(), expressionReceiver, context.call);
TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, call); TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, call, calleeType);
OverloadResolutionResults<FunctionDescriptor> invokeResults = return (OverloadResolutionResultsImpl<FunctionDescriptor>)
resolveCallForInvoke(context.replaceCall(call), tracingForInvoke); 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 { else {
// checkTypesWithNoCallee(trace, scope, call); // checkTypesWithNoCallee(trace, scope, call);
@@ -154,4 +154,9 @@ public class CallResolverUtil {
JetExpression expression = ((ExpressionReceiver) thisObject).getExpression(); JetExpression expression = ((ExpressionReceiver) thisObject).getExpression();
return expression instanceof JetSimpleNameExpression; 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); functionCall, context.checkArguments, context.dataFlowInfoForArguments);
// 'invoke' call resolve // 'invoke' call resolve
TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, functionCall); TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(
calleeExpression, functionCall, variableReceiver.getType());
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveCallForInvoke( OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveCallForInvoke(
basicCallResolutionContext, tracingForInvoke); basicCallResolutionContext, tracingForInvoke);
Collection<ResolvedCallWithTrace<FunctionDescriptor>> calls = ((OverloadResolutionResultsImpl<FunctionDescriptor>)results).getResultingCalls(); Collection<ResolvedCallWithTrace<FunctionDescriptor>> calls = ((OverloadResolutionResultsImpl<FunctionDescriptor>)results).getResultingCalls();
@@ -107,17 +107,17 @@ public class CandidateResolver {
argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(context.call, context.tracing, argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(context.call, context.tracing,
candidateCall, unmappedArguments); candidateCall, unmappedArguments);
if (!argumentMappingStatus.isSuccess()) { 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); candidateCall.addStatus(RECEIVER_PRESENCE_ERROR);
return;
} }
else { else {
candidateCall.addStatus(OTHER_ERROR); 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 receiverDescriptor = candidateDescriptor.getReceiverParameter();
ReceiverParameterDescriptor expectedThisObjectDescriptor = candidateDescriptor.getExpectedThisObject(); ReceiverParameterDescriptor expectedThisObjectDescriptor = candidateDescriptor.getExpectedThisObject();
ReceiverParameterDescriptor receiverParameterDescriptor; ResolutionStatus status = SUCCESS;
ReceiverValue receiverArgument; // For the expressions like '42.(f)()' where f: String.() -> Unit we'd like to generate a type mismatch error on '1',
if (receiverDescriptor != null && candidateCall.getReceiverArgument().exists()) { // not to throw away the candidate, so the following check is skipped.
receiverParameterDescriptor = receiverDescriptor; if (!CallResolverUtil.isInvokeCallOnExpressionWithBothReceivers(context.call)) {
receiverArgument = candidateCall.getReceiverArgument(); status = status.combine(checkReceiverTypeError(context, receiverDescriptor, candidateCall.getReceiverArgument()));
}
else if (expectedThisObjectDescriptor != null && candidateCall.getThisObject().exists()) {
receiverParameterDescriptor = expectedThisObjectDescriptor;
receiverArgument = candidateCall.getThisObject();
}
else {
return SUCCESS;
} }
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); 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.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
import org.jetbrains.jet.lang.types.JetType;
import java.util.Collection; 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.CALL;
import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL; import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
public class TracingStrategyForInvoke extends AbstractTracingStrategy { public class TracingStrategyForInvoke extends AbstractTracingStrategy {
private final JetType calleeType;
public TracingStrategyForInvoke( public TracingStrategyForInvoke(
@NotNull JetExpression reference, @NotNull JetExpression reference,
@NotNull Call call @NotNull Call call,
@NotNull JetType calleeType
) { ) {
super(reference, call); super(reference, call);
this.calleeType = calleeType;
} }
@Override @Override
@@ -52,11 +58,13 @@ public class TracingStrategyForInvoke extends AbstractTracingStrategy {
@Override @Override
public void unresolvedReference(@NotNull BindingTrace trace) { public void unresolvedReference(@NotNull BindingTrace trace) {
trace.report(FUNCTION_EXPECTED.on(reference, reference, calleeType));
} }
@Override @Override
public <D extends CallableDescriptor> void unresolvedReferenceWrongReceiver( public <D extends CallableDescriptor> void unresolvedReferenceWrongReceiver(
@NotNull BindingTrace trace, @NotNull Collection<ResolvedCallWithTrace<D>> candidates @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 b : Int
<!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>fooT2<!>()(1) // : Any? <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>fooT2<!>()(1) // : Any?
<!CALLEE_NOT_A_FUNCTION!>1<!>() <!FUNCTION_EXPECTED!>1<!>()
<!CALLEE_NOT_A_FUNCTION!>1<!>{} <!FUNCTION_EXPECTED!>1<!>{}
<!CALLEE_NOT_A_FUNCTION!>1<!>(){} <!FUNCTION_EXPECTED!>1<!>(){}
} }
fun f() : Int.() -> Unit = {} fun f() : Int.() -> Unit = {}
@@ -63,7 +63,7 @@ fun main1() {
1.if(true){Int.() -> 1}else{f()}() 1.if(true){Int.() -> 1}else{f()}()
1.if(true){Int.() -> 1}else{Int.() -> 1}() 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!>"sdf"<!>
1.<!ILLEGAL_SELECTOR!>{}<!> 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) { fun foo(i: Int) {
<!FUNCTION_EXPECTED!>i<!>() <!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) { fun foo(i: Int) {
<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>i<!>(1) <!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"); 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") @TestMetadata("typeInferenceErrorForInvoke.kt")
public void testTypeInferenceErrorForInvoke() throws Exception { public void testTypeInferenceErrorForInvoke() throws Exception {
doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/typeInferenceErrorForInvoke.kt"); 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"); 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") @TestMetadata("wrongReceiverTypeForInvoke.kt")
public void testWrongReceiverTypeForInvoke() throws Exception { public void testWrongReceiverTypeForInvoke() throws Exception {
doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt"); doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt");