function as argument checks for the complete signature.
diff --git a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/validation/TypeInfoValidator.java b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/validation/TypeInfoValidator.java
index 07aabba..80645cc 100644
--- a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/validation/TypeInfoValidator.java
+++ b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/validation/TypeInfoValidator.java
@@ -1476,8 +1476,16 @@
private TypeCompatibility testArgumentType(IRType paramType,
IValueReference argument) {
if (argument != null && paramType != null) {
- final IRType argumentType = JavaScriptValidations
+ final IRType argumentType;
+ if (argument.getAttribute(IReferenceAttributes.R_METHOD) != null) {
+ IRMethod method = (IRMethod) argument
+ .getAttribute(IReferenceAttributes.R_METHOD);
+ argumentType = RTypes.functionType(getContext(),
+ method.getParameters(), method.getType());
+ } else {
+ argumentType = JavaScriptValidations
.typeOf(argument);
+ }
if (argumentType != null) {
return paramType.isAssignableFrom(argumentType);
}
@@ -1552,6 +1560,11 @@
&& parameter.getType() instanceof IRRecordType) {
describeRecordTypeArgument(sb, argument,
(IRRecordType) parameter.getType());
+ } else if (argument.getAttribute(IReferenceAttributes.R_METHOD) != null) {
+ IRMethod method = (IRMethod) argument
+ .getAttribute(IReferenceAttributes.R_METHOD);
+ sb.append(RTypes.functionType(getContext(),
+ method.getParameters(), method.getType()).getName());
} else if (argument.getDeclaredType() != null) {
sb.append(argument.getDeclaredType().getName());
} else {
diff --git a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/javascript/typeinfo/RFunctionType.java b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/javascript/typeinfo/RFunctionType.java
index c78d80f..763e032 100644
--- a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/javascript/typeinfo/RFunctionType.java
+++ b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/javascript/typeinfo/RFunctionType.java
@@ -80,7 +80,54 @@
if (super.isAssignableFrom(type).ok()) {
return TypeCompatibility.TRUE;
} else if (type instanceof RFunctionType) {
- return TypeCompatibility.TRUE;
+ RFunctionType funcType = (RFunctionType) type;
+ // if return type is null or it is a IRRecordType instance then it
+ // is always just fine.
+ // RecordType is hard to check, can be type defs with fluent typing.
+ TypeCompatibility returnType = (getReturnType() == null || getReturnType() instanceof IRRecordType) ? TypeCompatibility.TRUE
+ : TypeCompatibility.FALSE;
+ if (returnType == TypeCompatibility.FALSE
+ && funcType.getReturnType() != null
+ && getReturnType() != null) {
+ returnType = getReturnType().isAssignableFrom(
+ funcType.getReturnType());
+ }
+ TypeCompatibility paramsType = TypeCompatibility.TRUE;
+ if (returnType == TypeCompatibility.TRUE) {
+ paramsType = ((funcType.getParameters() == null || funcType
+ .getParameters().size() == 0) && (getParameters() == null || getParameters()
+ .size() == 0)) ? TypeCompatibility.TRUE
+ : TypeCompatibility.FALSE;
+ if ((funcType.getParameters() != null && funcType
+ .getParameters().size() > 0)
+ && (getParameters() != null && getParameters().size() > 0)) {
+ paramsType = TypeCompatibility.TRUE;
+ for (int i = 0; i < getParameters().size(); i++) {
+ IRParameter parameter = getParameters().get(i);
+ if (parameter.getType() != null) {
+ if (i < funcType.getParameters().size()) {
+ IRParameter funcParam = funcType
+ .getParameters().get(i);
+ if (funcParam.getType() != null) {
+ if (funcParam.getType().isAssignableFrom(
+ parameter.getType()) == TypeCompatibility.FALSE) {
+ paramsType = TypeCompatibility.FALSE;
+ break;
+ }
+ } else {
+ paramsType = TypeCompatibility.FALSE;
+ break;
+ }
+ } else if (!parameter.isOptional()) {
+ paramsType = TypeCompatibility.FALSE;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return (returnType == TypeCompatibility.TRUE && paramsType == TypeCompatibility.TRUE) ? TypeCompatibility.TRUE
+ : TypeCompatibility.FALSE;
} else if (type instanceof IRSimpleType) {
// TODO (alex) convert when creating type
return TypeCompatibility
diff --git a/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/validation/TypeInfoValidationTests.java b/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/validation/TypeInfoValidationTests.java
index 167ba33..d52498e 100644
--- a/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/validation/TypeInfoValidationTests.java
+++ b/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/validation/TypeInfoValidationTests.java
@@ -3489,5 +3489,136 @@
final List<IProblem> problems = validate(code.toString());
assertEquals(problems.toString(), 1, problems.size());
}
+
+ public void testFunctionTypeExactParam() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number)} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number)} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+
+ public void testFunctionTypeObjectParam() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number)} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(Object,Object)} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+
+ public void testFunctionTypeObjectArgument() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(Object, Object)} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number)} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 1, problems.size());
+ }
+
+ public void testFunctionTypeExactReturnType() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number):Boolean} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number):Boolean} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+
+ public void testFunctionTypeObjectReturnType() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number):Object} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number):Boolean} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+
+ public void testFunctionTypeObjectCallerReturnType() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number):Boolean} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number):Object} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 1, problems.size());
+ }
+
+ public void testFunctionTypeObjectNoReturnType() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number)} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number):Object} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+
+ public void testFunctionTypeObjectNoCallerReturnType() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number):Boolean} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number)} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 1, problems.size());
+ }
+
+
+ public void testFunctionTypeOptionalParam() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number, String=):Boolean} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number):Boolean} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+ public void testFunctionTypeOptionalArgument() {
+ final StringList code = new StringList();
+ code.add("/**");
+ code.add(" * @param {function(String, Number):Boolean} f");
+ code.add(" */");
+ code.add("function test(f) {}");
+ code.add("/** @type {function(String,Number,String=):Boolean} */");
+ code.add("var f = function(a,b){};");
+ code.add("test(f);");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+
}