JSType calleeType = calleePair.type;
if (!calleeType.isSubtypeOf(JSType.topFunction())) {
warnings.add(JSError.make(
expr, TypeCheck.NOT_CALLABLE, calleeType.toString()));
}
FunctionType funType = calleeType.getFunType();
if (funType == null
|| funType.isTopFunction() || funType.isQmarkFunction()) {
return analyzeCallNodeArgumentsFwd(expr, inEnv);
} else if (funType.isLoose()) {
return analyzeLooseCallNodeFwd(expr, inEnv, requiredType);
} else if (expr.isCall() && funType.isConstructor()) {
// TODO(dimvar): handle constructors with @return; they can be called
// without new, eg, Number in es3.js
warnings.add(JSError.make(
expr, TypeCheck.CONSTRUCTOR_NOT_CALLABLE, funType.toString()));
return analyzeCallNodeArgumentsFwd(expr, inEnv);
} else if (expr.isNew() && !funType.isConstructor()) {
warnings.add(JSError.make(
expr, NOT_A_CONSTRUCTOR, funType.toString()));
return analyzeCallNodeArgumentsFwd(expr, inEnv);
}
int maxArity = funType.getMaxArity();
int minArity = funType.getMinArity();
int numArgs = expr.getChildCount() - 1;
if (numArgs < minArity || numArgs > maxArity) {
warnings.add(JSError.make(
expr, TypeCheck.WRONG_ARGUMENT_COUNT, "",
Integer.toString(numArgs), Integer.toString(minArity),
" and at most " + maxArity));
return analyzeCallNodeArgumentsFwd(expr, inEnv);
}
FunctionType origFunType = funType; // save for later
if (funType.isGeneric()) {
Map<String, JSType> typeMap =
calcTypeInstantiationFwd(expr, funType, inEnv);
funType = funType.instantiateGenerics(typeMap);
println("Instantiated function type: " + funType);
}
// argTypes collects types of actuals for deferred checks.
List<JSType> argTypes = new ArrayList<>();
TypeEnv tmpEnv = inEnv;
Node arg = expr.getChildAtIndex(1);
for (int i = 0; i < numArgs; i++) {
JSType formalType = funType.getFormalType(i);
if (formalType.isBottom()) {
warnings.add(JSError.make(expr, CALL_FUNCTION_WITH_BOTTOM_FORMAL,
Integer.toString(i)));
formalType = JSType.UNKNOWN;
}
EnvTypePair pair = analyzeExprFwd(arg, tmpEnv, formalType);
JSType argTypeForDeferredCheck = pair.type;
// Allow passing undefined for an optional argument.
if (i >= minArity && pair.type.equals(JSType.UNDEFINED)) {
argTypeForDeferredCheck = null; // No deferred check needed.
} else if (!pair.type.isSubtypeOf(formalType)) {
warnings.add(JSError.make(arg, INVALID_ARGUMENT_TYPE,
Integer.toString(i + 1), "",
formalType.toString(), pair.type.toString()));
argTypeForDeferredCheck = null; // No deferred check needed.
}
argTypes.add(argTypeForDeferredCheck);
tmpEnv = pair.env;
arg = arg.getNext();
}
JSType retType = funType.getReturnType();
if (callee.isName()) {
String calleeName = callee.getQualifiedName();
if (currentScope.isKnownFunction(calleeName)
&& !currentScope.isExternalFunction(calleeName)) {
// Local function definitions will be type-checked more
// exactly using their summaries, and don't need deferred checks
if (currentScope.isLocalFunDef(calleeName)) {
collectTypesForFreeVarsFwd(callee, tmpEnv);
} else if (!origFunType.isGeneric()) {
JSType expectedRetType = requiredType;
println("Updating deferred check with ret: ", expectedRetType,
" and args: ", argTypes);
DeferredCheck dc;
if (expr.isCall()) {