KT-2014 Better diagnostic when using property syntax to call a method
#KT-2014 fixed
This commit is contained in:
@@ -320,6 +320,8 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory2<JetDeclaration, CallableMemberDescriptor, String> CONFLICTING_OVERLOADS = DiagnosticFactory2.create(ERROR, DECLARATION);
|
||||
|
||||
DiagnosticFactory2<JetReferenceExpression, JetExpression, JetType> FUNCTION_EXPECTED = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory2<JetReferenceExpression, JetExpression, Boolean> FUNCTION_CALL_EXPECTED = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory3<JetExpression, String, JetType, JetType> RESULT_TYPE_MISMATCH = DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory3<JetReferenceExpression, String, String, String> UNSAFE_INFIX_CALL = DiagnosticFactory3.create(ERROR);
|
||||
|
||||
+17
@@ -23,6 +23,7 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetTypeConstraint;
|
||||
import org.jetbrains.jet.lang.types.ErrorUtils;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lexer.JetKeywordToken;
|
||||
import org.jetbrains.jet.resolve.DescriptorRenderer;
|
||||
@@ -364,6 +365,22 @@ 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>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String render(@NotNull JetType type) {
|
||||
if (ErrorUtils.isErrorType(type)) return "";
|
||||
return " of type '" + type.toString() + "'";
|
||||
}
|
||||
});
|
||||
MAP.put(FUNCTION_CALL_EXPECTED, "Function invocation ''{0}({1})'' expected", ELEMENT_TEXT,new Renderer<Boolean>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String render(@NotNull Boolean hasValueParameters) {
|
||||
return hasValueParameters ? "..." : "";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
MAP.put(RESULT_TYPE_MISMATCH, "{0} must return {1} but returns {2}", TO_STRING, RENDER_TYPE, RENDER_TYPE);
|
||||
MAP.put(UNSAFE_INFIX_CALL,
|
||||
|
||||
@@ -124,7 +124,13 @@ public class CallResolver {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public OverloadResolutionResults<FunctionDescriptor> resolveFunctionCall(BindingTrace trace, JetScope scope, Call call, JetType expectedType, DataFlowInfo dataFlowInfo) {
|
||||
public OverloadResolutionResults<FunctionDescriptor> resolveFunctionCall(
|
||||
@NotNull BindingTrace trace,
|
||||
@NotNull JetScope scope,
|
||||
@NotNull Call call,
|
||||
@NotNull JetType expectedType,
|
||||
@NotNull DataFlowInfo dataFlowInfo) {
|
||||
|
||||
return resolveFunctionCall(BasicResolutionContext.create(trace, scope, call, expectedType, dataFlowInfo));
|
||||
}
|
||||
|
||||
|
||||
+112
-42
@@ -595,50 +595,62 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JetType getSelectorReturnType(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull ExpressionTypingContext context) {
|
||||
if (selectorExpression instanceof JetCallExpression) {
|
||||
JetCallExpression callExpression = (JetCallExpression) selectorExpression;
|
||||
FunctionDescriptor functionDescriptor = context.resolveCall(receiver, callOperationNode, callExpression);
|
||||
checkSuper(receiver, functionDescriptor, context.trace, selectorExpression);
|
||||
return functionDescriptor != null ? functionDescriptor.getReturnType() : null;
|
||||
}
|
||||
else if (selectorExpression instanceof JetSimpleNameExpression) {
|
||||
JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) selectorExpression;
|
||||
private FunctionDescriptor getFunctionDescriptor(@NotNull Call call, @NotNull JetExpression callExpression, @NotNull ReceiverDescriptor receiver,
|
||||
@NotNull ExpressionTypingContext context, @NotNull boolean[] result) {
|
||||
|
||||
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace);
|
||||
OverloadResolutionResults<VariableDescriptor> resolutionResult = context.replaceBindingTrace(temporaryTrace).resolveSimpleProperty(receiver, callOperationNode, nameExpression);
|
||||
if (resolutionResult.isSuccess()) {
|
||||
temporaryTrace.commit();
|
||||
VariableDescriptor resultingDescriptor = resolutionResult.getResultingDescriptor();
|
||||
checkSuper(receiver, resultingDescriptor, context.trace, selectorExpression);
|
||||
return resultingDescriptor.getType();
|
||||
}
|
||||
if (resolutionResult.isSingleResult()) {
|
||||
temporaryTrace.commit();
|
||||
return resolutionResult.getResultingDescriptor().getReturnType();
|
||||
}
|
||||
if (resolutionResult.isAmbiguity()) {
|
||||
temporaryTrace.commit();
|
||||
return null;
|
||||
}
|
||||
OverloadResolutionResults<FunctionDescriptor> results = context.resolveFunctionCall(call);
|
||||
if (!results.isNothing()) {
|
||||
checkSuper(receiver, results, context.trace, callExpression);
|
||||
result[0] = true;
|
||||
return results.isSingleResult() ? results.getResultingDescriptor() : null;
|
||||
}
|
||||
result[0] = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JetType getVariableType(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverDescriptor receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context, @NotNull boolean[] result) {
|
||||
|
||||
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace);
|
||||
OverloadResolutionResults<VariableDescriptor> resolutionResult = context.replaceBindingTrace(temporaryTrace).resolveSimpleProperty(receiver, callOperationNode, nameExpression);
|
||||
if (!resolutionResult.isNothing()) {
|
||||
temporaryTrace.commit();
|
||||
checkSuper(receiver, resolutionResult, context.trace, nameExpression);
|
||||
result[0] = true;
|
||||
return resolutionResult.isSingleResult() ? resolutionResult.getResultingDescriptor().getReturnType() : null;
|
||||
}
|
||||
|
||||
ExpressionTypingContext newContext = receiver.exists()
|
||||
? context.replaceScope(receiver.getType().getMemberScope())
|
||||
: context;
|
||||
JetType jetType = lookupNamespaceOrClassObject(nameExpression, nameExpression.getReferencedNameAsName(), newContext);
|
||||
if (jetType != null) {
|
||||
|
||||
// Uncommitted changes in temp context
|
||||
context.trace.record(RESOLUTION_SCOPE, nameExpression, context.scope);
|
||||
if (context.dataFlowInfo.hasTypeInfoConstraints()) {
|
||||
context.trace.record(NON_DEFAULT_EXPRESSION_DATA_FLOW, nameExpression, context.dataFlowInfo);
|
||||
}
|
||||
|
||||
ExpressionTypingContext newContext = receiver.exists()
|
||||
? context.replaceScope(receiver.getType().getMemberScope())
|
||||
: context;
|
||||
JetType jetType = lookupNamespaceOrClassObject(nameExpression, nameExpression.getReferencedNameAsName(), newContext);
|
||||
if (jetType == null) {
|
||||
context.trace.report(UNRESOLVED_REFERENCE.on(nameExpression));
|
||||
}
|
||||
|
||||
result[0] = true;
|
||||
return jetType;
|
||||
}
|
||||
result[0] = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JetType getSelectorReturnType(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetExpression selectorExpression, @NotNull ExpressionTypingContext context) {
|
||||
if (selectorExpression instanceof JetCallExpression) {
|
||||
|
||||
return getCallExpressionType((JetCallExpression) selectorExpression, receiver, callOperationNode, context);
|
||||
}
|
||||
else if (selectorExpression instanceof JetSimpleNameExpression) {
|
||||
|
||||
return getSimpleNameExpressionType((JetSimpleNameExpression) selectorExpression, receiver, callOperationNode, context);
|
||||
}
|
||||
else if (selectorExpression instanceof JetQualifiedExpression) {
|
||||
|
||||
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression;
|
||||
JetExpression newReceiverExpression = qualifiedExpression.getReceiverExpression();
|
||||
JetType newReceiverType = getSelectorReturnType(receiver, callOperationNode, newReceiverExpression, context.replaceExpectedType(NO_EXPECTED_TYPE));
|
||||
@@ -653,11 +665,70 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void checkSuper(@NotNull ReceiverDescriptor receiverDescriptor, @Nullable DeclarationDescriptor member, @NotNull BindingTrace trace, @NotNull JetExpression expression) {
|
||||
@Nullable
|
||||
private JetType getSimpleNameExpressionType(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverDescriptor receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context) {
|
||||
|
||||
boolean[] result = new boolean[1];
|
||||
|
||||
TemporaryBindingTrace traceForVariable = TemporaryBindingTrace.create(context.trace);
|
||||
JetType type = getVariableType(nameExpression, receiver, callOperationNode, context.replaceBindingTrace(traceForVariable), result);
|
||||
if (result[0]) {
|
||||
traceForVariable.commit();
|
||||
return type;
|
||||
}
|
||||
|
||||
Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList());
|
||||
TemporaryBindingTrace traceForFunction = TemporaryBindingTrace.create(context.trace);
|
||||
FunctionDescriptor functionDescriptor = getFunctionDescriptor(call, nameExpression, receiver, context, result);
|
||||
if (result[0]) {
|
||||
traceForFunction.commit();
|
||||
boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0;
|
||||
context.trace.report(Errors.FUNCTION_CALL_EXPECTED.on(nameExpression, nameExpression, hasValueParameters));
|
||||
return functionDescriptor != null ? functionDescriptor.getReturnType() : null;
|
||||
}
|
||||
|
||||
traceForVariable.commit();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JetType getCallExpressionType(@NotNull JetCallExpression callExpression, @NotNull ReceiverDescriptor receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context) {
|
||||
|
||||
boolean[] result = new boolean[1];
|
||||
Call call = CallMaker.makeCall(receiver, callOperationNode, callExpression);
|
||||
|
||||
TemporaryBindingTrace traceForFunction = TemporaryBindingTrace.create(context.trace);
|
||||
FunctionDescriptor functionDescriptor = getFunctionDescriptor(call, callExpression, receiver,
|
||||
context.replaceBindingTrace(traceForFunction), result);
|
||||
if (result[0]) {
|
||||
traceForFunction.commit();
|
||||
return functionDescriptor != null ? functionDescriptor.getReturnType() : null;
|
||||
}
|
||||
|
||||
JetExpression calleeExpression = callExpression.getCalleeExpression();
|
||||
if (calleeExpression instanceof JetSimpleNameExpression) {
|
||||
TemporaryBindingTrace traceForVariable = TemporaryBindingTrace.create(context.trace);
|
||||
JetType type = getVariableType((JetSimpleNameExpression) calleeExpression, receiver, callOperationNode,
|
||||
context.replaceBindingTrace(traceForVariable), result);
|
||||
if (result[0]) {
|
||||
traceForVariable.commit();
|
||||
context.trace.report(Errors.FUNCTION_EXPECTED.on((JetReferenceExpression) calleeExpression, callExpression, type != null ? type : ErrorUtils.createErrorType("")));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
traceForFunction.commit();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void checkSuper(@NotNull ReceiverDescriptor receiverDescriptor, @NotNull OverloadResolutionResults<? extends CallableDescriptor> results, @NotNull BindingTrace trace, @NotNull JetExpression expression) {
|
||||
if (!results.isSingleResult()) return;
|
||||
if (!(receiverDescriptor instanceof ExpressionReceiver)) return;
|
||||
JetExpression receiver = ((ExpressionReceiver) receiverDescriptor).getExpression();
|
||||
if (receiver instanceof JetSuperExpression && member instanceof MemberDescriptor) {
|
||||
if (((MemberDescriptor) member).getModality() == Modality.ABSTRACT) {
|
||||
CallableDescriptor descriptor = results.getResultingDescriptor();
|
||||
if (receiver instanceof JetSuperExpression && descriptor instanceof MemberDescriptor) {
|
||||
if (((MemberDescriptor) descriptor).getModality() == Modality.ABSTRACT) {
|
||||
trace.report(ABSTRACT_SUPER_CALL.on(expression));
|
||||
}
|
||||
}
|
||||
@@ -665,8 +736,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
|
||||
@Override
|
||||
public JetType visitCallExpression(JetCallExpression expression, ExpressionTypingContext context) {
|
||||
FunctionDescriptor functionDescriptor = context.resolveCall(NO_RECEIVER, null, expression);
|
||||
JetType expressionType = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
|
||||
JetType expressionType = getCallExpressionType(expression, NO_RECEIVER, null, context);
|
||||
return DataFlowUtils.checkType(expressionType, expression, context);
|
||||
}
|
||||
|
||||
@@ -732,7 +802,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
ExpressionReceiver receiver = new ExpressionReceiver(baseExpression, type);
|
||||
|
||||
// Resolve the operation reference
|
||||
OverloadResolutionResults<FunctionDescriptor> resolutionResults = context.resolveCallWithGivenNameToDescriptor(
|
||||
OverloadResolutionResults<FunctionDescriptor> resolutionResults = context.resolveCallWithGivenName(
|
||||
CallMaker.makeCall(receiver, expression),
|
||||
expression.getOperationReference(),
|
||||
name);
|
||||
@@ -929,7 +999,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
public boolean checkInExpression(JetElement callElement, @NotNull JetSimpleNameExpression operationSign, @Nullable JetExpression left, @NotNull JetExpression right, ExpressionTypingContext context) {
|
||||
Name name = Name.identifier("contains");
|
||||
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, right, context.replaceExpectedType(NO_EXPECTED_TYPE));
|
||||
OverloadResolutionResults<FunctionDescriptor> resolutionResult = context.resolveCallWithGivenNameToDescriptor(
|
||||
OverloadResolutionResults<FunctionDescriptor> resolutionResult = context.resolveCallWithGivenName(
|
||||
CallMaker.makeCallWithExpressions(callElement, receiver, null, operationSign, Collections.singletonList(left)),
|
||||
operationSign,
|
||||
name);
|
||||
@@ -994,7 +1064,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
@NotNull
|
||||
/*package*/ OverloadResolutionResults<FunctionDescriptor> getResolutionResultsForBinaryCall(JetScope scope, Name name, ExpressionTypingContext context, JetBinaryExpression binaryExpression, ExpressionReceiver receiver) {
|
||||
// ExpressionReceiver receiver = safeGetExpressionReceiver(facade, binaryExpression.getLeft(), context.replaceScope(scope));
|
||||
return context.replaceScope(scope).resolveCallWithGivenNameToDescriptor(
|
||||
return context.replaceScope(scope).resolveCallWithGivenName(
|
||||
CallMaker.makeCall(receiver, binaryExpression),
|
||||
binaryExpression.getOperationReference(),
|
||||
name
|
||||
|
||||
+6
-21
@@ -19,10 +19,13 @@ package org.jetbrains.jet.lang.types.expressions;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.BasicResolutionContext;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallMaker;
|
||||
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
|
||||
@@ -34,6 +37,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -57,17 +61,6 @@ public class ExpressionTypingContext {
|
||||
labelResolver, trace, scope, dataFlowInfo, expectedType, namespacesAllowed);
|
||||
}
|
||||
|
||||
// @NotNull
|
||||
// public static ExpressionTypingContext newRootContext(
|
||||
// @NotNull JetSemanticServices semanticServices,
|
||||
// @NotNull BindingTrace trace,
|
||||
// @NotNull JetScope scope,
|
||||
// @NotNull DataFlowInfo dataFlowInfo,
|
||||
// @NotNull JetType expectedType,
|
||||
// @NotNull JetType expectedReturnType) {
|
||||
// return newContext(semanticServices, new HashMap<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), new LabelResolver(), trace, scope, dataFlowInfo, expectedType, expectedReturnType);
|
||||
// }
|
||||
//
|
||||
public final ExpressionTypingServices expressionTypingServices;
|
||||
public final BindingTrace trace;
|
||||
public final JetScope scope;
|
||||
@@ -155,15 +148,8 @@ public class ExpressionTypingContext {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public OverloadResolutionResults<FunctionDescriptor> resolveCallWithGivenNameToDescriptor(@NotNull Call call, @NotNull JetReferenceExpression functionReference, @NotNull Name name) {
|
||||
return resolveCallWithGivenName(call, functionReference, name);
|
||||
// return results == null ? null : results.getResultingDescriptor();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FunctionDescriptor resolveCall(@NotNull ReceiverDescriptor receiver, @Nullable ASTNode callOperationNode, @NotNull JetCallExpression callExpression) {
|
||||
OverloadResolutionResults<FunctionDescriptor> results = expressionTypingServices.getCallResolver().resolveFunctionCall(trace, scope, CallMaker.makeCall(receiver, callOperationNode, callExpression), expectedType, dataFlowInfo);
|
||||
return results.isSingleResult() ? results.getResultingDescriptor() : null;
|
||||
public OverloadResolutionResults<FunctionDescriptor> resolveFunctionCall(@NotNull Call call) {
|
||||
return expressionTypingServices.getCallResolver().resolveFunctionCall(makeResolutionContext(call));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -176,5 +162,4 @@ public class ExpressionTypingContext {
|
||||
public OverloadResolutionResults<FunctionDescriptor> resolveExactSignature(@NotNull ReceiverDescriptor receiver, @NotNull Name name, @NotNull List<JetType> parameterTypes) {
|
||||
return expressionTypingServices.getCallResolver().resolveExactSignature(scope, receiver, name, parameterTypes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
//KT-2014 Better diagnostic when using property syntax to call a method
|
||||
package c
|
||||
|
||||
class Foo {
|
||||
fun prop() : Int = 1
|
||||
fun bar(i: Int) : Int = i
|
||||
|
||||
val a : Int = 1
|
||||
}
|
||||
|
||||
fun x(f : Foo) {
|
||||
f.<!FUNCTION_CALL_EXPECTED!>prop<!>
|
||||
f.<!NO_VALUE_FOR_PARAMETER, FUNCTION_CALL_EXPECTED!>bar<!>
|
||||
|
||||
f.<!FUNCTION_EXPECTED!>a<!>()
|
||||
<!EXPRESSION_EXPECTED_NAMESPACE_FOUND, FUNCTION_EXPECTED!>c<!>()
|
||||
<!FUNCTION_EXPECTED!>R<!>()
|
||||
}
|
||||
|
||||
object R {}
|
||||
@@ -1,5 +1,5 @@
|
||||
package d
|
||||
|
||||
fun foo(<!UNUSED_PARAMETER!>a<!> : IntArray) {
|
||||
if (null == <!UNRESOLVED_REFERENCE!>a<!>()<!SYNTAX!><!>
|
||||
fun foo(a : IntArray) {
|
||||
if (null == <!FUNCTION_EXPECTED!>a<!>()<!SYNTAX!><!>
|
||||
<!SYNTAX!><!>}
|
||||
Reference in New Issue
Block a user