improved reporting TYPE_MISMATCH error for function literals

(introduced EXPECTED_PARAMETER_TYPE_MISMATCH, EXPECTED_RETURN_TYPE_MISMATCH, EXPECTED_PARAMETERS_NUMBER_MISMATCH
instead of reporting TYPE_MISMATCH on the whole function literal)
This commit is contained in:
Svetlana Isakova
2012-12-20 18:50:32 +04:00
parent 972b234db6
commit c37d7352a5
11 changed files with 172 additions and 41 deletions
@@ -32,6 +32,7 @@ import org.jetbrains.jet.lexer.JetTokens;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import static org.jetbrains.jet.lang.diagnostics.PositioningStrategies.*;
import static org.jetbrains.jet.lang.diagnostics.Severity.ERROR;
@@ -494,6 +495,10 @@ public interface Errors {
DiagnosticFactory3<JetExpression, String, JetType, JetType> RESULT_TYPE_MISMATCH = DiagnosticFactory3.create(ERROR);
SimpleDiagnosticFactory<JetWhenConditionInRange> TYPE_MISMATCH_IN_RANGE = new SimpleDiagnosticFactory<JetWhenConditionInRange>(ERROR, WHEN_CONDITION_IN_RANGE);
DiagnosticFactory1<JetParameter, JetType> EXPECTED_PARAMETER_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<JetTypeReference, JetType> EXPECTED_RETURN_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<JetFunctionLiteral, Integer, List<JetType>> EXPECTED_PARAMETERS_NUMBER_MISMATCH = DiagnosticFactory2.create(ERROR, FUNCTION_LITERAL_PARAMETERS);
DiagnosticFactory2<JetElement, JetType, JetType> INCOMPATIBLE_TYPES = DiagnosticFactory2.create(ERROR);
// Context tracking
@@ -354,6 +354,18 @@ public class PositioningStrategies {
}
};
public static final PositioningStrategy<JetFunctionLiteral> FUNCTION_LITERAL_PARAMETERS = new PositioningStrategy<JetFunctionLiteral>() {
@NotNull
@Override
public List<TextRange> mark(@NotNull JetFunctionLiteral functionLiteral) {
JetParameterList valueParameterList = functionLiteral.getValueParameterList();
if (valueParameterList != null) {
return markElement(valueParameterList);
}
return markNode(functionLiteral.getOpenBraceNode());
}
};
private PositioningStrategies() {
}
}
@@ -31,7 +31,6 @@ import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Iterator;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.diagnostics.rendering.Renderers.*;
@@ -254,6 +253,11 @@ public class DefaultErrorMessages {
MAP.put(EXPECTED_TYPE_MISMATCH, "Expected a value of type {0}", RENDER_TYPE);
MAP.put(ASSIGNMENT_TYPE_MISMATCH,
"Expected a value of type {0}. Assignment operation is not an expression, so it does not return any value", RENDER_TYPE);
MAP.put(EXPECTED_PARAMETER_TYPE_MISMATCH, "Expected parameter of type {0}", RENDER_TYPE);
MAP.put(EXPECTED_RETURN_TYPE_MISMATCH, "Expected return type {0}", RENDER_TYPE);
MAP.put(EXPECTED_PARAMETERS_NUMBER_MISMATCH, "Expected {0,choice,0#no parameters|1#one parameter of type|1<{0,number,integer} parameters of types} {1}", null, RENDER_COLLECTION_OF_TYPES);
MAP.put(IMPLICIT_CAST_TO_UNIT_OR_ANY, "Type was casted to ''{0}''. Please specify ''{0}'' as expected type, if you mean such cast",
RENDER_TYPE);
MAP.put(EXPRESSION_EXPECTED, "{0} is not an expression, and only expressions are allowed here", new Renderer<JetExpression>() {
@@ -332,22 +336,7 @@ public class DefaultErrorMessages {
MAP.put(CANNOT_CHECK_FOR_ERASED, "Cannot check for instance of erased type: {0}", RENDER_TYPE);
MAP.put(UNCHECKED_CAST, "Unchecked cast: {0} to {1}", RENDER_TYPE, RENDER_TYPE);
MAP.put(INCONSISTENT_TYPE_PARAMETER_VALUES, "Type parameter {0} of ''{1}'' has inconsistent values: {2}", NAME, NAME,
new Renderer<Collection<JetType>>() {
@NotNull
@Override
public String render(@NotNull Collection<JetType> types) {
StringBuilder builder = new StringBuilder();
for (Iterator<JetType> iterator = types.iterator(); iterator.hasNext(); ) {
JetType jetType = iterator.next();
builder.append(jetType);
if (iterator.hasNext()) {
builder.append(", ");
}
}
return builder.toString();
}
});
MAP.put(INCONSISTENT_TYPE_PARAMETER_VALUES, "Type parameter {0} of ''{1}'' has inconsistent values: {2}", NAME, NAME, RENDER_COLLECTION_OF_TYPES);
MAP.put(EQUALITY_NOT_APPLICABLE, "Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''", new Renderer<JetSimpleNameExpression>() {
@NotNull
@@ -310,6 +310,23 @@ public class Renderers {
}
};
public static final Renderer<Collection<JetType>> RENDER_COLLECTION_OF_TYPES = new Renderer<Collection<JetType>>() {
@NotNull
@Override
public String render(@NotNull Collection<JetType> types) {
StringBuilder builder = new StringBuilder();
for (Iterator<JetType> iterator = types.iterator(); iterator.hasNext(); ) {
JetType jetType = iterator.next();
builder.append(jetType);
if (iterator.hasNext()) {
builder.append(", ");
}
}
return builder.toString();
}
};
private Renderers() {
}
}
@@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.util.lazy.LazyValueWithDefault;
import org.jetbrains.jet.util.slicedmap.WritableSlice;
@@ -33,8 +34,9 @@ import org.jetbrains.jet.util.slicedmap.WritableSlice;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.lang.diagnostics.Errors.CANNOT_INFER_PARAMETER_TYPE;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
/**
* @author abreslav
@@ -94,7 +96,7 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
if (bodyExpression == null) return null;
JetType expectedType = context.expectedType;
boolean functionTypeExpected = expectedType != TypeUtils.NO_EXPECTED_TYPE && KotlinBuiltIns.getInstance().isFunctionType(expectedType);
boolean functionTypeExpected = expectedType != NO_EXPECTED_TYPE && KotlinBuiltIns.getInstance().isFunctionType(expectedType);
SimpleFunctionDescriptorImpl functionDescriptor = createFunctionDescriptor(expression, context, functionTypeExpected);
@@ -106,18 +108,24 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
ReceiverParameterDescriptor receiverParameter = functionDescriptor.getReceiverParameter();
JetType receiver = DescriptorUtils.getReceiverParameterType(receiverParameter);
JetType returnType = TypeUtils.NO_EXPECTED_TYPE;
JetType returnType = NO_EXPECTED_TYPE;
JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(context.scope, functionDescriptor, context.trace);
JetTypeReference returnTypeRef = functionLiteral.getReturnTypeRef();
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve function literal expression", expression);
JetType expectedReturnType = functionTypeExpected ? KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(expectedType) : null;
if (returnTypeRef != null) {
returnType = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, returnTypeRef, context.trace, true);
context.expressionTypingServices.checkFunctionReturnType(expression, context.replaceScope(functionInnerScope).
replaceExpectedType(returnType).replaceBindingTrace(temporaryTrace), temporaryTrace);
if (functionTypeExpected) {
if (!JetTypeChecker.INSTANCE.isSubtypeOf(expectedReturnType, returnType)) {
temporaryTrace.report(EXPECTED_RETURN_TYPE_MISMATCH.on(returnTypeRef, expectedReturnType));
}
}
}
else {
if (functionTypeExpected) {
returnType = KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(expectedType);
returnType = expectedReturnType;
}
returnType = context.expressionTypingServices.getBlockReturnedType(functionInnerScope, bodyExpression, CoercionStrategy.COERCION_TO_UNIT,
context.replaceExpectedType(returnType).replaceBindingTrace(temporaryTrace), temporaryTrace).getType();
@@ -130,17 +138,21 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
}
}, true);
JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("<return type>") : returnType;
functionDescriptor.setReturnType(safeReturnType);
if (!functionLiteral.hasDeclaredReturnType() && functionTypeExpected) {
JetType expectedReturnType = KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(expectedType);
if (KotlinBuiltIns.getInstance().isUnit(expectedReturnType)) {
functionDescriptor.setReturnType(KotlinBuiltIns.getInstance().getUnitType());
return DataFlowUtils.checkType(KotlinBuiltIns.getInstance().getFunctionType(Collections.<AnnotationDescriptor>emptyList(), receiver, parameterTypes, KotlinBuiltIns.getInstance().getUnitType()), expression, context, context.dataFlowInfo);
safeReturnType = KotlinBuiltIns.getInstance().getUnitType();
}
}
return DataFlowUtils.checkType(KotlinBuiltIns.getInstance().getFunctionType(Collections.<AnnotationDescriptor>emptyList(), receiver, parameterTypes, safeReturnType), expression, context, context.dataFlowInfo);
functionDescriptor.setReturnType(safeReturnType);
JetType resultType = KotlinBuiltIns.getInstance().getFunctionType(
Collections.<AnnotationDescriptor>emptyList(), receiver, parameterTypes, safeReturnType);
if (expectedType != NO_EXPECTED_TYPE && KotlinBuiltIns.getInstance().isFunctionType(expectedType)) {
// all checks were done before
return JetTypeInfo.create(resultType, context.dataFlowInfo);
}
return DataFlowUtils.checkType(resultType, expression, context, context.dataFlowInfo);
}
private SimpleFunctionDescriptorImpl createFunctionDescriptor(JetFunctionLiteralExpression expression, ExpressionTypingContext context, boolean functionTypeExpected) {
@@ -185,7 +197,8 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
? KotlinBuiltIns.getInstance().getValueParameters(functionDescriptor, context.expectedType)
: null;
boolean hasDeclaredValueParameters = functionLiteral.getValueParameterList() != null;
JetParameterList valueParameterList = functionLiteral.getValueParameterList();
boolean hasDeclaredValueParameters = valueParameterList != null;
if (functionTypeExpected && !hasDeclaredValueParameters && expectedValueParameters.size() == 1) {
ValueParameterDescriptor valueParameterDescriptor = expectedValueParameters.get(0);
ValueParameterDescriptor it = new ValueParameterDescriptorImpl(
@@ -195,17 +208,36 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
context.trace.record(AUTO_CREATED_IT, it);
}
else {
if (expectedValueParameters != null && declaredValueParameters.size() != expectedValueParameters.size()) {
List<JetType> expectedParameterTypes = Lists.newArrayList();
for (ValueParameterDescriptor parameter : expectedValueParameters) {
expectedParameterTypes.add(parameter.getType());
}
context.trace.report(EXPECTED_PARAMETERS_NUMBER_MISMATCH.on(functionLiteral, expectedParameterTypes.size(), expectedParameterTypes));
}
for (int i = 0; i < declaredValueParameters.size(); i++) {
JetParameter declaredParameter = declaredValueParameters.get(i);
JetTypeReference typeReference = declaredParameter.getTypeReference();
JetType expectedType;
if (expectedValueParameters != null && i < expectedValueParameters.size()) {
expectedType = expectedValueParameters.get(i).getType();
}
else {
expectedType = null;
}
JetType type;
if (typeReference != null) {
type = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReference, context.trace, true);
if (expectedType != null) {
if (!JetTypeChecker.INSTANCE.isSubtypeOf(type, expectedType)) {
context.trace.report(EXPECTED_PARAMETER_TYPE_MISMATCH.on(declaredParameter, expectedType));
}
}
}
else {
if (expectedValueParameters != null && i < expectedValueParameters.size()) {
type = expectedValueParameters.get(i).getType();
if (expectedType != null) {
type = expectedType;
}
else {
context.trace.report(CANNOT_INFER_PARAMETER_TYPE.on(declaredParameter));
@@ -2,17 +2,17 @@ fun text() {
"direct:a" to "mock:a"
"direct:a" on {it.body == "<hello/>"} to "mock:a"
"direct:a" on {it -> it.body == "<hello/>"} to "mock:a"
bar <!TYPE_MISMATCH!>{1}<!>
bar <!TYPE_MISMATCH!>{<!UNRESOLVED_REFERENCE!>it<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> 1}<!>
bar <!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>{<!>1}
bar <!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>{<!><!UNRESOLVED_REFERENCE!>it<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> 1}
bar {it, it1 -> it}
bar1 {1}
bar1 {it + 1}
bar2 <!TYPE_MISMATCH!>{<!TYPE_MISMATCH!><!>}<!>
bar2 {<!TYPE_MISMATCH!><!>}
bar2 {1}
bar2 {<!UNRESOLVED_REFERENCE!>it<!>}
bar2 <!TYPE_MISMATCH!>{<!CANNOT_INFER_PARAMETER_TYPE!>it<!> -> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>it<!>}<!>
bar2 {<!EXPECTED_PARAMETERS_NUMBER_MISMATCH, CANNOT_INFER_PARAMETER_TYPE!>it<!> -> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>it<!>}
}
fun bar(<!UNUSED_PARAMETER!>f<!> : (Int, Int) -> Int) {}
@@ -34,8 +34,8 @@ fun main(args : Array<String>) {
foo2()({})
foo2()<!TOO_MANY_ARGUMENTS, DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED!>{}<!>
(foo2()){}
(foo2())<!TYPE_MISMATCH!>{<!CANNOT_INFER_PARAMETER_TYPE!>x<!> -> }<!>
foo2()(<!TYPE_MISMATCH!>{<!CANNOT_INFER_PARAMETER_TYPE!>x<!> -> }<!>)
(foo2()){<!EXPECTED_PARAMETERS_NUMBER_MISMATCH, CANNOT_INFER_PARAMETER_TYPE!>x<!> -> }
foo2()({<!EXPECTED_PARAMETERS_NUMBER_MISMATCH, CANNOT_INFER_PARAMETER_TYPE!>x<!> -> })
val a = fooT1(1)()
a : Int
@@ -0,0 +1,19 @@
package a
trait Super
trait Trait : Super
class Sub : Trait
fun foo(f: (Trait) -> Trait) = f
fun test(s: Sub) {
foo {
(<!EXPECTED_PARAMETER_TYPE_MISMATCH!>t: Super<!>): <!EXPECTED_RETURN_TYPE_MISMATCH!>Sub<!> -> s
}
foo {
(t: Trait): Trait -> s
}
foo {
(t: Sub): Super -> s
}
}
@@ -0,0 +1,47 @@
package a
fun foo0(f: () -> String) = f
fun foo1(f: (Int) -> String) = f
fun foo2(f: (Int, String) -> String) = f
fun test1() {
foo0 {
""
}
foo0 {
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>(s: String)<!> -> ""
}
foo0 {
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!CANNOT_INFER_PARAMETER_TYPE!>x<!>, <!CANNOT_INFER_PARAMETER_TYPE!>y<!><!> -> ""
}
foo0 {
(): <!EXPECTED_RETURN_TYPE_MISMATCH!>Int<!> -> 42
}
foo1 {
""
}
foo1 {
(<!EXPECTED_PARAMETER_TYPE_MISMATCH!>s: String<!>) -> ""
}
foo1 {
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>x, <!CANNOT_INFER_PARAMETER_TYPE!>y<!><!> -> ""
}
foo1 {
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>()<!>: <!EXPECTED_RETURN_TYPE_MISMATCH!>Int<!> -> 42
}
foo2 <!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>{<!>
""
}
foo2 {
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>(<!EXPECTED_PARAMETER_TYPE_MISMATCH!>s: String<!>)<!> -> ""
}
foo2 {
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>x<!> -> ""
}
foo2 {
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>()<!>: <!EXPECTED_RETURN_TYPE_MISMATCH!>Int<!> -> 42
}
}
@@ -2,18 +2,18 @@
package kt352
val f : (Any) -> Unit = <!TYPE_MISMATCH!>{ () : Unit -> }<!> //type mismatch
val f : (Any) -> Unit = { <!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>()<!> : Unit -> } //type mismatch
fun foo() {
val <!UNUSED_VARIABLE!>f<!> : (Any) -> Unit = <!TYPE_MISMATCH!>{ () : Unit -> }<!> //!!! no error
val <!UNUSED_VARIABLE!>f<!> : (Any) -> Unit = { <!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>()<!> : Unit -> } //!!! no error
}
class A() {
val f : (Any) -> Unit = <!TYPE_MISMATCH!>{ () : Unit -> }<!> //type mismatch
val f : (Any) -> Unit = { <!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>()<!> : Unit -> } //type mismatch
}
//more tests
val g : () -> Unit = <!TYPE_MISMATCH!>{ (): Int -> 42 }<!>
val g : () -> Unit = { (): <!EXPECTED_RETURN_TYPE_MISMATCH!>Int<!> -> 42 }
val h : () -> Unit = { doSmth() }
@@ -24,4 +24,4 @@ val testIt : (Any) -> Unit = {
if (it is String) {
doSmth(it)
}
}
}
@@ -1791,6 +1791,16 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/functionLiterals"), "kt", true);
}
@TestMetadata("ExpectedParameterTypeMismatchVariance.kt")
public void testExpectedParameterTypeMismatchVariance() throws Exception {
doTest("compiler/testData/diagnostics/tests/functionLiterals/ExpectedParameterTypeMismatchVariance.kt");
}
@TestMetadata("ExpectedParametersTypesMismatch.kt")
public void testExpectedParametersTypesMismatch() throws Exception {
doTest("compiler/testData/diagnostics/tests/functionLiterals/ExpectedParametersTypesMismatch.kt");
}
@TestMetadata("kt2906.kt")
public void testKt2906() throws Exception {
doTest("compiler/testData/diagnostics/tests/functionLiterals/kt2906.kt");