Improved error reporting for invoke on expression
This commit is contained in:
@@ -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);
|
||||
|
||||
+2
-3
@@ -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);
|
||||
|
||||
|
||||
+9
-1
@@ -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!>{}<!>
|
||||
|
||||
+11
@@ -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<!>()
|
||||
}
|
||||
+15
@@ -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)
|
||||
}
|
||||
+1
-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");
|
||||
|
||||
Reference in New Issue
Block a user