KT-2014 Better diagnostic when using property syntax to call a method

#KT-2014 fixed
This commit is contained in:
Svetlana Isakova
2012-05-23 18:52:57 +04:00
parent deb13344c0
commit b22a7dbf99
7 changed files with 166 additions and 66 deletions
@@ -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);
@@ -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));
}
@@ -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
@@ -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!><!>}