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, 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);
|
||||||
|
|||||||
+2
-3
@@ -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);
|
||||||
|
|
||||||
|
|||||||
+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.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!>{}<!>
|
||||||
|
|||||||
+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) {
|
fun foo(i: Int) {
|
||||||
<!FUNCTION_EXPECTED!>i<!>()
|
<!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) {
|
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");
|
||||||
|
|||||||
Reference in New Issue
Block a user