Add diagnostics for get/set methods of property delegate

This commit is contained in:
Natalia.Ukhorskaya
2013-04-16 15:41:25 +04:00
parent c58d4fd6d2
commit a9b4a342e7
24 changed files with 410 additions and 7 deletions
@@ -400,6 +400,11 @@ public interface Errors {
DiagnosticFactory0<JetExpression> ITERATOR_MISSING = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> ITERATOR_AMBIGUITY = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<JetExpression, String, JetType> DELEGATE_SPECIAL_FUNCTION_MISSING = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<JetExpression, String, Collection<? extends ResolvedCall<?>>> DELEGATE_SPECIAL_FUNCTION_AMBIGUITY = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<JetExpression, String, Collection<? extends ResolvedCall<?>>> DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory3<JetExpression, String, JetType, JetType> DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH = DiagnosticFactory3.create(ERROR);
DiagnosticFactory1<JetSimpleNameExpression, JetType> COMPARE_TO_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR);
// Labels
@@ -250,6 +250,12 @@ public class DefaultErrorMessages {
MAP.put(ITERATOR_MISSING, "For-loop range must have an iterator() method");
MAP.put(ITERATOR_AMBIGUITY, "Method ''iterator()'' is ambiguous for this expression: {0}", AMBIGUOUS_CALLS);
MAP.put(DELEGATE_SPECIAL_FUNCTION_MISSING, "Missing ''{0}'' method on delegate of type ''{1}''", TO_STRING, RENDER_TYPE);
MAP.put(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY, "Overload resolution ambiguity on method ''{0}'': {1}", TO_STRING, AMBIGUOUS_CALLS);
MAP.put(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, "Property delegate must have a ''{0}'' method. None of the following functions is suitable: {1}", TO_STRING, AMBIGUOUS_CALLS);
MAP.put(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH, "The ''{0}'' function of property delegate is expected to return ''{1}'', but returns ''{2}''",
TO_STRING, RENDER_TYPE, RENDER_TYPE);
MAP.put(COMPARE_TO_TYPE_MISMATCH, "''compareTo()'' must return jet.Int, but returns {0}", RENDER_TYPE);
MAP.put(CALLEE_NOT_A_FUNCTION, "Expecting a function type, but found {0}", RENDER_TYPE);
@@ -24,9 +24,11 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.diagnostics.rendering.Renderers;
import org.jetbrains.jet.lang.psi.Call;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
@@ -44,11 +46,10 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import java.util.List;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.psi.JetPsiFactory.createExpression;
import static org.jetbrains.jet.lang.psi.JetPsiFactory.createSimpleName;
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL;
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
public class DelegatedPropertyUtils {
@@ -91,7 +92,10 @@ public class DelegatedPropertyUtils {
}
if (returnType != null && !JetTypeChecker.INSTANCE.isSubtypeOf(returnType, propertyType)) {
//todo report error
Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
.on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
}
}
@@ -152,13 +156,43 @@ public class DelegatedPropertyUtils {
OverloadResolutionResults<FunctionDescriptor> functionResults = context.resolveCallWithGivenName(call, fakeCalleeExpression, functionName);
if (!functionResults.isSuccess()) {
//todo report errors
String expectedFunction = renderCall(call, trace.getBindingContext());
if (functionResults.isIncomplete()) {
context.trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
}
else if (functionResults.isSingleResult() ||
functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
context.trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
}
else if (functionResults.isAmbiguity()) {
context.trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
}
else {
context.trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
}
return;
}
context.trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, functionResults.getResultingCall());
}
private static String renderCall(@NotNull Call call, @NotNull BindingContext context) {
JetExpression calleeExpression = call.getCalleeExpression();
assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
StringBuilder builder = new StringBuilder(calleeExpression.getText());
builder.append("(");
List<JetType> argumentTypes = Lists.newArrayList();
for (ValueArgument argument : call.getValueArguments()) {
argumentTypes.add(context.get(EXPRESSION_TYPE, argument.getArgumentExpression()));
}
builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
builder.append(")");
return builder.toString();
}
private DelegatedPropertyUtils() {
}
}
@@ -0,0 +1,18 @@
trait A {
val prop: Int
}
class AImpl: A {
override val <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>prop<!> by Delegate()
}
fun foo() {
AImpl().prop
}
class Delegate {
fun get(t: Any?, p: String): String {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return ""
}
}
@@ -0,0 +1,9 @@
val a: Int by A(1)
class A<T: Any>(i: T) {
fun get(t: Any?, p: String): T {
t.equals(p) // to avoid UNUSED_PARAMETER warning
throw Exception()
}
}
@@ -0,0 +1,13 @@
open class Base
class Derived: Base()
val a: Base by A()
class A {
fun get(t: Any?, p: String): Derived {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return Derived()
}
}
@@ -0,0 +1,14 @@
class A
class D {
val c: Int by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>IncorrectThis<A>()<!>
}
val cTopLevel: Int by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>IncorrectThis<A>()<!>
class IncorrectThis<T> {
fun get<R>(t: Any?, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
}
@@ -0,0 +1,3 @@
val a: Int by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>A()<!>
class A
@@ -0,0 +1,8 @@
var a: Int by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>A()<!>
class A {
fun get(t: Any?, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
}
@@ -0,0 +1,18 @@
class D {
var c: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
}
var cTopLevel: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
class A
class Delegate {
fun get(t: Any?, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
fun set(t: A, p: String, i: Int) {
t.equals(p) // to avoid UNUSED_PARAMETER warning
i.equals(null)
}
}
@@ -0,0 +1,17 @@
open class Base
class Derived: Base()
var a: Derived by A()
class A {
fun get(t: Any?, p: String): Derived {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return Derived()
}
fun set(t: Any?, p: String, i: Base) {
t.equals(p) // to avoid UNUSED_PARAMETER warning
i.equals(null) // to avoid UNUSED_PARAMETER warning
}
}
@@ -0,0 +1,16 @@
class A {
var a: Int by Delegate()
}
var aTopLevel: Int by Delegate()
class Delegate {
fun get(t: Any?, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
fun set(t: Any?, p: String, a: Int) {
t.equals(p) // to avoid UNUSED_PARAMETER warning
a.equals(null)
}
}
@@ -0,0 +1,15 @@
class A {
var a: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
}
var aTopLevel: Int by Delegate()
class Delegate {
fun get(<!UNUSED_PARAMETER!>t<!>: Nothing?, p: String): Int {
p.equals(null) // to avoid UNUSED_PARAMETER warning
return 1
}
fun set(<!UNUSED_PARAMETER!>t<!>: Nothing?, p: String, a: Int) {
p.equals(a) // to avoid UNUSED_PARAMETER warning
}
}
@@ -0,0 +1,15 @@
class A {
var a: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
}
var aTopLevel: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
class Delegate {
fun get(<!UNUSED_PARAMETER!>t<!>: Nothing, p: String): Int {
p.equals(null) // to avoid UNUSED_PARAMETER warning
return 1
}
fun set(<!UNUSED_PARAMETER!>t<!>: Nothing, p: String, a: Int) {
p.equals(a) // to avoid UNUSED_PARAMETER warning
}
}
@@ -0,0 +1,15 @@
class A {
val c: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
}
class Delegate {
fun get(t: Int, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
fun get(t: String, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
}
@@ -0,0 +1,8 @@
val c: Int by <!DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH!>Delegate()<!>
class Delegate {
fun get(t: Any?, p: String): String {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return ""
}
}
@@ -0,0 +1,20 @@
class A
class B {
val b: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate<A>()<!>
}
val bTopLevel: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate<A>()<!>
class C {
val c: Int by Delegate<C>()
}
val cTopLevel: Int by Delegate<Nothing?>()
class Delegate<T> {
fun get(t: T, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
}
@@ -0,0 +1,16 @@
class A {
var a: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
}
var aTopLevel: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
class Delegate {
fun get(t: Any?, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
fun set(t: Any?, p: String, i: String) {
t.equals(p) // to avoid UNUSED_PARAMETER warning
i.equals(null)
}
}
@@ -0,0 +1,15 @@
class B {
val b: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
}
val bTopLevel: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
class A
class Delegate {
fun get(t: A, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
}
@@ -0,0 +1,12 @@
class A {
val a: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
}
val aTopLevel: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
class Delegate {
fun get(t: Any?, p: String, a: Int): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return a
}
}
@@ -0,0 +1,17 @@
class A {
var a: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
}
var aTopLevel: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>Delegate()<!>
class Delegate {
fun get(t: Any?, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
fun set(t: Any?, p: String, a: Int, c: Int) {
t.equals(p) // to avoid UNUSED_PARAMETER warning
c.equals(a)
}
}
@@ -0,0 +1,13 @@
var b: Int by Delegate()
class Delegate {
fun get(t: Any?, p: String): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return 1
}
fun set(t: Any?, p: String, i: Int): Int {
t.equals(p) // to avoid UNUSED_PARAMETER warning
return i
}
}
@@ -1918,16 +1918,46 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTrait.kt");
}
@TestMetadata("delegatedPropertyOverridedInTraitTypeMismatch.kt")
public void testDelegatedPropertyOverridedInTraitTypeMismatch() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.kt");
}
@TestMetadata("genericGetter.kt")
public void testGenericGetter() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/genericGetter.kt");
}
@TestMetadata("getterWithSubtype.kt")
public void testGetterWithSubtype() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/getterWithSubtype.kt");
}
@TestMetadata("inTrait.kt")
public void testInTrait() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/inTrait.kt");
}
@TestMetadata("incompleteTypeInference.kt")
public void testIncompleteTypeInference() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/incompleteTypeInference.kt");
}
@TestMetadata("localVariable.kt")
public void testLocalVariable() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/localVariable.kt");
}
@TestMetadata("missedGetter.kt")
public void testMissedGetter() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/missedGetter.kt");
}
@TestMetadata("missedSetter.kt")
public void testMissedSetter() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/missedSetter.kt");
}
@TestMetadata("propertyDefferedType.kt")
public void testPropertyDefferedType() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/propertyDefferedType.kt");
@@ -1953,11 +1983,76 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/delegatedProperty/redundantSetter.kt");
}
@TestMetadata("setterThisTypeMismatch.kt")
public void testSetterThisTypeMismatch() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/setterThisTypeMismatch.kt");
}
@TestMetadata("setterWithSupertype.kt")
public void testSetterWithSupertype() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/setterWithSupertype.kt");
}
@TestMetadata("thisInDelegate.kt")
public void testThisInDelegate() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/thisInDelegate.kt");
}
@TestMetadata("thisOfAnyType.kt")
public void testThisOfAnyType() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/thisOfAnyType.kt");
}
@TestMetadata("thisOfNothingNullableType.kt")
public void testThisOfNothingNullableType() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/thisOfNothingNullableType.kt");
}
@TestMetadata("thisOfNothingType.kt")
public void testThisOfNothingType() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/thisOfNothingType.kt");
}
@TestMetadata("twoGetMethods.kt")
public void testTwoGetMethods() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/twoGetMethods.kt");
}
@TestMetadata("typeMismatchForGetReturnType.kt")
public void testTypeMismatchForGetReturnType() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/typeMismatchForGetReturnType.kt");
}
@TestMetadata("typeMismatchForGetWithGeneric.kt")
public void testTypeMismatchForGetWithGeneric() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/typeMismatchForGetWithGeneric.kt");
}
@TestMetadata("typeMismatchForSetParameter.kt")
public void testTypeMismatchForSetParameter() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/typeMismatchForSetParameter.kt");
}
@TestMetadata("typeMismatchForThisGetParameter.kt")
public void testTypeMismatchForThisGetParameter() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/typeMismatchForThisGetParameter.kt");
}
@TestMetadata("wrongCountOfParametersInGet.kt")
public void testWrongCountOfParametersInGet() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/wrongCountOfParametersInGet.kt");
}
@TestMetadata("wrongCountOfParametersInSet.kt")
public void testWrongCountOfParametersInSet() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/wrongCountOfParametersInSet.kt");
}
@TestMetadata("wrongSetterReturnType.kt")
public void testWrongSetterReturnType() throws Exception {
doTest("compiler/testData/diagnostics/tests/delegatedProperty/wrongSetterReturnType.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/deparenthesize")
@@ -16,14 +16,12 @@
package org.jetbrains.jet.plugin.highlighter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.diagnostics.rendering.*;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.diagnostics.rendering.Renderers.NAME;
import static org.jetbrains.jet.lang.diagnostics.rendering.Renderers.RENDER_CLASS_OR_OBJECT;
import static org.jetbrains.jet.lang.diagnostics.rendering.Renderers.TO_STRING;
import static org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.TextElementType;
@@ -96,6 +94,9 @@ public class IdeErrorMessages {
new NoneApplicableCallsRenderer());
MAP.put(CANNOT_COMPLETE_RESOLVE, "<html>Cannot choose among the following candidates without completing type inference: <ul>{0}</ul></html>", HTML_AMBIGUOUS_CALLS);
MAP.put(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY, "<html>Overload resolution ambiguity on method ''{0}''. All these functions match. <ul>{1}</ul></html>", TO_STRING, HTML_AMBIGUOUS_CALLS);
MAP.put(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, "<html>Property delegate must have a ''{0}'' method. None of the following functions is suitable. <ul>{1}</ul></html>",
TO_STRING, new NoneApplicableCallsRenderer());
MAP.setImmutable();
}